Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/modules/Bots/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ set(BOT_SRCS
playerbot/strategy/actions/BuyAction.h
playerbot/strategy/actions/CastCustomSpellAction.cpp
playerbot/strategy/actions/CastCustomSpellAction.h
playerbot/strategy/actions/CcActions.h
playerbot/strategy/actions/CcActions.cpp
playerbot/strategy/actions/ChangeChatAction.cpp
playerbot/strategy/actions/ChangeChatAction.h
playerbot/strategy/actions/ChangeStrategyAction.cpp
Expand Down Expand Up @@ -486,6 +488,8 @@ set(BOT_SRCS
playerbot/strategy/values/AttackerWithoutAuraTargetValue.cpp
playerbot/strategy/values/AttackerWithoutAuraTargetValue.h
playerbot/strategy/values/AvailableLootValue.h
playerbot/strategy/values/CcReachTargetValue.cpp
playerbot/strategy/values/CcReachTargetValue.h
playerbot/strategy/values/CcTargetValue.cpp
playerbot/strategy/values/CcTargetValue.h
playerbot/strategy/values/ChatValue.h
Expand Down
3 changes: 3 additions & 0 deletions src/modules/Bots/playerbot/strategy/actions/ActionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "PullActions.h"
#include "FishWithMasterAction.h"
#include "RestoreWeaponsAction.h"
#include "CcActions.h"

namespace ai
{
Expand Down Expand Up @@ -87,6 +88,7 @@ namespace ai
creators["fish with master"] = &ActionContext::fish_with_master;
creators["restore weapons"] = &ActionContext::restore_weapons;
creators["swim to surface"] = &ActionContext::swim_to_surface;
creators["cc reach spell"] = &ActionContext::cc_reach_spell;
}

private:
Expand Down Expand Up @@ -152,6 +154,7 @@ namespace ai
static Action* fish_with_master(PlayerbotAI* ai) { return new FishWithMasterAction(ai); }
static Action* restore_weapons(PlayerbotAI* ai) { return new RestoreWeaponsAction(ai); }
static Action* swim_to_surface(PlayerbotAI* ai) { return new SwimToSurfaceAction(ai); }
static Action* cc_reach_spell(PlayerbotAI* ai) { return new CcReachSpellAction(ai); }

};
};
306 changes: 306 additions & 0 deletions src/modules/Bots/playerbot/strategy/actions/CcActions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
#include "botpch.h"
#include "../../playerbot.h"
#include "CcActions.h"

using namespace ai;

bool CcReachSpellAction::Execute(Event event)
{
Unit* target = AI_VALUE(Unit*, "cc reach target");
if (!target)
{
return false;
}

float dist = bot->GetDistance(target);
if (dist <= sPlayerbotAIConfig.spellDistance +
sPlayerbotAIConfig.tooCloseDistance)
{
if (!bot->IsWithinLOSInMap(target))
{
float nx, ny, nz;
if (FindNearbyLosPoint(target, nx, ny, nz))
{
return MoveTo(bot->GetMapId(), nx, ny, nz);
}
}
return false;
}

return MoveTo(target, sPlayerbotAIConfig.spellDistance);
}

bool CcReachSpellAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "cc reach target");
if (!target)
{
return false;
}

if (bot->GetDistance(target) > sPlayerbotAIConfig.spellDistance ||
!bot->IsWithinLOSInMap(target))
{
return true;
}

return false;
}

bool CastCcOnMyTargetAction::isPersistent()
{
if (bot->getClass() == CLASS_ROGUE)
{
return false;
}

return !m_ccWithdrawn;
}

bool CastCcOnMyTargetAction::isUseful()
{
if (bot->getClass() == CLASS_ROGUE)
{
return false;
}

if (m_ccWithdrawn)
{
Player* master = GetMaster();
if (master)
{
Unit* target = sObjectAccessor.GetUnit(*bot, master->GetSelectionGuid());
ObjectGuid currentGuid = target ? target->GetObjectGuid() : ObjectGuid();
ObjectGuid ccGuid = m_ccTargetGuid;

if (currentGuid != ccGuid)
{
m_castAttempts = 0;
m_ccWithdrawn = false;
m_ccTargetGuid = ObjectGuid();
return true;
}
}

return false;
}

Player* master = GetMaster();
if (!master)
{
return false;
}

Unit* target = sObjectAccessor.GetUnit(*bot, master->GetSelectionGuid());
if (!target || !target->IsAlive())
{
return false;
}

return true;
}

NextAction** CastCcOnMyTargetAction::getPrerequisites()
{
if (bot->getClass() == CLASS_ROGUE)
{
return NULL;
}

Player* master = GetMaster();
if (!master)
{
return NULL;
}

Unit* target = sObjectAccessor.GetUnit(*bot, master->GetSelectionGuid());
if (!target)
{
return NULL;
}

string ccSpell = GetCcSpell(target);
if (ccSpell.empty() || ai->HasAura(ccSpell, target))
{
return NULL;
}

context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<Unit*>("cc reach target")->Set(target);

return NextAction::array(0, new NextAction("cc reach spell"), NULL);
}

