Compare commits

...

6 Commits

Author SHA1 Message Date
NoxMax
25800f54e8
Fix/Feat: PVP with master and PVP probablity system (thread-safe remake) (#2008)
This is a remake of #1914 that had to be reverted. Original PR had a
thread-safe issue where a crash happens if multiple threads access the
cache at the same time. Unfortunately this problem was not caught in
earlier testing. I don't know if because I was testing on a month old
branch, if my settings had only ~2000, or if I needed test runs longer
than an hour to find out.

Regardless, this has all been addressed. Test have been run on the
latest commits from today (2026/1/11), with all 7500 of my bots active,
with a test run that lasted 15 hours. All stable and bots are following
the probability system without issue.

~~The new edit uses mutex locking, preventing simultaneous access of the
cache by multiple threads.~~
The new edit uses deterministic hashing, thereby not having issues with
cache thread safety to begin with. Thank you @hermensbas for catching
and reverting the original problem PR. Apologies for not catching the
issue myself.

---
Original PR description:

There are two related PVP components in this PR. First is the simple yet
fundamental change to bot behaviour when they are in party. Right now
bots with a master will go into PVP when there's a nearby PVP target,
even if master is not in PVP. This absolutely should not happen. Bots
should not consider PVP at all if master is not in PVP. The fix is only
3 lines in EnemyPlayerValue

The second component is introducing PVP probabilities, to make decisions
more realistic. Right now even a level 1 bot will 100% go into PVP if it
sees a level 80 PVP target. They can't help themselves. So the change
here addresses that insanity. Several thresholds (subject to community
review) are introduced:

1. Bots will not fight a target 5 or more levels higher than them
2. Bots have a 25% chance starting a fight with a target +/- 4 levels
from them.
3. Bots have a 50% chance starting a fight with a target +/- 3 levels
from them.
4. Bots have a 75% chance starting a fight with a target +/- 2 levels
from them.
5. Bots have a 100% chance starting a fight with a target +/- 1 level
from them.
6. Bots have a 25% chance starting a fight with a target 5 or more
levels below them (ganking. thought it would be funny, and technically
realistic of player behaviour)

Exception of course exist for BG/Arena/Duel, and in capitals where bots
will always PVP. Also bots will always defend themselves if attacked.

Few notes: 
1. The if/ else if logic can be further simplified, but only if we use
thresholds that are different by one. So current logic allows for
flexibility of using values like 10/7/5/3 instead of 5/4/3/2.
2. The caching system is per-bot basis. So for some target X, if some
bot decides to attack it, another bot will make its own decision. At
first I used a simplified global system (thinking there might be
performance concerns) where if one bot decides to attack a target then
they all do, but when I switched to the more realistic per-bot basis, I
didn't see an effect on performance.
3. Variables are obviously not configurable right now. I'm starting to
see Bash's POV that maybe we have too many configs 😬 Still,
they can be easily exposed in the future, and if someone is reading this
then, remember to change constexpr to const.

---------

Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com>
2026-02-13 09:31:55 -08:00
Crow
9748e36ad6
Fix potential failure of Magtheridon cube clickers to engage in combat (#2129)
# Pull Request

I noticed a problem that has always existed with the Magtheridon
strategy but just never came up for me due to chance. Cube clicker logic
is based on a timer that resets after every Blast Nova. If the timer is
not reset, the cubes will still be clicked, but the clickers will do
nothing but wait to click on the cubes instead of resuming combat
between Blast Novas. Because tracking of the Blast Nova state happens
during the cube clicking sequence, if a cube clicker is assigned the
singular role to track Blast Nova state (which is done simply by
returning the first DPS bot found), then the timer will not be reset.

This whole strategy needs a refactor, but the simple fix for this
problem for now is just to remove the role check for tracking the Blast
Nova state. I tested the fix, and it works.

---

## Design Philosophy

We prioritize **stability, performance, and predictability** over
behavioral realism.
Complex player-mimicking logic is intentionally limited due to its
negative impact on scalability, maintainability, and
long-term robustness.

Excessive processing overhead can lead to server hiccups, increased CPU
usage, and degraded performance for all
participants. Because every action and
decision tree is executed **per bot and per trigger**, even small
increases in logic complexity can scale poorly and
negatively affect both players and
world (random) bots. Bots are not expected to behave perfectly, and
perfect simulation of human decision-making is not a
project goal. Increased behavioral
realism often introduces disproportionate cost, reduced predictability,
and significantly higher maintenance overhead.

Every additional branch of logic increases long-term responsibility. All
decision paths must be tested, validated, and
maintained continuously as the system evolves.
If advanced or AI-intensive behavior is introduced, the **default
configuration must remain the lightweight decision
model**. More complex behavior should only be
available as an **explicit opt-in option**, clearly documented as having
a measurable performance cost.

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:

- Describe the **minimum logic** required to achieve the intended
behavior?
- Describe the **cheapest implementation** that produces an acceptable
result?
- Describe the **runtime cost** when this logic executes across many
bots?

---

## How to Test the Changes

- Step-by-step instructions to test the change
- Any required setup (e.g. multiple players, bots, specific
configuration)
- Expected behavior and how to verify it

## Complexity & Impact

Does this change add new decision branches?
- - [X] No
- - [ ] 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:
- - [ ] Lightweight mode remains the default
- - [ ] 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?
- - [X] No
- - [ ] Yes (**explain below**)

If yes, please specify:

- AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.)
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation)
- Which parts of the change were influenced or generated
- Whether the result was manually reviewed and adapted

AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
Any AI-influenced changes must be verified against existing CORE and PB
logic. We expect contributors to be honest
about what they do and do not understand.

---

## Final Checklist

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

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.
2026-02-13 09:25:12 -08:00
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
privatecore
a0a50204ec
Fix action validation checks: isUseful -> isPossible + codestyle fixes and corrections (#2125)
# Pull Request

Fix the incorrect logic flaw when processing actions from different
sources. It should be: `isUseful` -> `isPossible`. The original logic is
based on the Mangosbot code and the impl presented inside
`Engine::DoNextAction`. This should fix all wrong validation orders for
triggers and direct/specific actions.

Code style is based on the AzerothCore style guide + clang-format.

---

## Design Philosophy

We prioritize **stability, performance, and predictability** over
behavioral realism.
Complex player-mimicking logic is intentionally limited due to its
negative impact on scalability, maintainability, and
long-term robustness.

Excessive processing overhead can lead to server hiccups, increased CPU
usage, and degraded performance for all
participants. Because every action and
decision tree is executed **per bot and per trigger**, even small
increases in logic complexity can scale poorly and
negatively affect both players and
world (random) bots. Bots are not expected to behave perfectly, and
perfect simulation of human decision-making is not a
project goal. Increased behavioral
realism often introduces disproportionate cost, reduced predictability,
and significantly higher maintenance overhead.

Every additional branch of logic increases long-term responsibility. All
decision paths must be tested, validated, and
maintained continuously as the system evolves.
If advanced or AI-intensive behavior is introduced, the **default
configuration must remain the lightweight decision
model**. More complex behavior should only be
available as an **explicit opt-in option**, clearly documented as having
a measurable performance cost.

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:

- Describe the **minimum logic** required to achieve the intended
behavior?
- Describe the **cheapest implementation** that produces an acceptable
result?
- Describe the **runtime cost** when this logic executes across many
bots?

---

## How to Test the Changes

- Step-by-step instructions to test the change
- Any required setup (e.g. multiple players, bots, specific
configuration)
- Expected behavior and how to verify it

## Complexity & Impact

Does this change add new decision branches?
- - [x] No
- - [ ] 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:
- - [ ] Lightweight mode remains the default
- - [ ] 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?
- - [x] No
- - [ ] Yes (**explain below**)

If yes, please specify:

- AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.)
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation)
- Which parts of the change were influenced or generated
- Whether the result was manually reviewed and adapted

AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
Any AI-influenced changes must be verified against existing CORE and PB
logic. We expect contributors to be honest
about what they do and do not understand.

---

