mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-12-01 21:12:50 +08:00
[Combat formation] Combat formation set up
This commit is contained in:
@@ -274,6 +274,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
{
|
{
|
||||||
engine->addStrategy("avoid aoe");
|
engine->addStrategy("avoid aoe");
|
||||||
}
|
}
|
||||||
|
engine->addStrategy("combat formation");
|
||||||
switch (player->getClass())
|
switch (player->getClass())
|
||||||
{
|
{
|
||||||
case CLASS_PRIEST:
|
case CLASS_PRIEST:
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
|
|||||||
creators["grind"] = &StrategyContext::grind;
|
creators["grind"] = &StrategyContext::grind;
|
||||||
creators["avoid aoe"] = &StrategyContext::avoid_aoe;
|
creators["avoid aoe"] = &StrategyContext::avoid_aoe;
|
||||||
creators["move random"] = &StrategyContext::move_random;
|
creators["move random"] = &StrategyContext::move_random;
|
||||||
|
creators["combat formation"] = &StrategyContext::combat_formation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -174,6 +175,7 @@ class StrategyContext : public NamedObjectContext<Strategy>
|
|||||||
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
|
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
|
||||||
static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); }
|
static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); }
|
||||||
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
|
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
|
||||||
|
static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||||
|
|||||||
@@ -326,4 +326,10 @@ class UnitManualSetValue : public ManualSetValue<Unit*>
|
|||||||
Unit* Get() override;
|
Unit* Get() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DisperseDistanceValue : public ManualSetValue<float>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse value") :
|
||||||
|
ManualSetValue<float>(botAI, defaultValue, name) { }
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ class ActionContext : public NamedObjectContext<Action>
|
|||||||
creators["flee"] = &ActionContext::flee;
|
creators["flee"] = &ActionContext::flee;
|
||||||
creators["flee with pet"] = &ActionContext::flee_with_pet;
|
creators["flee with pet"] = &ActionContext::flee_with_pet;
|
||||||
creators["avoid aoe"] = &ActionContext::avoid_aoe;
|
creators["avoid aoe"] = &ActionContext::avoid_aoe;
|
||||||
|
creators["combat formation move"] = &ActionContext::combat_formation_move;
|
||||||
|
creators["disperse set"] = &ActionContext::disperse_set;
|
||||||
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
|
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
|
||||||
creators["shoot"] = &ActionContext::shoot;
|
creators["shoot"] = &ActionContext::shoot;
|
||||||
creators["lifeblood"] = &ActionContext::lifeblood;
|
creators["lifeblood"] = &ActionContext::lifeblood;
|
||||||
@@ -265,6 +267,8 @@ class ActionContext : public NamedObjectContext<Action>
|
|||||||
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
|
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
|
||||||
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
|
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
|
||||||
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
|
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
|
||||||
|
static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); }
|
||||||
|
static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); }
|
||||||
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
||||||
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
||||||
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PathGenerator.h"
|
#include "PathGenerator.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "Position.h"
|
||||||
#include "Random.h"
|
#include "Random.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "SpellAuraEffects.h"
|
#include "SpellAuraEffects.h"
|
||||||
@@ -25,10 +26,13 @@
|
|||||||
#include "LootObjectStack.h"
|
#include "LootObjectStack.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
|
#include "Timer.h"
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include "WaypointMovementGenerator.h"
|
#include "WaypointMovementGenerator.h"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name)
|
MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name)
|
||||||
{
|
{
|
||||||
@@ -1495,6 +1499,9 @@ bool FleeWithPetAction::Execute(Event event)
|
|||||||
|
|
||||||
bool AvoidAoeAction::isUseful()
|
bool AvoidAoeAction::isUseful()
|
||||||
{
|
{
|
||||||
|
if (getMSTime() - moveInterval < lastMoveTimer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage");
|
GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage");
|
||||||
GuidVector triggers = AI_VALUE(GuidVector, "possible triggers");
|
GuidVector triggers = AI_VALUE(GuidVector, "possible triggers");
|
||||||
return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty();
|
return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty();
|
||||||
@@ -1504,14 +1511,17 @@ bool AvoidAoeAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
// Case #1: Aura with dynamic object (e.g. rain of fire)
|
// Case #1: Aura with dynamic object (e.g. rain of fire)
|
||||||
if (AvoidAuraWithDynamicObj()) {
|
if (AvoidAuraWithDynamicObj()) {
|
||||||
|
lastMoveTimer = getMSTime();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Case #2: Trap game object with spell (e.g. lava bomb)
|
// Case #2: Trap game object with spell (e.g. lava bomb)
|
||||||
if (AvoidGameObjectWithDamage()) {
|
if (AvoidGameObjectWithDamage()) {
|
||||||
|
lastMoveTimer = getMSTime();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Case #3: Trigger npc (e.g. Lesser shadow fissure)
|
// Case #3: Trigger npc (e.g. Lesser shadow fissure)
|
||||||
if (AvoidUnitWithDamageAura()) {
|
if (AvoidUnitWithDamageAura()) {
|
||||||
|
lastMoveTimer = getMSTime();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1541,7 +1551,13 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
|||||||
}
|
}
|
||||||
std::ostringstream name;
|
std::ostringstream name;
|
||||||
name << spellInfo->SpellName[0]; // << "] (aura)";
|
name << spellInfo->SpellName[0]; // << "] (aura)";
|
||||||
if (FleePosition(dynOwner->GetPosition(), radius, name.str())) {
|
if (FleePosition(dynOwner->GetPosition(), radius)) {
|
||||||
|
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||||
|
lastTellTimer = time(NULL);
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "I'm avoiding " << name.str() << "...";
|
||||||
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1592,7 +1608,13 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
|
|||||||
}
|
}
|
||||||
std::ostringstream name;
|
std::ostringstream name;
|
||||||
name << spellInfo->SpellName[0]; // << "] (object)";
|
name << spellInfo->SpellName[0]; // << "] (object)";
|
||||||
if (FleePosition(go->GetPosition(), radius, name.str())) {
|
if (FleePosition(go->GetPosition(), radius)) {
|
||||||
|
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||||
|
lastTellTimer = time(NULL);
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "I'm avoiding " << name.str() << "...";
|
||||||
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1634,8 +1656,13 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
|||||||
}
|
}
|
||||||
std::ostringstream name;
|
std::ostringstream name;
|
||||||
name << triggerSpellInfo->SpellName[0]; //<< "] (unit)";
|
name << triggerSpellInfo->SpellName[0]; //<< "] (unit)";
|
||||||
if (FleePosition(unit->GetPosition(), radius, name.str())) {
|
if (FleePosition(unit->GetPosition(), radius)) {
|
||||||
return true;
|
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
||||||
|
lastTellTimer = time(NULL);
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "I'm avoiding " << name.str() << "...";
|
||||||
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1645,7 +1672,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
|
||||||
{
|
{
|
||||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||||
std::vector<CheckAngle> possibleAngles;
|
std::vector<CheckAngle> possibleAngles;
|
||||||
@@ -1671,7 +1698,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
|||||||
for (CheckAngle &checkAngle : possibleAngles) {
|
for (CheckAngle &checkAngle : possibleAngles) {
|
||||||
float angle = checkAngle.angle;
|
float angle = checkAngle.angle;
|
||||||
bool strict = checkAngle.strict;
|
bool strict = checkAngle.strict;
|
||||||
float fleeDis = sPlayerbotAIConfig->fleeDistance;
|
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
|
||||||
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
||||||
bot->GetPositionY() + sin(angle) * fleeDis,
|
bot->GetPositionY() + sin(angle) * fleeDis,
|
||||||
bot->GetPositionZ()};
|
bot->GetPositionZ()};
|
||||||
@@ -1690,7 +1717,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
|
|||||||
return Position();
|
return Position();
|
||||||
}
|
}
|
||||||
|
|
||||||
Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius)
|
||||||
{
|
{
|
||||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||||
std::vector<CheckAngle> possibleAngles;
|
std::vector<CheckAngle> possibleAngles;
|
||||||
@@ -1714,7 +1741,7 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
|||||||
for (CheckAngle &checkAngle : possibleAngles) {
|
for (CheckAngle &checkAngle : possibleAngles) {
|
||||||
float angle = checkAngle.angle;
|
float angle = checkAngle.angle;
|
||||||
bool strict = checkAngle.strict;
|
bool strict = checkAngle.strict;
|
||||||
float fleeDis = sPlayerbotAIConfig->fleeDistance;
|
float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance);
|
||||||
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
||||||
bot->GetPositionY() + sin(angle) * fleeDis,
|
bot->GetPositionY() + sin(angle) * fleeDis,
|
||||||
bot->GetPositionZ()};
|
bot->GetPositionZ()};
|
||||||
@@ -1737,28 +1764,162 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
|
|||||||
return Position();
|
return Position();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name)
|
bool MovementAction::FleePosition(Position pos, float radius)
|
||||||
{
|
{
|
||||||
Position bestPos;
|
Position bestPos;
|
||||||
if (botAI->IsMelee(bot)) {
|
if (botAI->IsMelee(bot)) {
|
||||||
bestPos = BestPositionForMelee(pos, radius);
|
bestPos = BestPositionForMeleeToFlee(pos, radius);
|
||||||
} else {
|
} else {
|
||||||
bestPos = BestPositionForRanged(pos, radius);
|
bestPos = BestPositionForRangedToFlee(pos, radius);
|
||||||
}
|
}
|
||||||
if (bestPos != Position()) {
|
if (bestPos != Position()) {
|
||||||
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) {
|
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) {
|
||||||
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
|
|
||||||
lastTellTimer = time(NULL);
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "I'm avoiding " << name << "...";
|
|
||||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CombatFormationMoveAction::isUseful()
|
||||||
|
{
|
||||||
|
if (getMSTime() - moveInterval < lastMoveTimer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
return dis > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CombatFormationMoveAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
Player* playerToLeave = NearestGroupMember(dis);
|
||||||
|
if (playerToLeave && bot->GetExactDist(playerToLeave) < dis) {
|
||||||
|
if (FleePosition(playerToLeave->GetPosition(), dis)) {
|
||||||
|
lastMoveTimer = getMSTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position CombatFormationMoveAction::AverageGroupPos(float dis)
|
||||||
|
{
|
||||||
|
float averageX = 0, averageY = 0, averageZ = 0;
|
||||||
|
int cnt = 0;
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group) {
|
||||||
|
return Position();
|
||||||
|
}
|
||||||
|
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||||
|
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||||
|
{
|
||||||
|
Player *member = ObjectAccessor::FindPlayer(itr->guid);
|
||||||
|
if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis)
|
||||||
|
continue;
|
||||||
|
cnt++;
|
||||||
|
averageX += member->GetPositionX();
|
||||||
|
averageY += member->GetPositionY();
|
||||||
|
averageZ += member->GetPositionZ();
|
||||||
|
}
|
||||||
|
averageX /= cnt;
|
||||||
|
averageY /= cnt;
|
||||||
|
averageZ /= cnt;
|
||||||
|
return Position(averageX, averageY, averageZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* CombatFormationMoveAction::NearestGroupMember(float dis)
|
||||||
|
{
|
||||||
|
float nearestDis = 10000.0f;
|
||||||
|
Player* result = nullptr;
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||||
|
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||||
|
{
|
||||||
|
Player *member = ObjectAccessor::FindPlayer(itr->guid);
|
||||||
|
if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis)
|
||||||
|
continue;
|
||||||
|
if (nearestDis > bot->GetExactDist(member)) {
|
||||||
|
result = member;
|
||||||
|
nearestDis = bot->GetExactDist(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisperseSetAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
std::string const text = event.getParam();
|
||||||
|
if (text == "disable") {
|
||||||
|
RESET_AI_VALUE(float, "disperse distance");
|
||||||
|
botAI->TellMasterNoFacing("Disable disperse");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (text == "enable" || text == "reset") {
|
||||||
|
if (botAI->IsMelee(bot)) {
|
||||||
|
SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_MELEE);
|
||||||
|
} else {
|
||||||
|
SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_RANGED);
|
||||||
|
}
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Enable disperse distance " << std::setprecision(2) << dis;
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (text == "increase") {
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
std::ostringstream out;
|
||||||
|
if (dis <= 0.0f) {
|
||||||
|
out << "Enable disperse first";
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dis += 1.0f;
|
||||||
|
SET_AI_VALUE(float, "disperse distance", dis);
|
||||||
|
out << "Increase disperse distance to " << std::setprecision(2) << dis;
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (text == "decrease") {
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
dis -= 1.0f;
|
||||||
|
if (dis <= 0.0f) {
|
||||||
|
dis += 1.0f;
|
||||||
|
}
|
||||||
|
SET_AI_VALUE(float, "disperse distance", dis);
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Increase disperse distance to " << std::setprecision(2) << dis;
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (text.starts_with("set")) {
|
||||||
|
float dis = -1.0f;;
|
||||||
|
sscanf(text.c_str(), "set %f", &dis);
|
||||||
|
std::ostringstream out;
|
||||||
|
if (dis < 0 || dis > 100.0f) {
|
||||||
|
out << "Invalid disperse distance " << std::setprecision(2) << dis;
|
||||||
|
} else {
|
||||||
|
SET_AI_VALUE(float, "disperse distance", dis);
|
||||||
|
out << "Set disperse distance to " << std::setprecision(2) << dis;
|
||||||
|
}
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Usage: disperse [enable|disable|increase|decrease|set {distance}]" << "\n";
|
||||||
|
float dis = AI_VALUE(float, "disperse distance");
|
||||||
|
if (dis > 0.0f) {
|
||||||
|
out << "Current disperse distance: " << std::setprecision(2) << dis;
|
||||||
|
}
|
||||||
|
botAI->TellMasterNoFacing(out.str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool RunAwayAction::Execute(Event event)
|
bool RunAwayAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
return Flee(AI_VALUE(Unit*, "master target"));
|
return Flee(AI_VALUE(Unit*, "master target"));
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ class MovementAction : public Action
|
|||||||
bool MoveAway(Unit* target);
|
bool MoveAway(Unit* target);
|
||||||
bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance);
|
bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance);
|
||||||
void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false);
|
void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false);
|
||||||
|
Position BestPositionForMeleeToFlee(Position pos, float radius);
|
||||||
|
Position BestPositionForRangedToFlee(Position pos, float radius);
|
||||||
|
bool FleePosition(Position pos, float radius);
|
||||||
|
protected:
|
||||||
|
struct CheckAngle {
|
||||||
|
float angle;
|
||||||
|
bool strict;
|
||||||
|
};
|
||||||
private:
|
private:
|
||||||
// float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool normal_only = false, float step = 8.0f);
|
// float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool normal_only = false, float step = 8.0f);
|
||||||
const Movement::PointsArray SearchForBestPath(float x, float y, float z, float &modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f);
|
const Movement::PointsArray SearchForBestPath(float x, float y, float z, float &modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f);
|
||||||
@@ -69,7 +77,8 @@ class FleeWithPetAction : public MovementAction
|
|||||||
class AvoidAoeAction : public MovementAction
|
class AvoidAoeAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { }
|
AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "avoid aoe"),
|
||||||
|
moveInterval(moveInterval) { }
|
||||||
|
|
||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
@@ -78,14 +87,36 @@ class AvoidAoeAction : public MovementAction
|
|||||||
bool AvoidAuraWithDynamicObj();
|
bool AvoidAuraWithDynamicObj();
|
||||||
bool AvoidGameObjectWithDamage();
|
bool AvoidGameObjectWithDamage();
|
||||||
bool AvoidUnitWithDamageAura();
|
bool AvoidUnitWithDamageAura();
|
||||||
Position BestPositionForMelee(Position pos, float radius);
|
|
||||||
Position BestPositionForRanged(Position pos, float radius);
|
|
||||||
bool FleePosition(Position pos, float radius, std::string name);
|
|
||||||
time_t lastTellTimer = 0;
|
time_t lastTellTimer = 0;
|
||||||
struct CheckAngle {
|
int lastMoveTimer = 0;
|
||||||
float angle;
|
int moveInterval;
|
||||||
bool strict;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CombatFormationMoveAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "combat formation move"),
|
||||||
|
moveInterval(moveInterval) { }
|
||||||
|
|
||||||
|
bool isUseful() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance);
|
||||||
|
Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance);
|
||||||
|
int lastMoveTimer = 0;
|
||||||
|
int moveInterval;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DisperseSetAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DisperseSetAction(PlayerbotAI* botAI, std::string const name = "disperse set") : Action(botAI, name) { }
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
float DEFAULT_DISPERSE_DISTANCE_RANGED = 5.0f;
|
||||||
|
float DEFAULT_DISPERSE_DISTANCE_MELEE = 2.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RunAwayAction : public MovementAction
|
class RunAwayAction : public MovementAction
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
triggers.push_back(new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL)));
|
triggers.push_back(new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL)));
|
||||||
triggers.push_back(new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
triggers.push_back(new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
||||||
triggers.push_back(new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL)));
|
triggers.push_back(new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL)));
|
||||||
|
triggers.push_back(new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||||
|
|||||||
@@ -82,3 +82,10 @@ void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
|||||||
{
|
{
|
||||||
// multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
|
// multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NextAction** CombatFormationStrategy::getDefaultActions()
|
||||||
|
{
|
||||||
|
return NextAction::array(0,
|
||||||
|
new NextAction("combat formation move", ACTION_EMERGENCY),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,4 +28,12 @@ public:
|
|||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CombatFormationStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CombatFormationStrategy(PlayerbotAI* ai): Strategy(ai) {}
|
||||||
|
const std::string getName() override { return "combat formation"; }
|
||||||
|
NextAction** getDefaultActions() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
|
|||||||
creators["naxx"] = &ChatTriggerContext::naxx;
|
creators["naxx"] = &ChatTriggerContext::naxx;
|
||||||
creators["bwl"] = &ChatTriggerContext::bwl;
|
creators["bwl"] = &ChatTriggerContext::bwl;
|
||||||
creators["dps"] = &ChatTriggerContext::dps;
|
creators["dps"] = &ChatTriggerContext::dps;
|
||||||
|
creators["disperse"] = &ChatTriggerContext::disperse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -218,6 +219,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
|
|||||||
static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); }
|
static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); }
|
||||||
static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); }
|
static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); }
|
||||||
static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
|
static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
|
||||||
|
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
||||||
creators["area debuff"] = &ValueContext::area_debuff;
|
creators["area debuff"] = &ValueContext::area_debuff;
|
||||||
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
||||||
|
creators["disperse distance"] = &ValueContext::disperse_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -505,6 +506,7 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
||||||
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
||||||
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
||||||
|
static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user