bool CastCcOnMyTargetAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
{
return false;
}

Unit* target = sObjectAccessor.GetUnit(*bot, master->GetSelectionGuid());
if (!target)
{
ai->TellMaster("You have no target");
return false;
}

if (bot->getClass() == CLASS_ROGUE)
{
context->GetValue<Unit*>("current target")->Set(target);
ai->ChangeStrategy("+sap", BOT_STATE_NON_COMBAT);
return true;
}

string ccSpell = GetCcSpell(target);
if (ccSpell.empty())
{
ai->TellMaster("I have no crowd control spell");
return false;
}

if (ai->HasAura(ccSpell, target))
{
if (!m_ccWithdrawn)
{
m_ccTargetGuid = target->GetObjectGuid();
WithdrawToGroupCenter(target);
m_ccWithdrawn = true;
}
return true;
}

float range = AI_VALUE2(float, "spell range", ccSpell);
if (bot->GetDistance(target) > range || bot->IsNonMeleeSpellCasted(false))
{
return true;
}

uint32 spellId = AI_VALUE2(uint32, "spell id", ccSpell);
if (!spellId)
{
ostringstream msg;
msg << "I don't know spell " << ccSpell;
ai->TellMaster(msg.str());
return false;
}

if (m_lastCastAttempt > 0 && time(0) - m_lastCastAttempt < 1)
{
return true;
}

ai->CastSpell(spellId, target);
m_lastCastAttempt = time(0);

Spell* pcs = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);

if (!pcs)
{
m_castAttempts++;

if (m_castAttempts >= 3)
{
ai->TellMaster("I cannot cast that");
m_ccWithdrawn = true;
return false;
}
return true;
}

m_castAttempts = 0;

ostringstream msg;
msg << "Casting " << ccSpell << " on " << target->GetName();
ai->TellMasterNoFacing(msg.str());
return true;
}

string CastCcOnMyTargetAction::GetCcSpell(Unit* target)
{
switch (bot->getClass())
{
case CLASS_MAGE:
return "polymorph";
case CLASS_WARLOCK:
{
uint32 ctype = target->GetCreatureType();
if (ctype == CREATURE_TYPE_DEMON ||
ctype == CREATURE_TYPE_ELEMENTAL)
{
return "banish";
}
return "fear";
}
case CLASS_HUNTER:
return "freezing trap";
case CLASS_PRIEST:
{
if (target->GetCreatureType() == CREATURE_TYPE_UNDEAD)
{
return "shackle undead";
}
return "";
}
case CLASS_DRUID:
{
uint32 ctype = target->GetCreatureType();
if (ctype == CREATURE_TYPE_BEAST ||
ctype == CREATURE_TYPE_DRAGONKIN)
{
return "hibernate";
}
return "entangling roots";
}
default:
return "";
}
}

void CastCcOnMyTargetAction::WithdrawToGroupCenter(Unit* ccTarget)
{
Group* group = bot->GetGroup();
if (!group)
{
return;
}

float myDist = bot->GetDistance(ccTarget);
bool amClosest = true;

float sumX = 0.0f, sumY = 0.0f, sumZ = 0.0f;
int count = 0;

for (GroupReference* ref = group->GetFirstMember();
ref; ref = ref->next())
{
Player* member = ref->getSource();
if (!member || member == bot)
{
continue;
}

float dist = member->GetDistance(ccTarget);
if (dist < myDist)
{
amClosest = false;
}

sumX += member->GetPositionX();
sumY += member->GetPositionY();
sumZ += member->GetPositionZ();
count++;
}

if (!amClosest || count == 0)
{
return;
}

float medianX = sumX / count;
float medianY = sumY / count;
float medianZ = sumZ / count;

MoveTo(bot->GetMapId(), medianX, medianY, medianZ);
}
38 changes: 38 additions & 0 deletions src/modules/Bots/playerbot/strategy/actions/CcActions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include "ObjectAccessor.h"
#include "MovementActions.h"

namespace ai
{
class CcReachSpellAction : public MovementAction
{
public:
CcReachSpellAction(PlayerbotAI* ai)
: MovementAction(ai, "cc reach spell") {}

virtual bool Execute(Event event);
virtual bool isUseful();
};

class CastCcOnMyTargetAction : public MovementAction
{
public:
CastCcOnMyTargetAction(PlayerbotAI* ai)
: MovementAction(ai, "cc on my target") {}

virtual bool isPersistent();
virtual bool isUseful();
virtual NextAction** getPrerequisites();
virtual bool Execute(Event event);

private:
time_t m_lastCastAttempt = 0;
uint32 m_castAttempts = 0;
ObjectGuid m_ccTargetGuid;
bool m_ccWithdrawn = false;

string GetCcSpell(Unit* target);
void WithdrawToGroupCenter(Unit* ccTarget);
};
}
Loading
Loading