## 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

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.
2026-02-13 09:24:11 -08:00
Keleborn
80b3823f12
Warnings PR 3, remove std::move when not necessary. (#2108)
# Pull Request

std::move was being used in a few places to return a vector. Its not
necessary. A direct return allows for some optimizations that moving
wouldnt.

## How to Test the Changes

-Bots should initialize correctly 

## Complexity & Impact

- Does this change add new decision branches?
    - [x] No
    - [ ] 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
- [ ] 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?
    - [x] No
    - [ ] Yes (**explain below**)

---

## Final Checklist

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

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.

---------

Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com>
2026-02-13 09:22:27 -08:00
Keleborn
ee2a399ac8
Refactor newrpginfo data union to std::variant (#2079)
# Pull Request

As I began modifying the newrpginfo to change the types of data it
stored, or add new data I found myself with the issue of ending up
either with garbage memory if the information wasnt properly stored on
status change, or needing complicated destructor patterns for non
trivial data sets.

---

## Design Philosophy

Make rpginfo able to handle more complicated information in a strongly 

---

## Feature Evaluation

No Feature changes

---

## How to Test the Changes

-  Server should be stable for an extended period of time. 
- Bots should be able to complete quests, fly, etc as they did before.

## Complexity & Impact

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

- Does this change increase per-bot or per-tick processing?
    - [ ] No
    - [ X] Yes (**describe and justify impact**)
Potentially as there can be more memory involved in the object.

- 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:

- [ ] Lightweight mode remains the default
- [ ] 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:

- Gemini suggested the use of std::variant as an alternative data
structure. I found additinal external references that correlated with
the same suggestion of moving away from a union.
- Implementation was performed manually with Co-pilot auto-complete

---

## Final Checklist 
In progress.
- [ ] Stability is not compromised
- [ ] Performance impact is understood, tested, and acceptable
- [ ] Added logic complexity is justified and explained
- [ ] Documentation updated if needed

---

## Notes for Reviewers

Im not 100% sure if this is a good design choice. There are some things
I didnt quite like by the end of this, specifically having to double
check whenever accessing data whether exists or not even though an
action has already been triggered. But I have a PR in the works where I
want to store a full flight path vector, and the union was giving me
issues. (It appears that state changes may be occuring in the same tick
between RPG status update and the stated action, leading to incorrect
data gathering.

I ended up solving it by first checking a pointer to the object, and
then getting the reference.
```c++
    auto* dataPtr = std::get_if<NewRpgInfo::DoQuest>(&info.data);
    if (!dataPtr)
        return false;
    auto& data = *dataPtr;
```

---------

Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com>
2026-02-13 09:19:54 -08:00
53 changed files with 884 additions and 690 deletions

View File

@ -184,7 +184,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param)
// // }
// // }
// return std::move(ret);
// return ret;
// }
// std::vector<TalentPath*> ChangeTalentsAction::getPremadePaths(TalentSpec* oldSpec)
@ -201,7 +201,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param)
// // }
// // }
// return std::move(ret);
// return ret;
// }
// TalentPath* ChangeTalentsAction::getPremadePath(uint32 id)

View File

@ -55,63 +55,6 @@ MountData CollectMountData(const Player* bot)
return data;
}
bool CheckMountStateAction::isUseful()
{
// Not useful when:
if (botAI->IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
!bot->IsOutdoors() || bot->InArena())
return false;
master = GetMaster();
// Get shapeshift states, only applicable when there's a master
if (master)
{
botInShapeshiftForm = bot->GetShapeshiftForm();
masterInShapeshiftForm = master->GetShapeshiftForm();
}
// Not useful when in combat and not currently mounted / travel formed
if ((bot->IsInCombat() || botAI->GetState() == BOT_STATE_COMBAT) &&
!bot->IsMounted() && botInShapeshiftForm != FORM_TRAVEL && botInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT_EPIC)
return false;
// In addition to checking IsOutdoors, also check whether bot is clipping below floor slightly because that will
// cause bot to falsly indicate they are outdoors. This fixes bug where bot tries to mount indoors (which seems
// to mostly be an issue in tunnels of WSG and AV)
float posZ = bot->GetPositionZ();
float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ);
if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel)
return false;
// Not useful when bot does not have mount strat and is not currently mounted
if (!GET_PLAYERBOT_AI(bot)->HasStrategy("mount", BOT_STATE_NON_COMBAT) && !bot->IsMounted())
return false;
// Not useful when level lower than minimum required
if (bot->GetLevel() < sPlayerbotAIConfig.useGroundMountAtMinLevel)
return false;
// Allow mounting while transformed only if the form allows it
if (bot->HasAuraType(SPELL_AURA_TRANSFORM) && bot->IsInDisallowedMountForm())
return false;
// BG Logic
if (bot->InBattleground())
{
// Do not use when carrying BG Flags
if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))
return false;
// Only mount if BG starts in less than 30 sec
if (Battleground* bg = bot->GetBattleground())
if (bg->GetStatus() == STATUS_WAIT_JOIN && bg->GetStartDelayTime() > BG_START_DELAY_30S)
return false;
}
return true;
}
bool CheckMountStateAction::Execute(Event /*event*/)
{
// Determine if there are no attackers
@ -182,6 +125,63 @@ bool CheckMountStateAction::Execute(Event /*event*/)
return false;
}
bool CheckMountStateAction::isUseful()
{
// Not useful when:
if (botAI->IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
!bot->IsOutdoors() || bot->InArena())
return false;
master = GetMaster();
// Get shapeshift states, only applicable when there's a master
if (master)
{
botInShapeshiftForm = bot->GetShapeshiftForm();
masterInShapeshiftForm = master->GetShapeshiftForm();
}
// Not useful when in combat and not currently mounted / travel formed
if ((bot->IsInCombat() || botAI->GetState() == BOT_STATE_COMBAT) &&
!bot->IsMounted() && botInShapeshiftForm != FORM_TRAVEL && botInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT_EPIC)
return false;
// In addition to checking IsOutdoors, also check whether bot is clipping below floor slightly because that will
// cause bot to falsly indicate they are outdoors. This fixes bug where bot tries to mount indoors (which seems
// to mostly be an issue in tunnels of WSG and AV)
float posZ = bot->GetPositionZ();
float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ);
if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel)
return false;
// Not useful when bot does not have mount strat and is not currently mounted
if (!GET_PLAYERBOT_AI(bot)->HasStrategy("mount", BOT_STATE_NON_COMBAT) && !bot->IsMounted())
return false;
// Not useful when level lower than minimum required
if (bot->GetLevel() < sPlayerbotAIConfig.useGroundMountAtMinLevel)
return false;
// Allow mounting while transformed only if the form allows it
if (bot->HasAuraType(SPELL_AURA_TRANSFORM) && bot->IsInDisallowedMountForm())
return false;
// BG Logic
if (bot->InBattleground())
{
// Do not use when carrying BG Flags
if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))
return false;
// Only mount if BG starts in less than 30 sec
if (Battleground* bg = bot->GetBattleground())
if (bg->GetStatus() == STATUS_WAIT_JOIN && bg->GetStartDelayTime() > BG_START_DELAY_30S)
return false;
}
return true;
}
bool CheckMountStateAction::Mount()
{
// Remove current Shapeshift if need be

View File

@ -320,7 +320,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
inDungeon = true;
if (realMaster && realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon() &&
(realMaster->GetMapId() != pos.getMapId()))
(realMaster->GetMapId() != pos.GetMapId()))
return false;
}
@ -334,7 +334,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
return false;
Formation* formation = AI_VALUE(Formation*, "formation");
float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY());
float distance = groupLeader->GetDistance2d(pos.GetPositionX(), pos.GetPositionY());
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
{

View File

@ -30,37 +30,6 @@ bool AttackEnemyFlagCarrierAction::isUseful()
PlayerHasFlag::IsCapturingFlag(bot);
}
bool AttackAnythingAction::isUseful()
{
if (!bot || !botAI) // Prevents invalid accesses
return false;
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot cannot be active
return false;
if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT))
return false;
if (bot->IsInCombat())
return false;
Unit* target = GetTarget();
if (!target || !target->IsInWorld()) // Checks if the target is valid and in the world
return false;
std::string const name = std::string(target->GetName());
if (!name.empty() &&
(name.find("Dummy") != std::string::npos ||
name.find("Charge Target") != std::string::npos ||
name.find("Melee Target") != std::string::npos ||
name.find("Ranged Target") != std::string::npos))
{
return false;
}
return true;
}
bool DropTargetAction::Execute(Event event)
{
Unit* target = context->GetValue<Unit*>("current target")->Get();
@ -127,7 +96,38 @@ bool AttackAnythingAction::Execute(Event event)
return result;
}
bool AttackAnythingAction::isPossible() { return AttackAction::isPossible() && GetTarget(); }
bool AttackAnythingAction::isUseful()
{
if (!bot || !botAI) // Prevents invalid accesses
return false;
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot cannot be active
return false;
if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT))
return false;
if (bot->IsInCombat())
return false;
Unit* target = GetTarget();
if (!target || !target->IsInWorld()) // Checks if the target is valid and in the world
return false;
std::string const name = std::string(target->GetName());
if (!name.empty() &&
(name.find("Dummy") != std::string::npos ||
name.find("Charge Target") != std::string::npos ||
name.find("Melee Target") != std::string::npos ||
name.find("Ranged Target") != std::string::npos))
{
return false;
}
return true;
}
bool AttackAnythingAction::isPossible() { return GetTarget() && AttackAction::isPossible(); }
bool DpsAssistAction::isUseful()
{

View File

@ -28,8 +28,8 @@ bool DebugAction::Execute(Event event)
uint32 areaId = 0;
uint32 zoneId = 0;
sMapMgr->GetZoneAndAreaId(PHASEMASK_NORMAL, zoneId, areaId, pos.getMapId(), pos.getX(), pos.getY(),
pos.getZ());
sMapMgr->GetZoneAndAreaId(PHASEMASK_NORMAL, zoneId, areaId, pos.GetMapId(), pos.GetPositionX(), pos.GetPositionY(),
pos.GetPositionZ());
std::ostringstream out;
out << zoneId << "," << areaId << "," << (pos.getAreaName().empty() ? "none" : pos.getAreaName()) << ",";
@ -298,7 +298,7 @@ bool DebugAction::Execute(Event event)
for (auto p : ppath)
{
Creature* wpCreature =
bot->SummonCreature(1, p.getX(), p.getY(), p.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000.0f);
bot->SummonCreature(1, p.GetPositionX(), p.GetPositionY(), p.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000.0f);
// addAura(246, wpCreature);
units.push_back(wpCreature->GetGUID());
@ -325,11 +325,11 @@ bool DebugAction::Execute(Event event)
WorldPosition botPos(bot);
WorldPosition botPos1 = botPos;
botPos.setX(botPos.getX() + cos(ang) * dist);
botPos.setY(botPos.getY() + sin(ang) * dist);
botPos.setX(botPos.GetPositionX() + cos(ang) * dist);
botPos.setY(botPos.GetPositionY() + sin(ang) * dist);
botPos.setZ(botPos.getHeight() + 2);
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
FakeSpell(spellEffect, wpCreature, wpCreature, prev->GetGUID(), {}, {}, botPos, botPos);
@ -352,11 +352,11 @@ bool DebugAction::Execute(Event event)
WorldPosition botPos(bot);
WorldPosition botPos1 = botPos;
botPos.setX(botPos.getX() + cos(ang) * dist);
botPos.setY(botPos.getY() + sin(ang) * dist);
botPos.setX(botPos.GetPositionX() + cos(ang) * dist);
botPos.setY(botPos.GetPositionY() + sin(ang) * dist);
botPos.setZ(botPos.getHeight() + 2);
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature)
@ -383,11 +383,11 @@ bool DebugAction::Execute(Event event)
WorldPosition botPos(bot);
WorldPosition botPos1 = botPos;
botPos.setX(botPos.getX() + cos(ang) * dist);
botPos.setY(botPos.getY() + sin(ang) * dist);
botPos.setX(botPos.GetPositionX() + cos(ang) * dist);
botPos.setY(botPos.GetPositionY() + sin(ang) * dist);
botPos.setZ(botPos.getHeight() + 2);
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 5000.0f + i * 100.0f);
wpCreature->SetObjectScale(0.5f);
@ -411,11 +411,11 @@ bool DebugAction::Execute(Event event)
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + cos(ang) * dist);
botPos.setY(botPos.getY() + sin(ang) * dist);
botPos.setX(botPos.GetPositionX() + cos(ang) * dist);
botPos.setY(botPos.GetPositionY() + sin(ang) * dist);
botPos.setZ(botPos.getHeight() + 2);
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
units.push_back(wpCreature->GetGUID());
@ -480,13 +480,13 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature =
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(),
botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(),
botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature)
{
@ -512,11 +512,11 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature = bot->SummonCreature(effect, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(effect, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
}
}
@ -532,8 +532,8 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
FakeSpell(effect, bot, nullptr, ObjectGuid::Empty, {}, {}, botPos, botPos, true);
@ -552,11 +552,11 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature)
@ -568,7 +568,7 @@ bool DebugAction::Execute(Event event)
// wpCreature->SendMessageToSet(&data, true);
datMap.push_back(data);
// wpCreature->MonsterMoveWithSpeed(botPos.getX(), botPos.getY() + 80, botPos.getZ(), 8.0f, true,
// wpCreature->MonsterMoveWithSpeed(botPos.GetPositionX(), botPos.GetPositionY() + 80, botPos.GetPositionZ(), 8.0f, true,
// true);
}
}
@ -600,13 +600,13 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature =
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(),
botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(),
botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature)
{
@ -646,12 +646,12 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
wpCreature = bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(),
botPos.getY(), botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
wpCreature = bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(),
botPos.GetPositionY(), botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature && lCreature)
{
@ -675,11 +675,11 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + spellEffect * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
if (wpCreature)
@ -708,11 +708,11 @@ bool DebugAction::Execute(Event event)
{
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
all_targets.push_back(wpCreature->GetGUID());
@ -788,11 +788,11 @@ bool DebugAction::Execute(Event event)
{
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature = bot->SummonCreature(2334, botPos.getX(), botPos.getY(), botPos.getZ(), 0,
Creature* wpCreature = bot->SummonCreature(2334, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
all_targets.push_back(wpCreature->GetGUID());
@ -868,13 +868,13 @@ bool DebugAction::Execute(Event event)
uint32 effect = dx + dy * 10 + soundEffects * 100;
WorldPosition botPos(bot);
botPos.setX(botPos.getX() + (dx - 5) * 5);
botPos.setY(botPos.getY() + (dy - 5) * 5);
botPos.setX(botPos.GetPositionX() + (dx - 5) * 5);
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight());
Creature* wpCreature =
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.getX(), botPos.getY(),
botPos.getZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
bot->SummonCreature((dy == 0 && (dx == 0 || dx == 2)) ? 6 : 2, botPos.GetPositionX(), botPos.GetPositionY(),
botPos.GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
wpCreature->PlayDistanceSound(effect);
}
@ -964,7 +964,7 @@ void DebugAction::FakeSpell(uint32 spellId, Unit* truecaster, Unit* caster, Obje
m_targets.SetDst(dest);
if ((spellInfo && spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) || forceDest)
m_targets.SetSrc(source.getX(), source.getY(), source.getZ());
m_targets.SetSrc(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ());
if (!forceDest && target)
if (!spellInfo ||

View File

@ -7,22 +7,24 @@
#define _PLAYERBOT_FISHINGACTION_H
#include "Action.h"
#include "MovementActions.h"
#include "Event.h"
#include "MovementActions.h"
#include "Playerbots.h"
extern const uint32 FISHING_SPELL;
extern const uint32 FISHING_POLE;
extern const uint32 FISHING_BOBBER;
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance,
float maxDistance, float increment, bool checkLOS = false, int numDirections = 16);
class PlayerbotAI;
class FishingAction : public Action
{
public:
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing") {}
bool Execute(Event event) override;
bool isUseful() override;
};
@ -31,8 +33,10 @@ class EquipFishingPoleAction : public Action
{
public:
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
bool Execute(Event event) override;
bool isUseful() override;
private:
Item* _pole = nullptr;
};
@ -40,7 +44,8 @@ private:
class MoveNearWaterAction : public MovementAction
{
public:
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
MoveNearWaterAction(PlayerbotAI* botAI) : MovementAction(botAI, "move near water") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
@ -50,6 +55,7 @@ class UseBobberAction : public Action
{
public:
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
bool Execute(Event event) override;
bool isUseful() override;
};
@ -58,6 +64,7 @@ class EndMasterFishingAction : public Action
{
public:
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
bool Execute(Event event) override;
bool isUseful() override;
};
@ -66,6 +73,8 @@ class RemoveBobberStrategyAction : public Action
{
public:
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
bool Execute(Event event) override;
};
#endif

View File

@ -78,6 +78,35 @@ bool CastSpellAction::Execute(Event event)
return botAI->CastSpell(spell, GetTarget());
}
bool CastSpellAction::isUseful()
{
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
return false;
if (spell == "mount" && !bot->IsMounted() && !bot->IsInCombat())
return true;
if (spell == "mount" && bot->IsInCombat())
{
bot->Dismount();
return false;
}
Unit* spellTarget = GetTarget();
if (!spellTarget)
return false;
if (!spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId())
return false;
// float combatReach = bot->GetCombatReach() + target->GetCombatReach();
// if (!botAI->IsRanged(bot))
// combatReach += 4.0f / 3.0f;
return AI_VALUE2(bool, "spell cast useful", spell);
// && ServerFacade::instance().GetDistance2d(bot, target) <= (range + combatReach);
}
bool CastSpellAction::isPossible()
{
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
@ -106,36 +135,6 @@ bool CastSpellAction::isPossible()
return botAI->CanCastSpell(spell, GetTarget());
}
bool CastSpellAction::isUseful()
{
if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true))
return false;
if (spell == "mount" && !bot->IsMounted() && !bot->IsInCombat())
return true;
if (spell == "mount" && bot->IsInCombat())
{
bot->Dismount();
return false;
}
Unit* spellTarget = GetTarget();
if (!spellTarget)
return false;
if (!spellTarget->IsInWorld() || spellTarget->GetMapId() != bot->GetMapId())
return false;
// float combatReach = bot->GetCombatReach() + spellTarget->GetCombatReach();
// if (!botAI->IsRanged(bot))
// combatReach += 4.0f / 3.0f;
return spellTarget &&
AI_VALUE2(bool, "spell cast useful",
spell); // && ServerFacade::instance().GetDistance2d(bot, spellTarget) <= (range + combatReach);
}
CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
{
range = ATTACK_DISTANCE;

View File

@ -23,8 +23,8 @@ public:
std::string const GetTargetName() override { return "current target"; };
bool Execute(Event event) override;
bool isPossible() override;
bool isUseful() override;
bool isPossible() override;
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
std::vector<NextAction> getPrerequisites() override

View File

@ -370,7 +370,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// {
// movePosition = endPosition;
// if (startPosition.getMapId() != endPosition.getMapId() || totalDistance > maxDist)
// if (startPosition.GetMapId() != endPosition.GetMapId() || totalDistance > maxDist)
// {
// if (!TravelNodeMap::instance().getNodes().empty() && !bot->InBattleground())
// {
@ -421,7 +421,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// {
// //Use standard PathGenerator to find a route.
// PathGenerator path(mover);
// path.CalculatePath(movePosition.getX(), movePosition.getY(), movePosition.getZ(), false);
// path.CalculatePath(movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), false);
// PathType type = path.GetPathType();
// Movement::PointsArray const& points = path.GetPath();
// movePath.addPath(startPosition.fromPointsArray(points));
@ -485,8 +485,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// else
// {
// LOG_DEBUG("playerbots", "!entry");
// return bot->TeleportTo(movePosition.getMapId(), movePosition.getX(), movePosition.getY(),
// movePosition.getZ(), movePosition.getO(), 0);
// return bot->TeleportTo(movePosition.GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(),
// movePosition.GetPositionZ(), movePosition.GetOrientation(), 0);
// }
// }
@ -563,14 +563,14 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// AI_VALUE(LastMovement&, "last movement").setPath(movePath);
// if (!movePosition || movePosition.getMapId() != bot->GetMapId())
// if (!movePosition || movePosition.GetMapId() != bot->GetMapId())
// {
// movePath.clear();
// AI_VALUE(LastMovement&, "last movement").setPath(movePath);
// if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT))
// botAI->TellMasterNoFacing("No point. Rebuilding.");
// LOG_DEBUG("playerbots", "!movePosition || movePosition.getMapId() != bot->GetMapId()");
// LOG_DEBUG("playerbots", "!movePosition || movePosition.GetMapId() != bot->GetMapId()");
// return false;
// }
@ -609,15 +609,15 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// float cz = z;
// for (auto i : movePath.getPath())
// {
// CreateWp(bot, i.point.getX(), i.point.getY(), i.point.getZ(), 0.f, 2334);
// CreateWp(bot, i.point.GetPositionX(), i.point.GetPositionY(), i.point.GetPositionZ(), 0.f, 2334);
// cx = i.point.getX();
// cy = i.point.getY();
// cz = i.point.getZ();
// cx = i.point.GetPositionX();
// cy = i.point.GetPositionY();
// cz = i.point.GetPositionZ();
// }
// }
// else
// CreateWp(bot, movePosition.getX(), movePosition.getY(), movePosition.getZ(), 0, 2334, true);
// CreateWp(bot, movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), 0, 2334, true);
// }
// //Log bot movement
@ -634,8 +634,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// sPlayerbotAIConfig.log("bot_movement.csv", out.str().c_str());
// }
// // LOG_DEBUG("playerbots", "({}, {}) -> ({}, {})", startPosition.getX(), startPosition.getY(),
// movePosition.getX(), movePosition.getY()); if (!react)
// // LOG_DEBUG("playerbots", "({}, {}) -> ({}, {})", startPosition.GetPositionX(), startPosition.GetPositionY(),
// movePosition.GetPositionX(), movePosition.GetPositionY()); if (!react)
// if (totalDistance > maxDist)
// WaitForReach(startPosition.distance(movePosition) - 10.0f);
// else
@ -671,7 +671,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// // else
// // {
// // mover->GetMotionMaster()->GetDestination(x, y, z);
// // if (movePosition.distance(WorldPosition(movePosition.getMapId(), x, y, z, 0)) > minDist)
// // if (movePosition.distance(WorldPosition(movePosition.GetMapId(), x, y, z, 0)) > minDist)
// // {
// // mover->StopMoving();
// // mover->GetMotionMaster()->Clear();
@ -685,8 +685,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// AI_VALUE(LastMovement&, "last movement").nextTeleport = now +
// (time_t)MoveDelay(startPosition.distance(movePosition)); LOG_DEBUG("playerbots", "totalDistance > maxDist &&
// !detailedMove && !botAI->HasPlayerNearby(&movePosition)"); return bot->TeleportTo(movePosition.getMapId(),
// movePosition.getX(), movePosition.getY(), movePosition.getZ(), startPosition.getAngleTo(movePosition));
// !detailedMove && !botAI->HasPlayerNearby(&movePosition)"); return bot->TeleportTo(movePosition.GetMapId(),
// movePosition.GetPositionX(), movePosition.GetPositionY(), movePosition.GetPositionZ(), startPosition.getAngleTo(movePosition));
// }
// // walk if master walks and is close
@ -708,9 +708,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// if (!bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !bot->HasAuraType(SPELL_AURA_FLY))
// {
// bot->SetWalk(masterWalking);
// bot->GetMotionMaster()->MovePoint(movePosition.getMapId(), movePosition.getX(), movePosition.getY(),
// movePosition.getZ(), generatePath); WaitForReach(startPosition.distance(movePosition));
// // LOG_DEBUG("playerbots", "Movepoint to ({}, {})", movePosition.getX(), movePosition.getY());
// bot->GetMotionMaster()->MovePoint(movePosition.GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(),
// movePosition.GetPositionZ(), generatePath); WaitForReach(startPosition.distance(movePosition));
// // LOG_DEBUG("playerbots", "Movepoint to ({}, {})", movePosition.GetPositionX(), movePosition.GetPositionY());
// }
// else
// {
@ -756,9 +756,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// }
// }
// bot->GetMotionMaster()->MovePoint(movePosition.getMapId(), Position(movePosition.getX(), movePosition.getY(),
// movePosition.getZ(), 0.f)); WaitForReach(startPosition.distance(movePosition)); LOG_DEBUG("playerbots",
// "Movepoint to ({}, {})", movePosition.getX(), movePosition.getY());
// bot->GetMotionMaster()->MovePoint(movePosition.GetMapId(), Position(movePosition.GetPositionX(), movePosition.GetPositionY(),
// movePosition.GetPositionZ(), 0.f)); WaitForReach(startPosition.distance(movePosition)); LOG_DEBUG("playerbots",
// "Movepoint to ({}, {})", movePosition.GetPositionX(), movePosition.GetPositionY());
// }
// AI_VALUE(LastMovement&, "last movement").setShort(movePosition);
@ -1190,7 +1190,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
WorldPosition cPos(corpse);
if (botPos.fDist(cPos) > sPlayerbotAIConfig.spellDistance)
return MoveTo(cPos.getMapId(), cPos.getX(), cPos.getY(), cPos.getZ());
return MoveTo(cPos.GetMapId(), cPos.GetPositionX(), cPos.GetPositionY(), cPos.GetPositionZ());
}
}
@ -1217,7 +1217,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
if ((lDist * 1.5 < tDist && ang < static_cast<float>(M_PI) / 2) ||
target->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
return MoveTo(longMove.getMapId(), longMove.getX(), longMove.getY(), longMove.getZ());
return MoveTo(longMove.GetMapId(), longMove.GetPositionX(), longMove.GetPositionY(), longMove.GetPositionZ());
}
}
}

