mod-playerbots/src/Ai/World/Rpg/Action/RpgSubActions.h
Keleborn 957eca0263
Feat. Enable multi node flying, and refactor into travel manager (#2156)
# Pull Request

Feature - Enable multi node flying for bots
- Bots currently only do node to node flying. This PR makes it so they
can connect multiple noted.
-- This is enabled by sending a vector containing the node sequence
instead of a single destination node
-- To minimize the run-time cost of searching for available nodes and
connection, a cache of all possible connections is prepared at start up
using a BFS search algorithm.

Refactor 
- Move all world destination logic (cities, banks, inns) to existing
Travel manager
- Eliminate flightmastercache and integrate to new manager
- replace SQLs calls with in-memory data search by core
- Add in new map that stores creature areas by template. 

Clean up
- Move other rpg files to related folder. 
(Next steps) The selection for where bots fly to should be smarter than
it is. Instead of trying to determine where a bot can go, it should
first decide where it should go, and then identify the correct way to
get there.
---

## 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**)
The call itself is fairly infrequent, and although now there are a
greater number of paths available for the bots, I dont think it would be
significant.

## Defaults & Configuration

Does this change modify default bot behavior?
- - [ ] No
- - [x] 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?
- - [ ] No
- - [x] Yes (**explain below**)
Gemini first suggested the use of a BFS algorithm. This was rewritten by
me to actually work as intended. Verification by additional logging not
present in final code.

Claude code converted the SQL filtering to the atrocious if statements
found in PrepareDestinationCache, but after verifying them it works. If
there are better ways to do this Im open to it.

---

## 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-03-20 20:39:53 +01:00

269 lines
6.8 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RPGSUBACTIONS_H
#define _PLAYERBOT_RPGSUBACTIONS_H
#include "Action.h"
#include "AiObject.h"
#include "Item.h"
class GuidPosition;
class ObjectGuid;
class PlayerbotAI;
class RpgHelper : public AiObject
{
public:
RpgHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
virtual ~RpgHelper() = default;
void OnExecute(std::string nextAction = "rpg");
void BeforeExecute();
void AfterExecute(bool doDelay = true, bool waitForGroup = false);
virtual GuidPosition guidP();
virtual ObjectGuid guid();
virtual bool InRange();
private:
void setFacingTo(GuidPosition guidPosition);
void setFacing(GuidPosition guidPosition);
void setDelay(bool waitForGroup);
};
class RpgEnabled
{
public:
RpgEnabled(PlayerbotAI* botAI) { rpg = std::make_unique<RpgHelper>(botAI); }
protected:
std::unique_ptr<RpgHelper> rpg;
};
class RpgSubAction : public Action, public RpgEnabled
{
public:
RpgSubAction(PlayerbotAI* botAI, std::string const name = "rpg sub") : Action(botAI, name), RpgEnabled(botAI) {}
// Long range is possible?
bool isPossible() override;
// Short range can we do the action now?
bool isUseful() override;
bool Execute(Event event) override;
protected:
virtual std::string const ActionName();
virtual Event ActionEvent(Event event);
};
class RpgStayAction : public RpgSubAction
{
public:
RpgStayAction(PlayerbotAI* botAI, std::string const name = "rpg stay") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
class RpgWorkAction : public RpgSubAction
{
public:
RpgWorkAction(PlayerbotAI* botAI, std::string const name = "rpg work") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
class RpgEmoteAction : public RpgSubAction
{
public:
RpgEmoteAction(PlayerbotAI* botAI, std::string const name = "rpg emote") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
class RpgCancelAction : public RpgSubAction
{
public:
RpgCancelAction(PlayerbotAI* botAI, std::string const name = "rpg cancel") : RpgSubAction(botAI, name) {}
bool Execute(Event event) override;
};
class RpgTaxiAction : public RpgSubAction
{
public:
RpgTaxiAction(PlayerbotAI* botAI, std::string const name = "rpg taxi") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
class RpgDiscoverAction : public RpgTaxiAction
{
public:
RpgDiscoverAction(PlayerbotAI* botAI, std::string const name = "rpg discover") : RpgTaxiAction(botAI, name) {}
bool Execute(Event event) override;
};
class RpgStartQuestAction : public RpgSubAction
{
public:
RpgStartQuestAction(PlayerbotAI* botAI, std::string const name = "rpg start quest") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgEndQuestAction : public RpgSubAction
{
public:
RpgEndQuestAction(PlayerbotAI* botAI, std::string const name = "rpg end quest") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgBuyAction : public RpgSubAction
{
public:
RpgBuyAction(PlayerbotAI* botAI, std::string const name = "rpg buy") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgSellAction : public RpgSubAction
{
public:
RpgSellAction(PlayerbotAI* botAI, std::string const name = "rpg sell") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgRepairAction : public RpgSubAction
{
public:
RpgRepairAction(PlayerbotAI* botAI, std::string const name = "rpg repair") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
};
class RpgTrainAction : public RpgSubAction
{
public:
RpgTrainAction(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgSubAction(botAI, name) {}
bool isPossible() override;
bool isUseful() override;
private:
std::string const ActionName() override;
};
class RpgHealAction : public RpgSubAction
{
public:
RpgHealAction(PlayerbotAI* botAI, std::string const name = "rpg heal") : RpgSubAction(botAI, name) {}
bool Execute(Event event) override;
};
class RpgHomeBindAction : public RpgSubAction
{
public:
RpgHomeBindAction(PlayerbotAI* botAI, std::string const name = "rpg home bind") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
};
class RpgQueueBgAction : public RpgSubAction
{
public:
RpgQueueBgAction(PlayerbotAI* botAI, std::string const name = "rpg queue bg") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
};
class RpgBuyPetitionAction : public RpgSubAction
{
public:
RpgBuyPetitionAction(PlayerbotAI* botAI, std::string const name = "rpg buy petition") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
};
class RpgUseAction : public RpgSubAction
{
public:
RpgUseAction(PlayerbotAI* botAI, std::string const name = "rpg use") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgSpellAction : public RpgSubAction
{
public:
RpgSpellAction(PlayerbotAI* botAI, std::string const name = "rpg spell") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgCraftAction : public RpgSubAction
{
public:
RpgCraftAction(PlayerbotAI* botAI, std::string const name = "rpg craft") : RpgSubAction(botAI, name) {}
private:
std::string const ActionName() override;
Event ActionEvent(Event event) override;
};
class RpgTradeUsefulAction : public RpgSubAction
{
public:
RpgTradeUsefulAction(PlayerbotAI* botAI, std::string const name = "rpg trade useful") : RpgSubAction(botAI, name) {}
std::vector<Item*> CanGiveItems(GuidPosition guidPosition);
bool Execute(Event event) override;
};
class RpgDuelAction : public RpgSubAction
{
public:
RpgDuelAction(PlayerbotAI* botAI, std::string const name = "rpg duel") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
class RpgMountAnimAction : public RpgSubAction
{
public:
RpgMountAnimAction(PlayerbotAI* botAI, std::string const name = "rpg mount anim") : RpgSubAction(botAI, name) {}
bool isUseful() override;
bool Execute(Event event) override;
};
#endif