View File

@ -49,17 +49,15 @@ bool DrinkAction::Execute(Event event)
bool DrinkAction::isUseful()
{
return UseItemAction::isUseful() &&
AI_VALUE2(bool, "has mana", "self target") &&
return UseItemAction::isUseful() && AI_VALUE2(bool, "has mana", "self target") &&
AI_VALUE2(uint8, "mana", "self target") < 100;
}
bool DrinkAction::isPossible()
{
return !bot->IsInCombat() &&
!bot->IsMounted() &&
!botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form",
"aquatic form","flight form", "swift flight form", nullptr) &&
return !bot->IsInCombat() && !bot->IsMounted() &&
!botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form",
"flight form", "swift flight form", nullptr) &&
(botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible());
}
@ -102,17 +100,12 @@ bool EatAction::Execute(Event event)
return UseItemAction::Execute(event);
}
bool EatAction::isUseful()
{
return UseItemAction::isUseful() &&
AI_VALUE2(uint8, "health", "self target") < 100;
}
bool EatAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8, "health", "self target") < 100; }
bool EatAction::isPossible()
{
return !bot->IsInCombat() &&
!bot->IsMounted() &&
!botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form",
"aquatic form","flight form", "swift flight form", nullptr) &&
return !bot->IsInCombat() && !bot->IsMounted() &&
!botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form",
"flight form", "swift flight form", nullptr) &&
(botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible());
}

View File

@ -150,7 +150,7 @@ bool FindCorpseAction::Execute(Event event)
{
float rx, ry, rz;
if (manager.CalculateDestination(&rx, &ry, &rz))
moveToPos = WorldPosition(moveToPos.getMapId(), rx, ry, rz, 0.0);
moveToPos = WorldPosition(moveToPos.GetMapId(), rx, ry, rz, 0.0);
else if (!moveToPos.GetReachableRandomPointOnGround(bot, reclaimDist, urand(0, 1)))
moveToPos = corpsePos;
}
@ -170,7 +170,7 @@ bool FindCorpseAction::Execute(Event event)
{
bot->GetMotionMaster()->Clear();
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
bot->TeleportTo(moveToPos.GetMapId(), moveToPos.GetPositionX(), moveToPos.GetPositionY(), moveToPos.GetPositionZ(), 0);
}
moved = true;
@ -184,7 +184,7 @@ bool FindCorpseAction::Execute(Event event)
if (deadTime < 10 * MINUTE && dCount < 5) // Look for corpse up to 30 minutes.
{
moved =
MoveTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), false, false);
MoveTo(moveToPos.GetMapId(), moveToPos.GetPositionX(), moveToPos.GetPositionY(), moveToPos.GetPositionZ(), false, false);
}
if (!moved)
@ -237,10 +237,10 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone)
{
uint32 areaId = 0;
uint32 zoneId = 0;
sMapMgr->GetZoneAndAreaId(bot->GetPhaseMask(), zoneId, areaId, travelPos.getMapId(), travelPos.getX(),
travelPos.getY(), travelPos.getZ());
ClosestGrave = sGraveyard->GetClosestGraveyard(travelPos.getMapId(), travelPos.getX(), travelPos.getY(),
travelPos.getZ(), bot->GetTeamId(), areaId, zoneId,
sMapMgr->GetZoneAndAreaId(bot->GetPhaseMask(), zoneId, areaId, travelPos.GetMapId(), travelPos.GetPositionX(),
travelPos.GetPositionY(), travelPos.GetPositionZ());
ClosestGrave = sGraveyard->GetClosestGraveyard(travelPos.GetMapId(), travelPos.GetPositionX(), travelPos.GetPositionY(),
travelPos.GetPositionZ(), bot->GetTeamId(), areaId, zoneId,
bot->getClass() == CLASS_DEATH_KNIGHT);
if (ClosestGrave)

View File

@ -85,7 +85,7 @@ bool RpgAction::SetNextRpgAction()
isChecked = true;
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
if (!dynamic_cast<RpgEnabled*>(action) || !action->isPossible() || !action->isUseful())
if (!dynamic_cast<RpgEnabled*>(action) || !action->isUseful() || !action->isPossible())
continue;
actions.push_back(action);

View File

@ -150,7 +150,7 @@ bool RpgTaxiAction::Execute(Event event)
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
uint32 node =
sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), guidP.GetMapId(), bot->GetTeamId());
std::vector<uint32> nodes;
for (uint32 i = 0; i < sTaxiPathStore.GetNumRows(); ++i)
@ -208,7 +208,7 @@ bool RpgDiscoverAction::Execute(Event event)
GuidPosition guidP = rpg->guidP();
uint32 node =
sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(), guidP.GetMapId(), bot->GetTeamId());
if (!node)
return false;

View File

@ -80,8 +80,9 @@ bool RTSCAction::Execute(Event event)
SET_AI_VALUE2(WorldPosition, "RTSC saved location", locationName, spellPosition);
Creature* wpCreature =
bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(),
spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f);
bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(),
spellPosition.GetPositionZ(), spellPosition.GetOrientation(), TEMPSUMMON_TIMED_DESPAWN,
2000.0f);
wpCreature->SetObjectScale(0.5f);
return true;
@ -110,8 +111,9 @@ bool RTSCAction::Execute(Event event)
if (spellPosition)
{
Creature* wpCreature =
bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(),
spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f);
bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(),
spellPosition.GetPositionZ(), spellPosition.GetOrientation(),
TEMPSUMMON_TIMED_DESPAWN, 2000.0f);
wpCreature->SetObjectScale(0.5f);
}

View File

@ -134,8 +134,8 @@ bool SeeSpellAction::Execute(Event event)
SET_AI_VALUE2(WorldPosition, "RTSC saved location", locationName, spellPosition);
Creature* wpCreature =
bot->SummonCreature(15631, spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(),
spellPosition.getO(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f);
bot->SummonCreature(15631, spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(),
spellPosition.GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 2000.0f);
wpCreature->SetObjectScale(0.5f);
RESET_AI_VALUE(std::string, "RTSC next spell action");
@ -167,14 +167,14 @@ bool SeeSpellAction::MoveToSpell(WorldPosition& spellPosition, bool inFormation)
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
PositionInfo stayPosition = posMap["stay"];
stayPosition.Set(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), spellPosition.getMapId());
stayPosition.Set(spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), spellPosition.GetMapId());
posMap["stay"] = stayPosition;
}
if (bot->IsWithinLOS(spellPosition.getX(), spellPosition.getY(), spellPosition.getZ()))
return MoveNear(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), 0);
if (bot->IsWithinLOS(spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ()))
return MoveNear(spellPosition.GetMapId(), spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), 0);
return MoveTo(spellPosition.getMapId(), spellPosition.getX(), spellPosition.getY(), spellPosition.getZ(), false,
return MoveTo(spellPosition.GetMapId(), spellPosition.GetPositionX(), spellPosition.GetPositionY(), spellPosition.GetPositionZ(), false,
false);
}

View File

@ -7,9 +7,9 @@
#include "ChatHelper.h"
#include "Event.h"
#include "ItemPackets.h"
#include "ItemUsageValue.h"
#include "Playerbots.h"
#include "ItemPackets.h"
bool UseItemAction::Execute(Event event)
{
@ -416,13 +416,6 @@ bool UseHearthStone::Execute(Event event)
bool UseHearthStone::isUseful() { return !bot->InBattleground(); }
bool UseRandomRecipe::isUseful()
{
return !bot->IsInCombat() && !botAI->HasActivePlayerMaster() && !bot->InBattleground();
}
bool UseRandomRecipe::isPossible() { return AI_VALUE2(uint32, "item count", "recipe") > 0; }
bool UseRandomRecipe::Execute(Event event)
{
std::vector<Item*> recipes = AI_VALUE2(std::vector<Item*>, "inventory items", "recipe");
@ -445,12 +438,12 @@ bool UseRandomRecipe::Execute(Event event)
return used;
}
bool UseRandomQuestItem::isUseful()
bool UseRandomRecipe::isUseful()
{
return !botAI->HasActivePlayerMaster() && !bot->InBattleground() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT);
return !bot->IsInCombat() && !botAI->HasActivePlayerMaster() && !bot->InBattleground();
}
bool UseRandomQuestItem::isPossible() { return AI_VALUE2(uint32, "item count", "quest") > 0; }
bool UseRandomRecipe::isPossible() { return AI_VALUE2(uint32, "item count", "recipe") > 0; }
bool UseRandomQuestItem::Execute(Event event)
{
@ -478,7 +471,6 @@ bool UseRandomQuestItem::Execute(Event event)
break;
}
}
}
if (!item)
@ -490,3 +482,10 @@ bool UseRandomQuestItem::Execute(Event event)
return used;
}
bool UseRandomQuestItem::isUseful()
{
return !botAI->HasActivePlayerMaster() && !bot->InBattleground() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT);
}
bool UseRandomQuestItem::isPossible() { return AI_VALUE2(uint32, "item count", "quest") > 0; }

View File

@ -69,8 +69,8 @@ class UseHearthStone : public UseItemAction
public:
UseHearthStone(PlayerbotAI* botAI) : UseItemAction(botAI, "hearthstone", true) {}
bool isUseful() override;
bool Execute(Event event) override;
bool isUseful() override;
};
class UseRandomRecipe : public UseItemAction
@ -78,9 +78,9 @@ class UseRandomRecipe : public UseItemAction
public:
UseRandomRecipe(PlayerbotAI* botAI) : UseItemAction(botAI, "random recipe", true) {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
bool Execute(Event event) override;
};
class UseRandomQuestItem : public UseItemAction
@ -88,9 +88,9 @@ class UseRandomQuestItem : public UseItemAction
public:
UseRandomQuestItem(PlayerbotAI* botAI) : UseItemAction(botAI, "random quest item", true) {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
bool Execute(Event event) override;
};
#endif

View File

@ -46,7 +46,8 @@ bool RpgTaxiTrigger::IsActive()
return false;
uint32 node =
sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(),
guidP.GetMapId(), bot->GetTeamId());
if (!node)
return false;
@ -68,7 +69,8 @@ bool RpgDiscoverTrigger::IsActive()
return false;
uint32 node =
sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
sObjectMgr->GetNearestTaxiNode(guidP.GetPositionX(), guidP.GetPositionY(), guidP.GetPositionZ(),
guidP.GetMapId(), bot->GetTeamId());
if (bot->m_taxi.IsTaximaskNodeKnown(node))
return false;

View File

@ -90,7 +90,7 @@ bool MoveLongStuckTrigger::IsActive()
}
if (cell.GridX() > 0 && cell.GridY() > 0 &&
!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(botPos.getMapId(), cell.GridX(), cell.GridY()))
!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(),

View File

@ -11,6 +11,10 @@
bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
{
// Apply parent's filtering first (includes level difference checks)
if (!PossibleTargetsValue::AcceptUnit(unit))
return false;
bool inCannon = botAI->IsInVehicle(false, true);
Player* enemy = dynamic_cast<Player*>(unit);
if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() &&
@ -19,7 +23,14 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
!(enemy->HasSpiritOfRedemptionAura()))
{
// If with master, only attack if master is PvP flagged
Player* master = botAI->GetMaster();
if (master && !master->IsPvP() && !master->IsFFAPvP())
return false;
return true;
}
return false;
}

View File

@ -107,7 +107,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
continue;
}
bool inactiveGrindStatus = botAI->rpgInfo.status != RPG_WANDER_RANDOM && botAI->rpgInfo.status != RPG_IDLE;
bool inactiveGrindStatus = botAI->rpgInfo.GetStatus() != RPG_WANDER_RANDOM && botAI->rpgInfo.GetStatus() != RPG_IDLE;
float aggroRange = 30.0f;
if (unit->ToCreature())

View File

@ -16,6 +16,20 @@
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "Unit.h"
#include "AreaDefines.h"
// Level difference thresholds for attack probability
constexpr int32 EXTREME_LEVEL_DIFF = 5; // Don't attack if enemy is this much higher
constexpr int32 HIGH_LEVEL_DIFF = 4; // 25% chance at +/- this difference
constexpr int32 MID_LEVEL_DIFF = 3; // 50% chance at +/- this difference
constexpr int32 LOW_LEVEL_DIFF = 2; // 75% chance at +/- this difference
// Time window for deterministic attack decisions
constexpr uint32 ATTACK_DECISION_TIME_WINDOW = 2 * MINUTE;
// 64 bit FNV-1a hash constants
constexpr uint64_t FNV_OFFSET_BASIS = 14695981039346656037ULL;
constexpr uint64_t FNV_PRIME = 1099511628211ULL;
void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
{
@ -24,7 +38,103 @@ void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
Cell::VisitObjects(bot, searcher, range);
}
bool PossibleTargetsValue::AcceptUnit(Unit* unit) { return AttackersValue::IsPossibleTarget(unit, bot, range); }
bool PossibleTargetsValue::AcceptUnit(Unit* unit)
{
if (!AttackersValue::IsPossibleTarget(unit, bot, range))
return false;
// Level-based PvP restrictions
if (unit->IsPlayer())
{
// Self-defense - always allow fighting back
if (bot->IsInCombat() && bot->GetVictim() == unit)
return true; // Already fighting
Unit* botAttacker = bot->getAttackerForHelper();
if (botAttacker)
{
if (botAttacker == unit)
return true; // Enemy attacking
if (botAttacker->IsPet())
{
Unit* petOwner = botAttacker->GetOwner();
if (petOwner && petOwner == unit)
return true; // Enemy's pet attacking
}
}
// Skip restrictions in BG/Arena
if (bot->InBattleground() || bot->InArena())
return true;
// Skip restrictions if in duel with this player
if (bot->duel && bot->duel->Opponent == unit)
return true;
// Capital cities - no restrictions
uint32 zoneId = bot->GetZoneId();
bool inCapitalCity = (zoneId == AREA_STORMWIND_CITY ||
zoneId == AREA_IRONFORGE ||
zoneId == AREA_DARNASSUS ||
zoneId == AREA_THE_EXODAR ||
zoneId == AREA_ORGRIMMAR ||
zoneId == AREA_THUNDER_BLUFF ||
zoneId == AREA_UNDERCITY ||
zoneId == AREA_SILVERMOON_CITY);
if (inCapitalCity)
return true;
// Level difference check
int32 levelDifference = unit->GetLevel() - bot->GetLevel();
int32 absLevelDifference = std::abs(levelDifference);
// Extreme difference - do not attack
if (levelDifference >= EXTREME_LEVEL_DIFF)
return false;
// Calculate attack chance based on level difference
uint32 attackChance = 100; // Default 100%: Bot and target's levels are very close
// There's a chance a bot might gank on an extremly low target
if ((absLevelDifference < EXTREME_LEVEL_DIFF && absLevelDifference >= HIGH_LEVEL_DIFF) ||
levelDifference <= -EXTREME_LEVEL_DIFF)
attackChance = 25;
else if (absLevelDifference < HIGH_LEVEL_DIFF && absLevelDifference >= MID_LEVEL_DIFF)
attackChance = 50;
else if (absLevelDifference < MID_LEVEL_DIFF && absLevelDifference >= LOW_LEVEL_DIFF)
attackChance = 75;
// If probability check needed, use deterministic hash-based decision
if (attackChance < 100)
{
// Decisions remain stable for ATTACK_DECISION_TIME_WINDOW.
time_t timeWindow = time(nullptr) / ATTACK_DECISION_TIME_WINDOW;
// FNV-1a hash used to deterministically convert botGUID, targetGUID, and timeWindow
// into a consistent percentage chance without needing to cache previous decisions.
// See: http://www.isthe.com/chongo/tech/comp/fnv/
uint64_t hash = FNV_OFFSET_BASIS;
// Diffuse bot GUID, target GUID, and time window into the hash
hash ^= bot->GetGUID().GetRawValue();
hash *= FNV_PRIME;
hash ^= unit->GetGUID().GetRawValue();
hash *= FNV_PRIME;
hash ^= static_cast<uint64_t>(timeWindow);
hash *= FNV_PRIME;
// Convert hash to 0-99 range and compare against attack chance percentage.
// Ex: attackChance=75: hash 0-74 = attack (75%), hash 75-99 = don't attack (25%)
return (hash % 100) < attackChance;
}
}
return true;
}
void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
{
@ -36,9 +146,8 @@ void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
bool PossibleTriggersValue::AcceptUnit(Unit* unit)
{
if (!unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
return false;
}
Unit::AuraEffectList const& aurasPeriodicTriggerSpell =
unit->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL);
Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell =
@ -58,11 +167,9 @@ bool PossibleTriggersValue::AcceptUnit(Unit* unit)
for (int j = 0; j < MAX_SPELL_EFFECTS; j++)
{
if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
{
return true;
}
}
}
}
return false;
}

View File

@ -7,20 +7,19 @@
#include "Playerbots.h"
bool CastBearFormAction::isPossible()
{
return CastBuffSpellAction::isPossible() && !botAI->HasAura("dire bear form", GetTarget());
}
bool CastBearFormAction::isUseful()
{
return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget());
}
bool CastBearFormAction::isPossible()
{
return CastBuffSpellAction::isPossible() && !botAI->HasAura("dire bear form", GetTarget());
}
std::vector<NextAction> CastDireBearFormAction::getAlternatives()
{
return NextAction::merge({ NextAction("bear form") },
CastSpellAction::getAlternatives());
return NextAction::merge({NextAction("bear form")}, CastSpellAction::getAlternatives());
}
bool CastTravelFormAction::isUseful()
@ -32,22 +31,17 @@ bool CastTravelFormAction::isUseful()
!botAI->HasAura("dash", bot);
}
bool CastCasterFormAction::isUseful()
{
return botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form",
"flight form", "swift flight form", "moonkin form", nullptr) &&
AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth;
}
bool CastCasterFormAction::Execute(Event event)
{
botAI->RemoveShapeshift();
return true;
}
bool CastCancelTreeFormAction::isUseful()
bool CastCasterFormAction::isUseful()
{
return botAI->HasAura(33891, bot);
return botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form",
"flight form", "swift flight form", "moonkin form", nullptr) &&
AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth;
}
bool CastCancelTreeFormAction::Execute(Event event)
@ -56,6 +50,8 @@ bool CastCancelTreeFormAction::Execute(Event event)
return true;
}
bool CastCancelTreeFormAction::isUseful() { return botAI->HasAura(33891, bot); }
bool CastTreeFormAction::isUseful()
{
return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot);

View File

@ -15,8 +15,8 @@ class CastBearFormAction : public CastBuffSpellAction
public:
CastBearFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bear form") {}
bool isPossible() override;
bool isUseful() override;
bool isPossible() override;
};
class CastDireBearFormAction : public CastBuffSpellAction
@ -37,6 +37,7 @@ class CastTreeFormAction : public CastBuffSpellAction
{
public:
CastTreeFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tree of life") {}
bool isUseful() override;
};
@ -65,9 +66,9 @@ class CastCasterFormAction : public CastBuffSpellAction
public:
CastCasterFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "caster form") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override { return true; }
bool Execute(Event event) override;
};
class CastCancelTreeFormAction : public CastBuffSpellAction
@ -75,9 +76,9 @@ class CastCancelTreeFormAction : public CastBuffSpellAction
public:
CastCancelTreeFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cancel tree form") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override { return true; }
bool Execute(Event event) override;
};
#endif

View File

@ -8,16 +8,14 @@
#include "Event.h"
#include "Playerbots.h"
bool CastRemoveShadowformAction::isUseful() { return botAI->HasAura("shadowform", AI_VALUE(Unit*, "self target")); }
bool CastRemoveShadowformAction::isPossible() { return true; }
bool CastRemoveShadowformAction::Execute(Event event)
{
botAI->RemoveAura("shadowform");
return true;
}
bool CastRemoveShadowformAction::isUseful() { return botAI->HasAura("shadowform", AI_VALUE(Unit*, "self target")); }
Unit* CastPowerWordShieldOnAlmostFullHealthBelowAction::GetTarget()
{
Group* group = bot->GetGroup();

View File

@ -56,7 +56,10 @@ HEAL_PARTY_ACTION(CastRenewOnPartyAction, "renew", 15.0f, HealingManaEfficiency:
class CastPrayerOfMendingAction : public HealPartyMemberAction
{
public:
CastPrayerOfMendingAction(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH, false) {}
CastPrayerOfMendingAction(PlayerbotAI* botAI)
: HealPartyMemberAction(botAI, "prayer of mending", 10.0f, HealingManaEfficiency::HIGH, false)
{
}
};
HEAL_PARTY_ACTION(CastBindingHealAction, "binding heal", 15.0f, HealingManaEfficiency::MEDIUM);
@ -65,7 +68,8 @@ HEAL_PARTY_ACTION(CastPrayerOfHealingAction, "prayer of healing", 15.0f, Healing
class CastCircleOfHealingAction : public HealPartyMemberAction
{
public:
CastCircleOfHealingAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "circle of healing", 15.0f, HealingManaEfficiency::HIGH)
CastCircleOfHealingAction(PlayerbotAI* ai)
: HealPartyMemberAction(ai, "circle of healing", 15.0f, HealingManaEfficiency::HIGH)
{
}
};
@ -134,15 +138,15 @@ class CastRemoveShadowformAction : public Action
public:
CastRemoveShadowformAction(PlayerbotAI* botAI) : Action(botAI, "remove shadowform") {}
bool isUseful() override;
bool isPossible() override;
bool Execute(Event event) override;
bool isUseful() override;
};
class CastDispersionAction : public CastSpellAction
{
public:
CastDispersionAction(PlayerbotAI* ai) : CastSpellAction(ai, "dispersion") {}
virtual std::string const GetTargetName() { return "self target"; }
};
@ -158,6 +162,7 @@ class CastHymnOfHopeAction : public CastSpellAction
{
public:
CastHymnOfHopeAction(PlayerbotAI* ai) : CastSpellAction(ai, "hymn of hope") {}
virtual std::string const GetTargetName() { return "self target"; }
};
@ -165,6 +170,7 @@ class CastDivineHymnAction : public CastSpellAction
{
public:
CastDivineHymnAction(PlayerbotAI* ai) : CastSpellAction(ai, "divine hymn") {}
virtual std::string const GetTargetName() { return "self target"; }
};
@ -172,6 +178,7 @@ class CastShadowfiendAction : public CastSpellAction
{
public:
CastShadowfiendAction(PlayerbotAI* ai) : CastSpellAction(ai, "shadowfiend") {}
virtual std::string const GetTargetName() { return "current target"; }
};
@ -182,6 +189,7 @@ public:
: HealPartyMemberAction(ai, "power word: shield", 15.0f, HealingManaEfficiency::HIGH)
{
}
bool isUseful() override;
Unit* GetTarget() override;
};
@ -193,6 +201,7 @@ public:
: HealPartyMemberAction(ai, "power word: shield", 5.0f, HealingManaEfficiency::HIGH)
{
}
bool isUseful() override;
Unit* GetTarget() override;
};
@ -201,13 +210,17 @@ class CastMindSearAction : public CastSpellAction
{
public:
CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastGuardianSpiritOnPartyAction : public HealPartyMemberAction
{
public:
CastGuardianSpiritOnPartyAction(PlayerbotAI* ai) : HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM) {}
CastGuardianSpiritOnPartyAction(PlayerbotAI* ai)
: HealPartyMemberAction(ai, "guardian spirit", 40.0f, HealingManaEfficiency::MEDIUM)
{
}
};
#endif

View File

@ -27,6 +27,7 @@ class CastHungerForBloodAction : public CastBuffSpellAction
{
public:
CastHungerForBloodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "hunger for blood") {}
std::string const GetTargetName() override { return "current target"; }
};
@ -43,9 +44,9 @@ class CastStealthAction : public CastBuffSpellAction
public:
CastStealthAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "stealth") {}
std::string const GetTargetName() override { return "self target"; }
bool isUseful() override;
bool isPossible() override;
std::string const GetTargetName() override { return "self target"; }
};
class UnstealthAction : public Action
@ -61,8 +62,8 @@ class CheckStealthAction : public Action
public:
CheckStealthAction(PlayerbotAI* botAI) : Action(botAI, "check stealth") {}
bool isPossible() override { return true; }
bool Execute(Event event) override;
bool isPossible() override { return true; }
};
class CastKickAction : public CastSpellAction
@ -131,6 +132,7 @@ class CastEnvenomAction : public CastMeleeSpellAction
{
public:
CastEnvenomAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "envenom") {}
bool isUseful() override;
bool isPossible() override;
};
@ -139,37 +141,42 @@ class CastTricksOfTheTradeOnMainTankAction : public BuffOnMainTankAction
{
public:
CastTricksOfTheTradeOnMainTankAction(PlayerbotAI* ai) : BuffOnMainTankAction(ai, "tricks of the trade", true) {}
virtual bool isUseful() override;
bool isUseful() override;
};
class UseDeadlyPoisonAction : public UseItemAction
{
public:
UseDeadlyPoisonAction(PlayerbotAI* ai) : UseItemAction(ai, "Deadly Poison") {}
virtual bool Execute(Event event) override;
virtual bool isPossible() override;
bool Execute(Event event) override;
bool isPossible() override;
};
class UseInstantPoisonAction : public UseItemAction
{
public:
UseInstantPoisonAction(PlayerbotAI* ai) : UseItemAction(ai, "Instant Poison") {}
virtual bool Execute(Event event) override;
virtual bool isPossible() override;
bool Execute(Event event) override;
bool isPossible() override;
};
class UseInstantPoisonOffHandAction : public UseItemAction
{
public:
UseInstantPoisonOffHandAction(PlayerbotAI* ai) : UseItemAction(ai, "Instant Poison Off Hand") {}
virtual bool Execute(Event event) override;
virtual bool isPossible() override;
bool Execute(Event event) override;
bool isPossible() override;
};
class FanOfKnivesAction : public CastMeleeSpellAction
{
public:
FanOfKnivesAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "fan of knives") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};

View File

@ -176,20 +176,19 @@ Unit* CastShatteringThrowAction::GetTarget()
return nullptr; // No valid target
}
bool CastShatteringThrowAction::Execute(Event event)
{
Unit* target = GetTarget();
if (!target)
return false;
return botAI->CastSpell("shattering throw", target);
}
bool CastShatteringThrowAction::isUseful()
{
// Spell cooldown check
if (!bot->HasSpell(64382))
{
if (!bot->HasSpell(64382) || bot->HasSpellCooldown(64382))
return false;
}
// Spell cooldown check
if (bot->HasSpellCooldown(64382))
{
return false;
}
GuidVector enemies = AI_VALUE(GuidVector, "possible targets");
@ -220,25 +219,12 @@ bool CastShatteringThrowAction::isPossible()
// Range check: Shattering Throw is 30 yards
if (!bot->IsWithinDistInMap(target, 30.0f))
{
return false;
}
// Check line of sight
if (!bot->IsWithinLOSInMap(target))
{
return false;
}
// If the minimal checks above pass, simply return true.
return true;
}
bool CastShatteringThrowAction::Execute(Event event)
{
Unit* target = GetTarget();
if (!target)
return false;
return botAI->CastSpell("shattering throw", target);
}

View File

@ -25,8 +25,7 @@ MELEE_ACTION_U(CastBattleShoutTauntAction, "battle shout", CastSpellAction::isUs
class CastDemoralizingShoutAction : public CastMeleeDebuffSpellAction
{
public:
CastDemoralizingShoutAction(PlayerbotAI* botAI)
: CastMeleeDebuffSpellAction(botAI, "demoralizing shout") {}
CastDemoralizingShoutAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "demoralizing shout") {}
};
class CastDemoralizingShoutWithoutLifeTimeCheckAction : public CastMeleeDebuffSpellAction
@ -140,8 +139,8 @@ class CastVigilanceAction : public BuffOnPartyAction
public:
CastVigilanceAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "vigilance") {}
Unit* GetTarget() override;
bool Execute(Event event) override;
Unit* GetTarget() override;
};
class CastRetaliationAction : public CastBuffSpellAction
@ -157,10 +156,10 @@ class CastShatteringThrowAction : public CastSpellAction
public:
CastShatteringThrowAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shattering throw") {}
Unit* GetTarget() override;
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
bool Execute(Event event) override;
Unit* GetTarget() override;
};
#endif

View File

@ -193,17 +193,18 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
{
float angle = i * ANGLE_INCREMENT;
Position potentialPos;
potentialPos.m_positionX = bot->GetPositionX() + SAFE_DISTANCE * cos(angle);
potentialPos.m_positionY = bot->GetPositionY() + SAFE_DISTANCE * sin(angle);
potentialPos.m_positionZ = bot->GetPositionZ();
potentialPos.Relocate(bot->GetPositionX() + SAFE_DISTANCE * cos(angle),
bot->GetPositionY() + SAFE_DISTANCE * sin(angle),
bot->GetPositionZ());
// Check if position is valid (not in a wall)
if (!bot->IsWithinLOS(potentialPos.m_positionX, potentialPos.m_positionY, potentialPos.m_positionZ))
if (!bot->IsWithinLOS(potentialPos.GetPositionX(), potentialPos.GetPositionY(),
potentialPos.GetPositionZ()))
continue;
// Check if position is within maximum allowed distance from boss
if (boss && sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
pow(potentialPos.m_positionY - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
if (boss && sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
continue;
// Score this position based on:
@ -216,8 +217,8 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
float minOrbDist = std::numeric_limits<float>::max();
for (Unit* orb : orbs)
{
float orbDist = sqrt(pow(potentialPos.m_positionX - orb->GetPositionX(), 2) +
pow(potentialPos.m_positionY - orb->GetPositionY(), 2));
float orbDist = sqrt(pow(potentialPos.GetPositionX() - orb->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - orb->GetPositionY(), 2));
minOrbDist = std::min(minOrbDist, orbDist);
}
score += minOrbDist * 2.0f; // Weight orb distance more heavily
@ -225,16 +226,16 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
// Check distance from other players
for (const Position& playerPos : playerPositions)
{
float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) +
pow(potentialPos.m_positionY - playerPos.m_positionY, 2));
float playerDist = sqrt(pow(potentialPos.GetPositionX() - playerPos.GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - playerPos.GetPositionY(), 2));
score += std::min(playerDist, 10.0f); // Cap player distance contribution
}
// Factor in proximity to boss (closer is better, as long as we're safe from orbs)
if (boss)
{
float bossDist = sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
pow(potentialPos.m_positionY - boss->GetPositionY(), 2));
float bossDist = sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2));
// Add points for being closer to boss (inverse relationship)
// but only if we're safely away from orbs
if (minOrbDist > SAFE_DISTANCE)
@ -257,7 +258,8 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
{
botAI->Reset();
// Use MoveTo instead of FleePosition since we already calculated the exact position
return MoveTo(bot->GetMapId(), bestPos.m_positionX, bestPos.m_positionY, bot->GetPositionZ(), false, false,
return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bot->GetPositionZ(), false,
false,
false, true, MovementPriority::MOVEMENT_COMBAT);
}
else

View File

@ -1,9 +1,9 @@
#ifndef _PLAYERBOT_RAIDEOEACTIONS_H
#define _PLAYERBOT_RAIDEOEACTIONS_H
#include "MovementActions.h"
#include "AttackAction.h"
#include "GenericSpellActions.h"
#include "MovementActions.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
@ -13,34 +13,38 @@ const std::pair<float, float> MALYGOS_STACK_POSITION = {755.0f, 1301.0f};
class MalygosPositionAction : public MovementAction
{
public:
MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position")
: MovementAction(botAI, name) {}
MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position") : MovementAction(botAI, name)
{
}
bool Execute(Event event) override;
};
class MalygosTargetAction : public AttackAction
{
public:
MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target")
: AttackAction(botAI, name) {}
MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class PullPowerSparkAction : public CastSpellAction
{
public:
PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark")
: CastSpellAction(botAI, name) {}
bool Execute(Event event) override;
bool isPossible() override;
bool isUseful() override;
};
//class PullPowerSparkAction : public CastSpellAction
//{
//public:
// PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark") : CastSpellAction(botAI, name)
// {
// }
// bool Execute(Event event) override;
// bool isUseful() override;
// bool isPossible() override;
//};
class KillPowerSparkAction : public AttackAction
{
public:
KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark")
: AttackAction(botAI, name) {}
KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
@ -48,6 +52,7 @@ class EoEFlyDrakeAction : public MovementAction
{
public:
EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {}
bool Execute(Event event) override;
bool isPossible() override;
};
@ -56,6 +61,7 @@ class EoEDrakeAttackAction : public Action
{
public:
EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {}
bool Execute(Event event) override;
bool isPossible() override;

View File

@ -164,21 +164,21 @@ namespace GruulsLairHelpers
{
float angle = 2 * M_PI * i / NUM_POSITIONS;
Position candidatePos;
candidatePos.m_positionX = bot->GetPositionX() + SEARCH_RADIUS * cos(angle);
candidatePos.m_positionY = bot->GetPositionY() + SEARCH_RADIUS * sin(angle);
candidatePos.m_positionZ = bot->GetPositionZ();
candidatePos.Relocate(bot->GetPositionX() + SEARCH_RADIUS * cos(angle),
bot->GetPositionY() + SEARCH_RADIUS * sin(angle),
bot->GetPositionZ());
float destX = candidatePos.m_positionX, destY = candidatePos.m_positionY, destZ = candidatePos.m_positionZ;
float destX = candidatePos.GetPositionX();
float destY = candidatePos.GetPositionY();
float destZ = candidatePos.GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), destX, destY, destZ, true))
continue;
if (destX != candidatePos.m_positionX || destY != candidatePos.m_positionY)
if (destX != candidatePos.GetPositionX() || destY != candidatePos.GetPositionY())
continue;
candidatePos.m_positionX = destX;
candidatePos.m_positionY = destY;
candidatePos.m_positionZ = destZ;
candidatePos.Relocate(destX, destY, destZ);
if (IsPositionSafe(botAI, bot, candidatePos))
{

View File

@ -1551,8 +1551,10 @@ Position IccFestergutSporeAction::CalculateSpreadPosition()
float angle = (bot->GetGUID().GetCounter() % 16) * (M_PI / 8);
Position spreadRangedPos = ICC_FESTERGUT_RANGED_SPORE;
spreadRangedPos.m_positionX += cos(angle) * SPREAD_RADIUS;
spreadRangedPos.m_positionY += sin(angle) * SPREAD_RADIUS;
spreadRangedPos.Relocate(spreadRangedPos.GetPositionX() + cos(angle) * SPREAD_RADIUS,
spreadRangedPos.GetPositionY() + sin(angle) * SPREAD_RADIUS,
spreadRangedPos.GetPositionZ(),
spreadRangedPos.GetOrientation());
return spreadRangedPos;
}

View File

@ -640,7 +640,7 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
bool lastBlastNova = lastBlastNovaState[instanceId];
if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
if (lastBlastNova && !blastNovaActive)
blastNovaTimer[instanceId] = now;
lastBlastNovaState[instanceId] = blastNovaActive;

View File

@ -64,17 +64,18 @@ bool StartRpgDoQuestAction::Execute(Event event)
bool NewRpgStatusUpdateAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
switch (info.status)
NewRpgStatus status = info.GetStatus();
switch (status)
{
case RPG_IDLE:
{
return RandomChangeStatus({RPG_GO_CAMP, RPG_GO_GRIND, RPG_WANDER_RANDOM, RPG_WANDER_NPC, RPG_DO_QUEST,
RPG_TRAVEL_FLIGHT, RPG_REST});
}
case RPG_GO_GRIND:
{
WorldPosition& originalPos = info.go_grind.pos;
assert(info.go_grind.pos != WorldPosition());
auto& data = std::get<NewRpgInfo::GoGrind>(info.data);
WorldPosition& originalPos = data.pos;
assert(data.pos != WorldPosition());
// GO_GRIND -> WANDER_RANDOM
if (bot->GetExactDist(originalPos) < 10.0f)
{
@ -85,8 +86,9 @@ bool NewRpgStatusUpdateAction::Execute(Event event)
}
case RPG_GO_CAMP:
{
WorldPosition& originalPos = info.go_camp.pos;
assert(info.go_camp.pos != WorldPosition());
auto& data = std::get<NewRpgInfo::GoCamp>(info.data);
WorldPosition& originalPos = data.pos;
assert(data.pos != WorldPosition());
// GO_CAMP -> WANDER_NPC
if (bot->GetExactDist(originalPos) < 10.0f)
{
@ -126,7 +128,8 @@ bool NewRpgStatusUpdateAction::Execute(Event event)
}
case RPG_TRAVEL_FLIGHT:
{
if (info.flight.inFlight && !bot->IsInFlight())
auto& data = std::get<NewRpgInfo::TravelFlight>(info.data);
if (data.inFlight && !bot->IsInFlight())
{
// flight arrival
info.ChangeToIdle();
@ -154,8 +157,10 @@ bool NewRpgGoGrindAction::Execute(Event event)
{
if (SearchQuestGiverAndAcceptOrReward())
return true;
if (auto* data = std::get_if<NewRpgInfo::GoGrind>(&botAI->rpgInfo.data))
return MoveFarTo(data->pos);
return MoveFarTo(botAI->rpgInfo.go_grind.pos);
return false;
}
bool NewRpgGoCampAction::Execute(Event event)
@ -163,7 +168,10 @@ bool NewRpgGoCampAction::Execute(Event event)
if (SearchQuestGiverAndAcceptOrReward())
return true;
return MoveFarTo(botAI->rpgInfo.go_camp.pos);
if (auto* data = std::get_if<NewRpgInfo::GoCamp>(&botAI->rpgInfo.data))
return MoveFarTo(data->pos);
return false;
}
bool NewRpgWanderRandomAction::Execute(Event event)
@ -177,7 +185,11 @@ bool NewRpgWanderRandomAction::Execute(Event event)
bool NewRpgWanderNpcAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
if (!info.wander_npc.npcOrGo)
auto* dataPtr = std::get_if<NewRpgInfo::WanderNpc>(&info.data);
if (!dataPtr)
return false;
auto& data = *dataPtr;
if (!data.npcOrGo)
{
// No npc can be found, switch to IDLE
ObjectGuid npcOrGo = ChooseNpcOrGameObjectToInteract();
@ -186,33 +198,32 @@ bool NewRpgWanderNpcAction::Execute(Event event)
info.ChangeToIdle();
return true;
}
info.wander_npc.npcOrGo = npcOrGo;
info.wander_npc.lastReach = 0;
data.npcOrGo = npcOrGo;
data.lastReach = 0;
return true;
}
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, info.wander_npc.npcOrGo);
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, data.npcOrGo);
if (object && IsWithinInteractionDist(object))
{
if (!info.wander_npc.lastReach)
if (!data.lastReach)
{
info.wander_npc.lastReach = getMSTime();
data.lastReach = getMSTime();
if (bot->CanInteractWithQuestGiver(object))
InteractWithNpcOrGameObjectForQuest(info.wander_npc.npcOrGo);
InteractWithNpcOrGameObjectForQuest(data.npcOrGo);
return true;
}
if (info.wander_npc.lastReach && GetMSTimeDiffToNow(info.wander_npc.lastReach) < npcStayTime)
if (data.lastReach && GetMSTimeDiffToNow(data.lastReach) < npcStayTime)
return false;
// has reached the npc for more than `npcStayTime`, select the next target
info.wander_npc.npcOrGo = ObjectGuid();
info.wander_npc.lastReach = 0;
data.npcOrGo = ObjectGuid();
data.lastReach = 0;
}
else
{
return MoveWorldObjectTo(info.wander_npc.npcOrGo);
}
return MoveWorldObjectTo(data.npcOrGo);
return true;
}
@ -222,29 +233,33 @@ bool NewRpgDoQuestAction::Execute(Event event)
return true;
NewRpgInfo& info = botAI->rpgInfo;
uint32 questId = RPG_INFO(quest, questId);
const Quest* quest = RPG_INFO(quest, quest);
auto* dataPtr = std::get_if<NewRpgInfo::DoQuest>(&info.data);
if (!dataPtr)
return false;
auto& data = *dataPtr;
uint32 questId = data.questId;
const Quest* quest = data.quest;
uint8 questStatus = bot->GetQuestStatus(questId);
switch (questStatus)
{
case QUEST_STATUS_INCOMPLETE:
return DoIncompleteQuest();
return DoIncompleteQuest(data);
case QUEST_STATUS_COMPLETE:
return DoCompletedQuest();
return DoCompletedQuest(data);
default:
break;
}
botAI->rpgInfo.ChangeToIdle();
info.ChangeToIdle();
return true;
}
bool NewRpgDoQuestAction::DoIncompleteQuest()
bool NewRpgDoQuestAction::DoIncompleteQuest(NewRpgInfo::DoQuest& data)
{
uint32 questId = RPG_INFO(do_quest, questId);
if (botAI->rpgInfo.do_quest.pos != WorldPosition())
uint32 questId = data.questId;
if (data.pos != WorldPosition())
{
/// @TODO: extract to a new function
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
int32 currentObjective = data.objectiveIdx;
// check if the objective has completed
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
@ -263,12 +278,12 @@ bool NewRpgDoQuestAction::DoIncompleteQuest()
// the current objective is completed, clear and find a new objective later
if (completed)
{
botAI->rpgInfo.do_quest.lastReachPOI = 0;
botAI->rpgInfo.do_quest.pos = WorldPosition();
botAI->rpgInfo.do_quest.objectiveIdx = 0;
data.lastReachPOI = 0;
data.pos = WorldPosition();
data.objectiveIdx = 0;
}
}
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
if (data.pos == WorldPosition())
{
std::vector<POIInfo> poiInfo;
if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo))
@ -291,28 +306,28 @@ bool NewRpgDoQuestAction::DoIncompleteQuest()
return false;
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
botAI->rpgInfo.do_quest.lastReachPOI = 0;
botAI->rpgInfo.do_quest.pos = pos;
botAI->rpgInfo.do_quest.objectiveIdx = objectiveIdx;
data.lastReachPOI = 0;
data.pos = pos;
data.objectiveIdx = objectiveIdx;
}
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI)
{
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
return MoveFarTo(data.pos);
}
// Now we are near the quest objective
// kill mobs and looting quest should be done automatically by grind strategy
if (!botAI->rpgInfo.do_quest.lastReachPOI)
if (!data.lastReachPOI)
{
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
data.lastReachPOI = getMSTime();
return true;
}
// stayed at this POI for more than 5 minutes
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
if (GetMSTimeDiffToNow(data.lastReachPOI) >= poiStayTime)
{
bool hasProgression = false;
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
int32 currentObjective = data.objectiveIdx;
// check if the objective has progression
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
@ -339,21 +354,21 @@ bool NewRpgDoQuestAction::DoIncompleteQuest()
return true;
}
// clear and select another poi later
botAI->rpgInfo.do_quest.lastReachPOI = 0;
botAI->rpgInfo.do_quest.pos = WorldPosition();
botAI->rpgInfo.do_quest.objectiveIdx = 0;
data.lastReachPOI = 0;
data.pos = WorldPosition();
data.objectiveIdx = 0;
return true;
}
return MoveRandomNear(20.0f);
}
bool NewRpgDoQuestAction::DoCompletedQuest()
bool NewRpgDoQuestAction::DoCompletedQuest(NewRpgInfo::DoQuest& data)
{
uint32 questId = RPG_INFO(quest, questId);
const Quest* quest = RPG_INFO(quest, quest);
uint32 questId = data.questId;
const Quest* quest = data.quest;
if (RPG_INFO(quest, objectiveIdx) != -1)
if (data.objectiveIdx != -1)
{
// if quest is completed, back to poi with -1 idx to reward
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, quest);
@ -376,26 +391,26 @@ bool NewRpgDoQuestAction::DoCompletedQuest()
return false;
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
botAI->rpgInfo.do_quest.lastReachPOI = 0;
botAI->rpgInfo.do_quest.pos = pos;
botAI->rpgInfo.do_quest.objectiveIdx = -1;
data.lastReachPOI = 0;
data.pos = pos;
data.objectiveIdx = -1;
}
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
if (data.pos == WorldPosition())
return false;
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI)
return MoveFarTo(data.pos);
// Now we are near the qoi of reward
// the quest should be rewarded by SearchQuestGiverAndAcceptOrReward
if (!botAI->rpgInfo.do_quest.lastReachPOI)
if (!data.lastReachPOI)
{
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
data.lastReachPOI = getMSTime();
return true;
}
// stayed at this POI for more than 5 minutes
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
if (GetMSTimeDiffToNow(data.lastReachPOI) >= poiStayTime)
{
// e.g. Can not reward quest to gameobjects
/// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db)
@ -410,29 +425,33 @@ bool NewRpgDoQuestAction::DoCompletedQuest()
bool NewRpgTravelFlightAction::Execute(Event event)
{
NewRpgInfo& info = botAI->rpgInfo;
auto* dataPtr = std::get_if<NewRpgInfo::TravelFlight>(&info.data);
if (!dataPtr)
return false;
auto& data = *dataPtr;
if (bot->IsInFlight())
{
botAI->rpgInfo.flight.inFlight = true;
data.inFlight = true;
return false;
}
Creature* flightMaster = ObjectAccessor::GetCreature(*bot, botAI->rpgInfo.flight.fromFlightMaster);
Creature* flightMaster = ObjectAccessor::GetCreature(*bot, data.fromFlightMaster);
if (!flightMaster || !flightMaster->IsAlive())
{
botAI->rpgInfo.ChangeToIdle();
return true;
}
const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(botAI->rpgInfo.flight.toNode);
const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(data.toNode);
if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE)
{
return MoveFarTo(flightMaster);
}
std::vector<uint32> nodes = {botAI->rpgInfo.flight.fromNode, botAI->rpgInfo.flight.toNode};
std::vector<uint32> nodes = {data.fromNode, data.toNode};
botAI->RemoveShapeshift();
if (bot->IsMounted())
{
bot->Dismount();
}
if (!bot->ActivateTaxiPathTo(nodes, flightMaster, 0))
{
LOG_DEBUG("playerbots", "[New RPG] {} active taxi path {} (from {} to {}) failed", bot->GetName(),

View File

@ -90,8 +90,8 @@ public:
bool Execute(Event event) override;
protected:
bool DoIncompleteQuest();
bool DoCompletedQuest();
bool DoIncompleteQuest(NewRpgInfo::DoQuest& data);
bool DoCompletedQuest(NewRpgInfo::DoQuest& data);
const uint32 poiStayTime = 5 * 60 * 1000;
};

View File

@ -66,7 +66,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
"playerbots",
"[New RPG] Teleport {} from ({},{},{},{}) to ({},{},{},{}) as it stuck when moving far - Zone: {} ({})",
bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.GetMapId(), bot->GetZoneId(),
zone_name);
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
return bot->TeleportTo(dest);
@ -75,7 +75,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
float dis = bot->GetExactDist(dest);
if (dis < pathFinderDis)
{
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
return MoveTo(dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
false, true);
}

View File

@ -6,71 +6,65 @@
void NewRpgInfo::ChangeToGoGrind(WorldPosition pos)
{
Reset();
status = RPG_GO_GRIND;
go_grind = GoGrind();
go_grind.pos = pos;
startT = getMSTime();
data = GoGrind{pos};
}
void NewRpgInfo::ChangeToGoCamp(WorldPosition pos)
{
Reset();
status = RPG_GO_CAMP;
go_camp = GoCamp();
go_camp.pos = pos;
startT = getMSTime();
data = GoCamp{pos};
}
void NewRpgInfo::ChangeToWanderNpc()
{
Reset();
status = RPG_WANDER_NPC;
wander_npc = WanderNpc();
startT = getMSTime();
data = WanderNpc{};
}
void NewRpgInfo::ChangeToWanderRandom()
{
Reset();
status = RPG_WANDER_RANDOM;
WANDER_RANDOM = WanderRandom();
startT = getMSTime();
data = WanderRandom{};
}
void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest)
{
Reset();
status = RPG_DO_QUEST;
do_quest = DoQuest();
startT = getMSTime();
DoQuest do_quest;
do_quest.questId = questId;
do_quest.quest = quest;
data = do_quest;
}
void NewRpgInfo::ChangeToTravelFlight(ObjectGuid fromFlightMaster, uint32 fromNode, uint32 toNode)
{
Reset();
status = RPG_TRAVEL_FLIGHT;
flight = TravelFlight();
startT = getMSTime();
TravelFlight flight;
flight.fromFlightMaster = fromFlightMaster;
flight.fromNode = fromNode;
flight.toNode = toNode;
flight.inFlight = false;
data = flight;
}
void NewRpgInfo::ChangeToRest()
{
Reset();
status = RPG_REST;
rest = Rest();
startT = getMSTime();
data = Rest{};
}
void NewRpgInfo::ChangeToIdle()
{
Reset();
status = RPG_IDLE;
startT = getMSTime();
data = Idle{};
}
bool NewRpgInfo::CanChangeTo(NewRpgStatus status) { return true; }
void NewRpgInfo::Reset()
{
*this = NewRpgInfo();
data = Idle{};
startT = getMSTime();
}
@ -82,58 +76,83 @@ void NewRpgInfo::SetMoveFarTo(WorldPosition pos)
moveFarPos = pos;
}
NewRpgStatus NewRpgInfo::GetStatus()
{
return std::visit([](auto&& arg) -> NewRpgStatus {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Idle>) return RPG_IDLE;
if constexpr (std::is_same_v<T, GoGrind>) return RPG_GO_GRIND;
if constexpr (std::is_same_v<T, GoCamp>) return RPG_GO_CAMP;
if constexpr (std::is_same_v<T, WanderNpc>) return RPG_WANDER_NPC;
if constexpr (std::is_same_v<T, WanderRandom>) return RPG_WANDER_RANDOM;
if constexpr (std::is_same_v<T, Rest>) return RPG_REST;
if constexpr (std::is_same_v<T, DoQuest>) return RPG_DO_QUEST;
if constexpr (std::is_same_v<T, TravelFlight>) return RPG_TRAVEL_FLIGHT;
return RPG_IDLE;
}, data);
}
std::string NewRpgInfo::ToString()
{
std::stringstream out;
out << "Status: ";
switch (status)
std::visit([&out, this](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, GoGrind>)
{
case RPG_GO_GRIND:
out << "GO_GRIND";
out << "\nGrindPos: " << go_grind.pos.GetMapId() << " " << go_grind.pos.GetPositionX() << " "
<< go_grind.pos.GetPositionY() << " " << go_grind.pos.GetPositionZ();
out << "\nGrindPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " "
<< arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ();
out << "\nlastGoGrind: " << startT;
break;
case RPG_GO_CAMP:
}
else if constexpr (std::is_same_v<T, GoCamp>)
{
out << "GO_CAMP";
out << "\nCampPos: " << go_camp.pos.GetMapId() << " " << go_camp.pos.GetPositionX() << " "
<< go_camp.pos.GetPositionY() << " " << go_camp.pos.GetPositionZ();
out << "\nCampPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " "
<< arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ();
out << "\nlastGoCamp: " << startT;
break;
case RPG_WANDER_NPC:
}
else if constexpr (std::is_same_v<T, WanderNpc>)
{
out << "WANDER_NPC";
out << "\nnpcOrGoEntry: " << wander_npc.npcOrGo.GetCounter();
out << "\nnpcOrGoEntry: " << arg.npcOrGo.GetCounter();
out << "\nlastWanderNpc: " << startT;
out << "\nlastReachNpcOrGo: " << wander_npc.lastReach;
break;
case RPG_WANDER_RANDOM:
out << "\nlastReachNpcOrGo: " << arg.lastReach;
}
else if constexpr (std::is_same_v<T, WanderRandom>)
{
out << "WANDER_RANDOM";
out << "\nlastWanderRandom: " << startT;
break;
case RPG_IDLE:
}
else if constexpr (std::is_same_v<T, Idle>)
{
out << "IDLE";
break;
case RPG_REST:
}
else if constexpr (std::is_same_v<T, Rest>)
{
out << "REST";
out << "\nlastRest: " << startT;
break;
case RPG_DO_QUEST:
out << "DO_QUEST";
out << "\nquestId: " << do_quest.questId;
out << "\nobjectiveIdx: " << do_quest.objectiveIdx;
out << "\npoiPos: " << do_quest.pos.GetMapId() << " " << do_quest.pos.GetPositionX() << " "
<< do_quest.pos.GetPositionY() << " " << do_quest.pos.GetPositionZ();
out << "\nlastReachPOI: " << do_quest.lastReachPOI ? GetMSTimeDiffToNow(do_quest.lastReachPOI) : 0;
break;
case RPG_TRAVEL_FLIGHT:
out << "TRAVEL_FLIGHT";
out << "\nfromFlightMaster: " << flight.fromFlightMaster.GetEntry();
out << "\nfromNode: " << flight.fromNode;
out << "\ntoNode: " << flight.toNode;
out << "\ninFlight: " << flight.inFlight;
break;
default:
out << "UNKNOWN";
}
else if constexpr (std::is_same_v<T, DoQuest>)
{
out << "DO_QUEST";
out << "\nquestId: " << arg.questId;
out << "\nobjectiveIdx: " << arg.objectiveIdx;
out << "\npoiPos: " << arg.pos.GetMapId() << " " << arg.pos.GetPositionX() << " "
<< arg.pos.GetPositionY() << " " << arg.pos.GetPositionZ();
out << "\nlastReachPOI: " << (arg.lastReachPOI ? GetMSTimeDiffToNow(arg.lastReachPOI) : 0);
}
else if constexpr (std::is_same_v<T, TravelFlight>)
{
out << "TRAVEL_FLIGHT";
out << "\nfromFlightMaster: " << arg.fromFlightMaster.GetEntry();
out << "\nfromNode: " << arg.fromNode;
out << "\ntoNode: " << arg.toNode;
out << "\ninFlight: " << arg.inFlight;
}
else
out << "UNKNOWN";
}, data);
return out.str();
}

View File

@ -13,7 +13,8 @@ using NewRpgStatusTransitionProb = std::vector<std::vector<int>>;
struct NewRpgInfo
{
NewRpgInfo() {}
NewRpgInfo() : data(Idle{}) {}
~NewRpgInfo() = default;
// RPG_GO_GRIND
struct GoGrind
@ -61,7 +62,6 @@ struct NewRpgInfo
struct Idle
{
};
NewRpgStatus status{RPG_IDLE};
uint32 startT{0}; // start timestamp of the current status
@ -72,18 +72,19 @@ struct NewRpgInfo
WorldPosition moveFarPos;
// END MOVE_FAR
union
{
GoGrind go_grind;
GoCamp go_camp;
WanderNpc wander_npc;
WanderRandom WANDER_RANDOM;
DoQuest do_quest;
Rest rest;
DoQuest quest;
TravelFlight flight;
};
using RpgData = std::variant<
Idle,
GoGrind,
GoCamp,
WanderNpc,
WanderRandom,
DoQuest,
Rest,
TravelFlight
>;
RpgData data;
NewRpgStatus GetStatus();
bool HasStatusPersisted(uint32 maxDuration) { return GetMSTimeDiffToNow(startT) > maxDuration; }
void ChangeToGoGrind(WorldPosition pos);
void ChangeToGoCamp(WorldPosition pos);
@ -127,7 +128,4 @@ struct NewRpgStatistic
}
};
// not sure is it necessary but keep it for now
#define RPG_INFO(x, y) botAI->rpgInfo.x.y
#endif

View File

@ -1,4 +1,4 @@
#include "NewRpgTriggers.h"
#include "PlayerbotAI.h"
bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; }
bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.GetStatus(); }

View File

@ -60,8 +60,27 @@ public:
virtual ~Action(void) {}
virtual bool Execute([[maybe_unused]] Event event) { return true; }
virtual bool isPossible() { return true; }
/**
* @brief First validation check - determines if this action is contextually useful
*
* Performs lightweight checks to evaluate whether the action makes sense
* in the current situation. Called before isPossible() during action selection.
*
* @return true if the action is useful, false otherwise
*/
virtual bool isUseful() { return true; }
/**
* @brief Second validation check - determines if this action can be executed
*
* Performs hard pre-execution validation against the event and game state.
* Called after isUseful() passes, before Execute().
*
* @return true if the action is possible, false otherwise
*/
virtual bool isPossible() { return true; }
virtual std::vector<NextAction> getPrerequisites() { return {}; }
virtual std::vector<NextAction> getAlternatives() { return {}; }
virtual std::vector<NextAction> getContinuers() { return {}; }

View File

@ -323,18 +323,18 @@ ActionResult Engine::ExecuteAction(std::string const name, Event event, std::str
q->Qualify(qualifier);
}
if (!action->isPossible())
{
delete actionNode;
return ACTION_RESULT_IMPOSSIBLE;
}
if (!action->isUseful())
{
delete actionNode;
return ACTION_RESULT_USELESS;
}
if (!action->isPossible())
{
delete actionNode;
return ACTION_RESULT_IMPOSSIBLE;
}
action->MakeVerbose();
result = ListenAndExecute(action, event);

View File

@ -277,7 +277,7 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
botName.clear();
continue;
}
return std::move(botName);
return botName;
}
// TRUE RANDOM NAME GENERATION
@ -302,11 +302,11 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
botName.clear();
continue;
}
return std::move(botName);
return botName;
}
LOG_ERROR("playerbots", "Random name generation failed.");
botName.clear();
return std::move(botName);
return botName;
}
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
@ -763,7 +763,7 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName()
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random guilds");
return std::move(guildName);
return guildName;
}
Field* fields = result->Fetch();
@ -777,13 +777,13 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName()
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random guilds");
return std::move(guildName);
return guildName;
}
fields = result->Fetch();
guildName = fields[0].Get<std::string>();
return std::move(guildName);
return guildName;
}
void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count)
@ -905,7 +905,7 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName()
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random arena teams");
return std::move(arenaTeamName);
return arenaTeamName;
}
Field* fields = result->Fetch();
@ -920,11 +920,11 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName()
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random arena teams");
return std::move(arenaTeamName);
return arenaTeamName;
}
fields = result->Fetch();
arenaTeamName = fields[0].Get<std::string>();
return std::move(arenaTeamName);
return arenaTeamName;
}

View File

@ -5192,8 +5192,9 @@ std::string const PlayerbotAI::HandleRemoteCommand(std::string const command)
{
LastMovement& data = *GetAiObjectContext()->GetValue<LastMovement&>("last movement");
std::ostringstream out;
out << data.lastMoveShort.getX() << " " << data.lastMoveShort.getY() << " " << data.lastMoveShort.getZ() << " "
<< data.lastMoveShort.getMapId() << " " << data.lastMoveShort.getO();
out << data.lastMoveShort.GetPositionX() << " " << data.lastMoveShort.GetPositionY() << " "
<< data.lastMoveShort.GetPositionZ() << " " << data.lastMoveShort.GetMapId() << " "
<< data.lastMoveShort.GetOrientation();
return out.str();
}
else if (command == "target")

View File

@ -1667,7 +1667,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
{
std::vector<uint32>::iterator i =
find(sPlayerbotAIConfig.randomBotMaps.begin(),
sPlayerbotAIConfig.randomBotMaps.end(), l.getMapId());
sPlayerbotAIConfig.randomBotMaps.end(), l.GetMapId());
return i == sPlayerbotAIConfig.randomBotMaps.end();
}),
tlocs.end());
@ -2680,7 +2680,7 @@ std::vector<uint32> RandomPlayerbotMgr::GetBgBots(uint32 bracket)
} while (result->NextRow());
}
return std::move(BgBots);
return BgBots;
}
CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event)
@ -3073,7 +3073,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction);
ReputationRank reaction = Unit::GetFactionReactionTo(player->GetFactionTemplateEntry(), factionEntry);
if (reaction > REP_NEUTRAL && dest->nearestPoint(&botPos)->m_mapId == player->GetMapId())
if (reaction > REP_NEUTRAL && dest->nearestPoint(&botPos)->GetMapId() == player->GetMapId())
{
botPos = *dest->nearestPoint(&botPos);
break;
@ -3252,7 +3252,7 @@ void RandomPlayerbotMgr::PrintStats()
if (sPlayerbotAIConfig.enableNewRpgStrategy)
{
rpgStatusCount[botAI->rpgInfo.status]++;
rpgStatusCount[botAI->rpgInfo.GetStatus()]++;
rpgStasticTotal += botAI->rpgStatistic;
botAI->rpgStatistic = NewRpgStatistic();
}

View File

@ -1700,7 +1700,7 @@ std::vector<uint32> RandomItemMgr::GetQuestIdsForItem(uint32 itemId)
}
}
return std::move(questIds);
return questIds;
}
uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, uint32 quality, uint32 itemId)
@ -1827,7 +1827,7 @@ std::vector<uint32> RandomItemMgr::GetUpgradeList(Player* player, std::string sp
{
std::vector<uint32> listItems;
if (!player)
return std::move(listItems);
return listItems;
// get old item statWeight
uint32 oldStatWeight = 0;
@ -1848,7 +1848,7 @@ std::vector<uint32> RandomItemMgr::GetUpgradeList(Player* player, std::string sp
}
if (!specId)
return std::move(listItems);
return listItems;
if (itemId && itemInfoCache.find(itemId) != itemInfoCache.end())
{
@ -1942,7 +1942,7 @@ std::vector<uint32> RandomItemMgr::GetUpgradeList(Player* player, std::string sp
LOG_INFO("playerbots", "New Items: {}, Old item:%d, New items max: {}", listItems.size(), oldStatWeight,
closestUpgradeWeight);
return std::move(listItems);
return listItems;
}
bool RandomItemMgr::HasStatWeight(uint32 itemId)

View File

@ -62,9 +62,9 @@ void FleeManager::calculatePossibleDestinations(std::vector<FleePoint*>& points)
}
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
float botPosX = startPosition.getX();
float botPosY = startPosition.getY();
float botPosZ = startPosition.getZ();
float botPosX = startPosition.GetPositionX();
float botPosY = startPosition.GetPositionY();
float botPosZ = startPosition.GetPositionZ();
FleePoint start(botAI, botPosX, botPosY, botPosZ);
calculateDistanceToCreatures(&start);

View File

@ -317,7 +317,7 @@ std::vector<TalentSpec::TalentListEntry> TalentSpec::GetTalentTree(uint32 tabpag
if (entry.tabPage() == tabpage)
retList.push_back(entry);
return std::move(retList);
return retList;
}
uint32 TalentSpec::GetTalentPoints(int32 tabpage) { return GetTalentPoints(talents, tabpage); };
@ -368,7 +368,7 @@ std::string const TalentSpec::GetTalentLink()
if (treeLink[2] != "0")
link = link + "-" + treeLink[2];
return std::move(link);
return link;
}
uint32 TalentSpec::highestTree()

View File

@ -71,13 +71,13 @@ WorldPosition::WorldPosition(std::vector<WorldPosition*> list, WorldPositionCons
set(*list[urand(0, size - 1)]);
else if (conType == WP_CENTROID)
{
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0]->getMapId(), 0, 0, 0, 0),
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0]->GetMapId(), 0, 0, 0, 0),
[size](WorldLocation i, WorldPosition* j)
{
i.m_positionX += j->getX() / size;
i.m_positionY += j->getY() / size;
i.m_positionZ += j->getZ() / size;
i.NormalizeOrientation(i.m_orientation += j->getO() / size);
i.m_positionX += j->GetPositionX() / size;
i.m_positionY += j->GetPositionY() / size;
i.m_positionZ += j->GetPositionZ() / size;
i.NormalizeOrientation(i.m_orientation += j->GetOrientation() / size);
return i;
}));
}
@ -100,13 +100,13 @@ WorldPosition::WorldPosition(std::vector<WorldPosition> list, WorldPositionConst
set(list[urand(0, size - 1)]);
else if (conType == WP_CENTROID)
{
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0].getMapId(), 0, 0, 0, 0),
set(std::accumulate(list.begin(), list.end(), WorldLocation(list[0].GetMapId(), 0, 0, 0, 0),
[size](WorldLocation i, WorldPosition& j)
{
i.m_positionX += j.getX() / size;
i.m_positionY += j.getY() / size;
i.m_positionZ += j.getZ() / size;
i.NormalizeOrientation(i.m_orientation += j.getO() / size);
i.m_positionX += j.GetPositionX() / size;
i.m_positionY += j.GetPositionY() / size;
i.m_positionZ += j.GetPositionZ() / size;
i.NormalizeOrientation(i.m_orientation += j.GetOrientation() / size);
return i;
}));
}
@ -190,16 +190,6 @@ WorldPosition& WorldPosition::operator-=(WorldPosition const& p1)
return *this;
}
uint32 WorldPosition::getMapId() { return GetMapId(); }
float WorldPosition::getX() { return GetPositionX(); }
float WorldPosition::getY() { return GetPositionY(); }
float WorldPosition::getZ() { return GetPositionZ(); }
float WorldPosition::getO() { return GetOrientation(); }
bool WorldPosition::isOverworld()
{
return GetMapId() == 0 || GetMapId() == 1 || GetMapId() == 530 || GetMapId() == 571;
@ -243,7 +233,7 @@ float WorldPosition::size()
float WorldPosition::distance(WorldPosition* center)
{
if (GetMapId() == center->getMapId())
if (GetMapId() == center->GetMapId())
return relPoint(center).size();
// this -> mapTransfer | mapTransfer -> center
@ -252,7 +242,7 @@ float WorldPosition::distance(WorldPosition* center)
float WorldPosition::fDist(WorldPosition* center)
{
if (GetMapId() == center->getMapId())
if (GetMapId() == center->GetMapId())
return sqrt(sqDistance2d(center));
// this -> mapTransfer | mapTransfer -> center
@ -328,7 +318,7 @@ WorldPosition WorldPosition::firstOutRange(std::vector<WorldPosition> list, floa
// Returns true if (on the x-y plane) the position is inside the three points.
bool WorldPosition::isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3)
{
if (getMapId() != p1->getMapId() != p2->getMapId() != p3->getMapId())
if (GetMapId() != p1->GetMapId() != p2->GetMapId() != p3->GetMapId())
return false;
float d1, d2, d3;
@ -348,7 +338,7 @@ MapEntry const* WorldPosition::getMapEntry() { return sMapStore.LookupEntry(GetM
uint32 WorldPosition::getInstanceId()
{
if (Map* map = sMapMgr->FindBaseMap(getMapId()))
if (Map* map = sMapMgr->FindBaseMap(GetMapId()))
return map->GetInstanceId();
return 0;
@ -361,7 +351,7 @@ Map* WorldPosition::getMap()
float WorldPosition::getHeight() // remove const - whipowill
{
return getMap()->GetHeight(getX(), getY(), getZ());
return getMap()->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ());
}
G3D::Vector3 WorldPosition::getVector3() { return G3D::Vector3(GetPositionX(), GetPositionY(), GetPositionZ()); }
@ -381,11 +371,11 @@ std::string const WorldPosition::print()
std::string const WorldPosition::to_string()
{
std::stringstream out;
out << m_mapId << '|';
out << m_positionX << '|';
out << m_positionY << '|';
out << m_positionZ << '|';
out << m_orientation;
out << GetMapId() << '|';
out << GetPositionX() << '|';
out << GetPositionY() << '|';
out << GetPositionZ() << '|';
out << GetOrientation();
return out.str();
}
@ -429,11 +419,14 @@ void WorldPosition::printWKT(std::vector<WorldPosition> points, std::ostringstre
WorldPosition WorldPosition::getDisplayLocation()
{
WorldPosition pos = TravelNodeMap::instance().getMapOffset(getMapId());
WorldPosition pos = TravelNodeMap::instance().getMapOffset(GetMapId());
return offset(const_cast<WorldPosition*>(&pos));
}
uint16 WorldPosition::getAreaId() { return sMapMgr->GetAreaId(PHASEMASK_NORMAL, getMapId(), getX(), getY(), getZ()); }
uint16 WorldPosition::getAreaId()
{
return sMapMgr->GetAreaId(PHASEMASK_NORMAL, GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ());
}
AreaTableEntry const* WorldPosition::getArea()
{
@ -448,7 +441,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName)
{
if (!isOverworld())
{
MapEntry const* map = sMapStore.LookupEntry(getMapId());
MapEntry const* map = sMapStore.LookupEntry(GetMapId());
if (map)
return map->name[0];
}
@ -480,7 +473,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName)
}
}
return std::move(areaName);
return areaName;
}
std::set<Transport*> WorldPosition::getTransports(uint32 entry)
@ -545,7 +538,7 @@ std::vector<WorldPosition> WorldPosition::fromGridCoord(GridCoord gridCoord)
if (d == 2 || d == 3)
g.inc_y(1);
retVec.push_back(WorldPosition(getMapId(), g));
retVec.push_back(WorldPosition(GetMapId(), g));
}
return retVec;
@ -566,7 +559,7 @@ std::vector<WorldPosition> WorldPosition::fromCellCoord(CellCoord cellcoord)
if (d == 2 || d == 3)
p.inc_y(1);
retVec.push_back(WorldPosition(getMapId(), p));
retVec.push_back(WorldPosition(GetMapId(), p));
}
return retVec;
}
@ -618,7 +611,7 @@ std::vector<WorldPosition> WorldPosition::frommGridCoord(mGridCoord GridCoord)
if (d == 2 || d == 3)
g.first++;
retVec.push_back(WorldPosition(getMapId(), g));
retVec.push_back(WorldPosition(GetMapId(), g));
}
return retVec;
@ -706,7 +699,7 @@ void WorldPosition::loadMapAndVMaps(WorldPosition secondPos)
{
for (auto& grid : getmGridCoords(secondPos))
{
loadMapAndVMap(getMapId(), grid.first, grid.second);
loadMapAndVMap(GetMapId(), grid.first, grid.second);
}
}
@ -714,7 +707,7 @@ std::vector<WorldPosition> WorldPosition::fromPointsArray(std::vector<G3D::Vecto
{
std::vector<WorldPosition> retVec;
for (auto p : path)
retVec.push_back(WorldPosition(getMapId(), p.x, p.y, p.z, getO()));
retVec.push_back(WorldPosition(GetMapId(), p.x, p.y, p.z, GetOrientation()));
return retVec;
}
@ -729,7 +722,7 @@ std::vector<WorldPosition> WorldPosition::getPathStepFrom(WorldPosition startPos
loadMapAndVMaps(startPos);
PathGenerator path(bot);
path.CalculatePath(startPos.getX(), startPos.getY(), startPos.getZ());
path.CalculatePath(startPos.GetPositionX(), startPos.GetPositionY(), startPos.GetPositionZ());
Movement::PointsArray points = path.GetPath();
PathType type = path.GetPathType();
@ -785,7 +778,7 @@ std::vector<WorldPosition> WorldPosition::getPathFromPath(std::vector<WorldPosit
WorldPosition currentPos = startPath.back();
// No pathfinding across maps.
if (getMapId() != currentPos.getMapId())
if (GetMapId() != currentPos.GetMapId())
return {};
std::vector<WorldPosition> subPath, fullPath = startPath;
@ -818,10 +811,18 @@ bool WorldPosition::GetReachableRandomPointOnGround(Player* bot, float radius, b
{
radius *= randomRange ? rand_norm() : 1.f;
float angle = rand_norm() * static_cast<float>(2 * M_PI);
m_positionX += radius * cosf(angle);
m_positionY += radius * sinf(angle);
setX(GetPositionX() + radius * cosf(angle));
setY(GetPositionY() + radius * sinf(angle));
return getMap()->CanReachPositionAndGetValidCoords(bot, m_positionX, m_positionY, m_positionZ);
float x = GetPositionX();
float y = GetPositionY();
float z = GetPositionZ();
bool canReach = getMap()->CanReachPositionAndGetValidCoords(bot, x, y, z);
setX(x);
setY(y);
setZ(z);
return canReach;
}
uint32 WorldPosition::getUnitsAggro(GuidVector& units, Player* bot)
@ -844,7 +845,7 @@ uint32 WorldPosition::getUnitsAggro(GuidVector& units, Player* bot)
void FindPointCreatureData::operator()(CreatureData const& creatureData)
{
if (!entry || creatureData.id1 == entry)
if ((!point || creatureData.mapid == point.getMapId()) &&
if ((!point || creatureData.mapid == point.GetMapId()) &&
(!radius || point.sqDistance(WorldPosition(creatureData.mapid, creatureData.posX, creatureData.posY,
creatureData.posZ)) < radius * radius))
{
@ -855,7 +856,7 @@ void FindPointCreatureData::operator()(CreatureData const& creatureData)
void FindPointGameObjectData::operator()(GameObjectData const& gameobjectData)
{
if (!entry || gameobjectData.id == entry)
if ((!point || gameobjectData.mapid == point.getMapId()) &&
if ((!point || gameobjectData.mapid == point.GetMapId()) &&
(!radius || point.sqDistance(WorldPosition(gameobjectData.mapid, gameobjectData.posX, gameobjectData.posY,
gameobjectData.posZ)) < radius * radius))
{
@ -3223,7 +3224,8 @@ void TravelMgr::LoadQuestTravelTable()
if (loc.second.empty())
continue;
if (!TravelNodeMap::instance().getMapOffset(loc.second.front().getMapId()) && loc.second.front().getMapId() != 0)
if (!TravelNodeMap::instance().getMapOffset(loc.second.front().GetMapId()) &&
loc.second.front().GetMapId() != 0)
continue;
std::vector<WorldPosition> points = loc.second;
@ -3235,7 +3237,7 @@ void TravelMgr::LoadQuestTravelTable()
out << "\"center\""
<< ",";
out << points.begin()->getMapId() << ",";
out << points.begin()->GetMapId() << ",";
out << points.begin()->getAreaName() << ",";
out << points.begin()->getAreaName(true, true) << ",";
@ -3245,7 +3247,7 @@ void TravelMgr::LoadQuestTravelTable()
out << "\"area\""
<< ",";
out << points.begin()->getMapId() << ",";
out << points.begin()->GetMapId() << ",";
out << points.begin()->getAreaName() << ",";
out << points.begin()->getAreaName(true, true) << ",";
@ -3613,17 +3615,18 @@ void TravelMgr::LoadQuestTravelTable()
if (!pos->getMap())
continue;
float nx = pos->getX() + (x*5)-5000.0f;
float ny = pos->getY() + (y*5)-5000.0f;
float nz = pos->getZ() + 100.0f;
float nx = pos->GetPositionX() + (x * 5) - 5000.0f;
float ny = pos->GetPositionY() + (y * 5) - 5000.0f;
float nz = pos->GetPositionZ() + 100.0f;
//pos->getMap()->GetHitPosition(nx, ny, nz + 200.0f, nx, ny, nz, -0.5f);
if (!pos->getMap()->GetHeightInRange(nx, ny, nz, 5000.0f)) // GetHeight can fail
continue;
WorldPosition npos = WorldPosition(pos->getMapId(), nx, ny, nz, 0.0);
uint32 area = path.getArea(npos.getMapId(), npos.getX(), npos.getY(), npos.getZ());
WorldPosition npos = WorldPosition(pos->GetMapId(), nx, ny, nz, 0.0);
uint32 area = path.getArea(npos.GetMapId(), npos.GetPositionX(), npos.GetPositionY(),
npos.GetPositionZ());
std::ostringstream out;
out << std::fixed << area << "," << npos.getDisplayX() << "," << npos.getDisplayY();
@ -3647,7 +3650,8 @@ void TravelMgr::LoadQuestTravelTable()
std::string const name = i.second->getTitle();
name.erase(remove(name.begin(), name.end(), '\"'), name.end());
out << std::fixed << std::setprecision(2) << name.c_str() << "," << i.first << "," << j->getDisplayX() <<
"," << j->getDisplayY() << "," << j->getX() << "," << j->getY() << "," << j->getZ(); sPlayerbotAIConfig.log(5,
"," << j->getDisplayY() << "," << j->GetPositionX() << "," << j->GetPositionY() << "," << j->GetPositionZ();
sPlayerbotAIConfig.log(5,
out.str().c_str());
}
}
@ -4022,7 +4026,7 @@ std::vector<TravelDestination*> TravelMgr::getRpgTravelDestinations(Player* bot,
retTravelLocations.push_back(dest);
}
return std::move(retTravelLocations);
return retTravelLocations;
}
std::vector<TravelDestination*> TravelMgr::getExploreTravelDestinations(Player* bot, bool ignoreFull,
@ -4087,8 +4091,8 @@ void TravelMgr::setNullTravelTarget(Player* player)
void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance, bool makeShortcuts)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
uint32 sMap = start.GetMapId();
uint32 eMap = end.GetMapId();
if (sMap == eMap)
return;
@ -4121,7 +4125,7 @@ void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float por
}
// Add actual transfer.
auto mapTransfers = mapTransfersMap.find(std::make_pair(start.getMapId(), end.getMapId()));
auto mapTransfers = mapTransfersMap.find(std::make_pair(start.GetMapId(), end.GetMapId()));
if (mapTransfers == mapTransfersMap.end())
mapTransfersMap.insert({{sMap, eMap}, {mapTransfer(start, end, portalDistance)}});
@ -4142,8 +4146,8 @@ void TravelMgr::loadMapTransfers()
float TravelMgr::mapTransDistance(WorldPosition start, WorldPosition end)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
uint32 sMap = start.GetMapId();
uint32 eMap = end.GetMapId();
if (sMap == eMap)
return start.distance(end);
@ -4167,8 +4171,8 @@ float TravelMgr::mapTransDistance(WorldPosition start, WorldPosition end)
float TravelMgr::fastMapTransDistance(WorldPosition start, WorldPosition end)
{
uint32 sMap = start.getMapId();
uint32 eMap = end.getMapId();
uint32 sMap = start.GetMapId();
uint32 eMap = end.GetMapId();
if (sMap == eMap)
return start.fDist(end);

View File

@ -120,12 +120,6 @@ public:
WorldPosition& operator+=(WorldPosition const& p1);
WorldPosition& operator-=(WorldPosition const& p1);
uint32 getMapId();
float getX();
float getY();
float getZ();
float getO();
G3D::Vector3 getVector3();
std::string const print();
@ -185,29 +179,29 @@ public:
// Quick square distance in 2d plane.
float sqDistance2d(WorldPosition center)
{
return (getX() - center.getX()) * (getX() - center.getX()) +
(getY() - center.getY()) * (getY() - center.getY());
return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) +
(GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY());
}
// Quick square distance calculation without map check. Used for getting the minimum distant points.
float sqDistance(WorldPosition center)
{
return (getX() - center.getX()) * (getX() - center.getX()) +
(getY() - center.getY()) * (getY() - center.getY()) +
(getZ() - center.getZ()) * (getZ() - center.getZ());
return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) +
(GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY()) +
(GetPositionZ() - center.GetPositionZ()) * (GetPositionZ() - center.GetPositionZ());
}
float sqDistance2d(WorldPosition* center)
{
return (getX() - center->getX()) * (getX() - center->getX()) +
(getY() - center->getY()) * (getY() - center->getY());
return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) +
(GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY());
}
float sqDistance(WorldPosition* center)
{
return (getX() - center->getX()) * (getX() - center->getX()) +
(getY() - center->getY()) * (getY() - center->getY()) +
(getZ() - center->getZ()) * (getZ() - center->getZ());
return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) +
(GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY()) +
(GetPositionZ() - center->GetPositionZ()) * (GetPositionZ() - center->GetPositionZ());
}
// Returns the closest point of the list. Fast but only works for the same map.
@ -227,7 +221,7 @@ public:
float getAngleTo(WorldPosition endPos)
{
float ang = atan2(endPos.getY() - getY(), endPos.getX() - getX());
float ang = atan2(endPos.GetPositionY() - GetPositionY(), endPos.GetPositionX() - GetPositionX());
return (ang >= 0) ? ang : 2 * static_cast<float>(M_PI) + ang;
}
@ -238,7 +232,8 @@ public:
float mSign(WorldPosition* p1, WorldPosition* p2)
{
return (getX() - p2->getX()) * (p1->getY() - p2->getY()) - (p1->getX() - p2->getX()) * (getY() - p2->getY());
return (GetPositionX() - p2->GetPositionX()) * (p1->GetPositionY() - p2->GetPositionY()) -
(p1->GetPositionX() - p2->GetPositionX()) * (GetPositionY() - p2->GetPositionY());
}
bool isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3);
@ -251,18 +246,23 @@ public:
std::set<Transport*> getTransports(uint32 entry = 0);
GridCoord getGridCoord() { return Acore::ComputeGridCoord(getX(), getY()); };
CellCoord getCellCoord() { return Acore::ComputeCellCoord(GetPositionX(), GetPositionY()); }
GridCoord getGridCoord()
{
CellCoord cellCoord = getCellCoord();
Cell cell(cellCoord);
return GridCoord(cell.GridX(), cell.GridY());
}
std::vector<GridCoord> getGridCoord(WorldPosition secondPos);
std::vector<WorldPosition> fromGridCoord(GridCoord GridCoord);
CellCoord getCellCoord() { return Acore::ComputeCellCoord(getX(), getY()); }
std::vector<WorldPosition> fromCellCoord(CellCoord cellCoord);
std::vector<WorldPosition> gridFromCellCoord(CellCoord cellCoord);
mGridCoord getmGridCoord()
{
return std::make_pair((int32)(CENTER_GRID_ID - getX() / SIZE_OF_GRIDS),
(int32)(CENTER_GRID_ID - getY() / SIZE_OF_GRIDS));
return std::make_pair((int32)(CENTER_GRID_ID - GetPositionX() / SIZE_OF_GRIDS),
(int32)(CENTER_GRID_ID - GetPositionY() / SIZE_OF_GRIDS));
}
std::vector<mGridCoord> getmGridCoords(WorldPosition secondPos);
@ -270,15 +270,15 @@ public:
void loadMapAndVMap(uint32 mapId, uint8 x, uint8 y);
void loadMapAndVMap() { loadMapAndVMap(getMapId(), getmGridCoord().first, getmGridCoord().second); }
void loadMapAndVMap() { loadMapAndVMap(GetMapId(), getmGridCoord().first, getmGridCoord().second); }
void loadMapAndVMaps(WorldPosition secondPos);
// Display functions
WorldPosition getDisplayLocation();
float getDisplayX() { return getDisplayLocation().getY() * -1.0; }
float getDisplayX() { return getDisplayLocation().GetPositionY() * -1.0; }
float getDisplayY() { return getDisplayLocation().getX(); }
float getDisplayY() { return getDisplayLocation().GetPositionX(); }
uint16 getAreaId();
AreaTableEntry const* getArea();
@ -334,11 +334,11 @@ private:
inline ByteBuffer& operator<<(ByteBuffer& b, WorldPosition& guidP)
{
b << guidP.getMapId();
b << guidP.getX();
b << guidP.getY();
b << guidP.getZ();
b << guidP.getO();
b << guidP.GetMapId();
b << guidP.GetPositionX();
b << guidP.GetPositionY();
b << guidP.GetPositionZ();
b << guidP.GetOrientation();
b << guidP.getVisitors();
return b;
}
@ -440,7 +440,7 @@ std::vector<std::pair<T, WorldPosition>> GetPosList(std::vector<T> oList)
for (auto& obj : oList)
retList.push_back(std::make_pair(obj, WorldPosition(obj)));
return std::move(retList);
return retList;
};
template <class T>
@ -450,7 +450,7 @@ std::vector<std::pair<T, WorldPosition>> GetPosVector(std::vector<T> oList)
for (auto& obj : oList)
retList.push_back(make_pair(obj, WorldPosition(obj)));
return std::move(retList);
return retList;
};
class mapTransfer
@ -461,9 +461,9 @@ public:
{
}
bool isFrom(WorldPosition point) { return point.getMapId() == pointFrom.getMapId(); }
bool isFrom(WorldPosition point) { return point.GetMapId() == pointFrom.GetMapId(); }
bool isTo(WorldPosition point) { return point.getMapId() == pointTo.getMapId(); }
bool isTo(WorldPosition point) { return point.GetMapId() == pointTo.GetMapId(); }
WorldPosition* getPointFrom() { return &pointFrom; }
@ -543,7 +543,7 @@ public:
WorldPosition* nearestPoint(WorldPosition* pos);
float distanceTo(WorldPosition* pos) { return nearestPoint(pos)->distance(pos); }
bool onMap(WorldPosition* pos) { return nearestPoint(pos)->getMapId() == pos->getMapId(); }
bool onMap(WorldPosition* pos) { return nearestPoint(pos)->GetMapId() == pos->GetMapId(); }
virtual bool isIn(WorldPosition* pos, float radius = 0.f)
{
return onMap(pos) && distanceTo(pos) <= (radius ? radius : radiusMin);

View File

@ -81,7 +81,7 @@ void TravelNodePath::calculateCost(bool distanceOnly)
}
}
if (lastPoint && point.getMapId() == lastPoint.getMapId())
if (lastPoint && point.GetMapId() == lastPoint.GetMapId())
{
if (!distanceOnly && (point.isInWater() || lastPoint.isInWater()))
swimDistance += point.distance(lastPoint);
@ -364,7 +364,7 @@ std::vector<TravelNode*> TravelNode::getNodeMap(bool importantOnly, std::vector<
}
}
return std::move(closeList);
return closeList;
}
bool TravelNode::isUselessLink(TravelNode* farNode)
@ -687,7 +687,7 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist)
// if (p.point.getMapId() != startPos.getMapId())
// continue;
if (p.point.getMapId() == startPos.getMapId())
if (p.point.GetMapId() == startPos.GetMapId())
{
float curDist = p.point.sqDistance(startPos);
@ -722,7 +722,7 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist)
newPath.push_back(p);
}
if (newPath.empty() || minDist > maxDistSq || newPath.front().point.getMapId() != startPos.getMapId())
if (newPath.empty() || minDist > maxDistSq || newPath.front().point.GetMapId() != startPos.GetMapId())
{
clear();
return false;
@ -800,7 +800,7 @@ bool TravelPath::shouldMoveToNextPoint(WorldPosition startPos, std::vector<PathN
float nextMove = p->point.distance(nextP->point);
if (p->point.getMapId() != startPos.getMapId() ||
if (p->point.GetMapId() != startPos.GetMapId() ||
((moveDist + nextMove > maxDist || startPos.distance(nextP->point) > maxDist) && moveDist > 0))
{
return false;
@ -827,7 +827,7 @@ WorldPosition TravelPath::getNextPoint(WorldPosition startPos, float maxDist, Tr
// Get the closest point on the path to start from.
for (auto p = startP; p != ed; p++)
{
if (p->point.getMapId() != startPos.getMapId())
if (p->point.GetMapId() != startPos.GetMapId())
continue;
float curDist = p->point.distance(startPos);
@ -1126,7 +1126,7 @@ std::vector<TravelNode*> TravelNodeMap::getNodes(WorldPosition pos, float range)
for (auto& node : m_nodes)
{
if (node->getMapId() == pos.getMapId())
if (node->getMapId() == pos.GetMapId())
if (range == -1 || node->getDistance(pos) <= range)
retVec.push_back(node);
}
@ -1135,7 +1135,7 @@ std::vector<TravelNode*> TravelNodeMap::getNodes(WorldPosition pos, float range)
[pos](TravelNode* i, TravelNode* j)
{ return i->getPosition()->distance(pos) < j->getPosition()->distance(pos); });
return std::move(retVec);
return retVec;
}
TravelNode* TravelNodeMap::getNode(WorldPosition pos, [[maybe_unused]] std::vector<WorldPosition>& ppath, Unit* bot,
@ -1787,8 +1787,9 @@ void TravelNodeMap::generateTransportNodes()
float dy = -1 * p.second->Y;
WorldPosition pos =
WorldPosition(basePos.getMapId(), basePos.getX() + dx, basePos.getY() + dy,
basePos.getZ() + p.second->Z, basePos.getO());
WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx,
basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z,
basePos.GetOrientation());
if (prevNode)
{
@ -1830,8 +1831,9 @@ void TravelNodeMap::generateTransportNodes()
float dx = -1 * p.second->X;
float dy = -1 * p.second->Y;
WorldPosition pos =
WorldPosition(basePos.getMapId(), basePos.getX() + dx, basePos.getY() + dy,
basePos.getZ() + p.second->Z, basePos.getO());
WorldPosition(basePos.GetMapId(), basePos.GetPositionX() + dx,
basePos.GetPositionY() + dy, basePos.GetPositionZ() + p.second->Z,
basePos.GetOrientation());
ppath.push_back(pos);
@ -2356,10 +2358,10 @@ void TravelNodeMap::saveNodeStore()
stmt->SetData(0, i);
stmt->SetData(1, saveNodes.find(link.first)->second);
stmt->SetData(2, j);
stmt->SetData(3, point.getMapId());
stmt->SetData(4, point.getX());
stmt->SetData(5, point.getY());
stmt->SetData(6, point.getZ());
stmt->SetData(3, point.GetMapId());
stmt->SetData(4, point.GetPositionX());
stmt->SetData(5, point.GetPositionY());
stmt->SetData(6, point.GetPositionZ());
trans->Append(stmt);
points++;
@ -2516,10 +2518,10 @@ void TravelNodeMap::calcMapOffset()
}
else
{
min.back().setX(std::min(min.back().getX(), node->getX()));
min.back().setY(std::min(min.back().getY(), node->getY()));
max.back().setX(std::max(max.back().getX(), node->getX()));
max.back().setY(std::max(max.back().getY(), node->getY()));
min.back().setX(std::min(min.back().GetPositionX(), node->getX()));
min.back().setY(std::min(min.back().GetPositionY(), node->getY()));
max.back().setX(std::max(max.back().GetPositionX(), node->getX()));
max.back().setY(std::max(max.back().GetPositionY(), node->getY()));
}
}
}
@ -2533,14 +2535,15 @@ void TravelNodeMap::calcMapOffset()
for (auto& mapId : mapIds)
{
mapOffsets.push_back(std::make_pair(
mapId, WorldPosition(mapId, curPos.getX() - min[i].getX(), curPos.getY() - max[i].getY(), 0, 0)));
mapId, WorldPosition(mapId, curPos.GetPositionX() - min[i].GetPositionX(),
curPos.GetPositionY() - max[i].GetPositionY(), 0, 0)));
maxY = std::max(maxY, (max[i].getY() - min[i].getY() + 500));
curPos.setX(curPos.getX() + (max[i].getX() - min[i].getX() + 500));
maxY = std::max(maxY, (max[i].GetPositionY() - min[i].GetPositionY() + 500));
curPos.setX(curPos.GetPositionX() + (max[i].GetPositionX() - min[i].GetPositionX() + 500));
if (curPos.getX() > endPos.getX())
if (curPos.GetPositionX() > endPos.GetPositionX())
{
curPos.setY(curPos.getY() - maxY);
curPos.setY(curPos.GetPositionY() - maxY);
curPos.setX(-13000);
}

View File

@ -251,11 +251,11 @@ public:
}
// WorldLocation shortcuts
uint32 getMapId() { return point.getMapId(); }
float getX() { return point.getX(); }
float getY() { return point.getY(); }
float getZ() { return point.getZ(); }
float getO() { return point.getO(); }
uint32 getMapId() { return point.GetMapId(); }
float getX() { return point.GetPositionX(); }
float getY() { return point.GetPositionY(); }
float getZ() { return point.GetPositionZ(); }
float getO() { return point.GetOrientation(); }
float getDistance(WorldPosition pos) { return point.distance(pos); }
float getDistance(TravelNode* node) { return point.distance(node->getPosition()); }
float fDist(TravelNode* node) { return point.fDist(node->getPosition()); }

View File

@ -42,21 +42,20 @@ enum class HealingManaEfficiency : uint8
enum NewRpgStatus : int
{
RPG_STATUS_START = 0,
// Going to far away place
RPG_GO_GRIND = 0,
RPG_GO_CAMP = 1,
//Initial Status
RPG_IDLE = 0,
RPG_GO_GRIND = 1,
RPG_GO_CAMP = 2,
// Exploring nearby
RPG_WANDER_RANDOM = 2,
RPG_WANDER_NPC = 3,
RPG_WANDER_RANDOM = 3,
RPG_WANDER_NPC = 4,
// Do Quest (based on quest status)
RPG_DO_QUEST = 4,
RPG_DO_QUEST = 5,
// Travel
RPG_TRAVEL_FLIGHT = 5,
RPG_TRAVEL_FLIGHT = 6,
// Taking a break
RPG_REST = 6,
// Initial status
RPG_IDLE = 7,
RPG_REST = 7,
RPG_STATUS_END = 8
};