Merge pull request #509 from liyunfan1223/spell_interruption

Spell interruption
This commit is contained in:
Yunfan Li
2024-09-07 13:11:38 +08:00
committed by GitHub
28 changed files with 376 additions and 259 deletions

View File

@@ -774,12 +774,12 @@ AiPlayerbot.RandomBotInWorldWithRotationDisabled = 31104000
AiPlayerbot.PremadeSpecName.1.0 = arms pve
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421
AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100202012013031241
AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032123335100202012013031251-32505010002
AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100002012211231241
AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032023335100102012213231251-305-2033
AiPlayerbot.PremadeSpecName.1.1 = fury pve
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432
AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351
AiPlayerbot.PremadeSpecLink.1.1.80 = 30202300233-305053000500310153120511351
AiPlayerbot.PremadeSpecLink.1.1.80 = 32002300233-305053000500310153120511351
AiPlayerbot.PremadeSpecName.1.2 = prot pve
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
@@ -825,8 +825,8 @@ AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021
AiPlayerbot.PremadeSpecName.3.1 = mm pve
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233125031051
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002
AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-025335101030013233135031351-5000002
AiPlayerbot.PremadeSpecName.3.2 = surv pve
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311
@@ -910,8 +910,8 @@ AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351
AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003
AiPlayerbot.PremadeSpecName.6.2 = unholy pve
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549
AiPlayerbot.PremadeSpecLink.6.2.60 = --2300303050032152000150213130051
AiPlayerbot.PremadeSpecLink.6.2.80 = -320053500002-2300303050032152000150213130051
AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151
AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305

View File

@@ -310,24 +310,22 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType()));
}
if (!CanUpdateAI())
return;
// check activity
AllowActivity();
// if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) {
// return;
// }
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (currentSpell && currentSpell->getState() == SPELL_STATE_CASTING && currentSpell->GetCastTime())
if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING)
{
nextAICheckDelay = currentSpell->GetCastTime() + sPlayerbotAIConfig->reactDelay;
SetNextCheckDelay(nextAICheckDelay);
if (!CanUpdateAI())
return;
if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive())
{
bot->InterruptSpell(CURRENT_GENERIC_SPELL);
SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
}
return;
}
if (!CanUpdateAI())
return;
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup())
{
Player* leader = bot->GetGroup()->GetLeader();
@@ -898,9 +896,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
p >> casterGuid.ReadAsPacked();
if (casterGuid != bot->GetGUID())
return;
uint8 count, result;
uint32 spellId;
p >> spellId;
p >> count >> spellId >> result;
SpellInterrupted(spellId);
return;
}
@@ -1134,21 +1132,15 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
void PlayerbotAI::SpellInterrupted(uint32 spellid)
{
for (uint8 type = CURRENT_MELEE_SPELL; type < CURRENT_CHANNELED_SPELL; type++)
{
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
if (!spell)
continue;
if (spell->GetSpellInfo()->Id == spellid)
bot->InterruptSpell((CurrentSpellTypes)type);
}
LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
if (!spellid || lastSpell.id != spellid)
return;
time_t now = time(nullptr);
if (now <= lastSpell.timer)
return;
uint32 castTimeSpent = 1000 * (now - lastSpell.timer);
int32 globalCooldown = CalculateGlobalCooldown(lastSpell.id);
if (static_cast<int32>(castTimeSpent) < globalCooldown)
SetNextCheckDelay(globalCooldown - castTimeSpent);
else
SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
lastSpell.id = 0;
}
@@ -1512,14 +1504,15 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
default:
break;
}
if (strategyName.empty())
return;
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
if (tellMaster && !strategyName.empty())
{
std::ostringstream out;
out << "Add " << strategyName << " instance strategy";
TellMaster(out.str());
TellMasterNoFacing(out.str());
}
}
@@ -3616,6 +3609,7 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo
void PlayerbotAI::WaitForSpellCast(Spell* spell)
{
return;
SpellInfo const* spellInfo = spell->GetSpellInfo();
uint32 castTime = spell->GetCastTime();
// float castTime = spell->GetCastTime();

View File

@@ -128,6 +128,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
allowed = false;
out << "Failure: You are not allowed to control bot " << bot->GetName().c_str();
}
if (allowed && masterSession)
{
Player* player = masterSession->GetPlayer();
@@ -145,6 +146,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
out << "Failure: You have added too many bots for this class";
}
}
if (allowed)
{
sRandomPlayerbotMgr->OnPlayerLogin(bot);
@@ -1305,7 +1307,7 @@ uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls)
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
{
Player* const bot = it->second;
if (bot->getClass() == cls)
if (bot && bot->IsInWorld() && bot->getClass() == cls)
{
count++;
}

View File

@@ -207,7 +207,8 @@ void PlayerbotFactory::Randomize(bool incremental)
{
ClearAllItems();
}
// bot->SaveToDB(false, false);
bot->RemoveAllSpellCooldown();
UnbindInstance();
bot->GiveLevel(level);
bot->InitStatsForLevel();
@@ -3880,7 +3881,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
}
availableGems.push_back(enchantGem);
}
StatsWeightCalculator calculator(bot);
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
{
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
@@ -3940,7 +3941,6 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
{
continue;
}
StatsWeightCalculator calculator(bot);
float score = calculator.CalculateEnchant(enchant_id);
if (score >= bestScore)
{

View File

@@ -11,7 +11,7 @@
#include "SpellMgr.h"
#include "UpdateFields.h"
StatsCollector::StatsCollector(CollectorType type) : type_(type) { Reset(); }
StatsCollector::StatsCollector(CollectorType type, int32 cls) : type_(type), cls_(cls) { Reset(); }
void StatsCollector::Reset()
{
@@ -243,6 +243,18 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
case 67771: // Death's Verdict (heroic)
stats[STATS_TYPE_ATTACK_POWER] += 260;
return true;
case 71406: // Tiny Abomination in a Jar
if (cls_ == CLASS_PALADIN)
stats[STATS_TYPE_ATTACK_POWER] += 600;
else
stats[STATS_TYPE_ATTACK_POWER] += 150;
return true;
case 71545: // Tiny Abomination in a Jar (heroic)
if (cls_ == CLASS_PALADIN)
stats[STATS_TYPE_ATTACK_POWER] += 800;
else
stats[STATS_TYPE_ATTACK_POWER] += 200;
return true;
case 71519: // Deathbringer's Will
stats[STATS_TYPE_ATTACK_POWER] += 350;
return true;

View File

@@ -57,7 +57,7 @@ enum CollectorType : uint8
class StatsCollector
{
public:
StatsCollector(CollectorType type);
StatsCollector(CollectorType type, int32 cls = -1);
StatsCollector(StatsCollector& stats) = default;
void Reset();
void CollectItemStats(ItemTemplate const* proto);
@@ -78,6 +78,7 @@ private:
private:
CollectorType type_;
uint32 cls_;
};
#endif

View File

@@ -27,10 +27,10 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
type_ = CollectorType::MELEE;
else
type_ = CollectorType::RANGED;
collector_ = std::make_unique<StatsCollector>(type_);
cls = player->getClass();
tab = AiFactory::GetPlayerSpecTab(player);
collector_ = std::make_unique<StatsCollector>(type_, cls);
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
hitOverflowType_ = CollectorType::SPELL;
@@ -128,58 +128,58 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
{
stats_weights_[STATS_TYPE_AGILITY] += 2.4f;
stats_weights_[STATS_TYPE_AGILITY] += 2.5f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f;
stats_weights_[STATS_TYPE_HIT] += 1.6f;
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
stats_weights_[STATS_TYPE_HASTE] += 1.4f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f;
stats_weights_[STATS_TYPE_HIT] += 1.7f;
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
stats_weights_[STATS_TYPE_HASTE] += 1.6f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 7.5f;
}
else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP)
{
stats_weights_[STATS_TYPE_AGILITY] += 2.2f;
stats_weights_[STATS_TYPE_AGILITY] += 2.3f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.2f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.25f;
stats_weights_[STATS_TYPE_HIT] += 2.1f;
stats_weights_[STATS_TYPE_CRIT] += 2.0f;
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 10.0f;
}
else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT)
{
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
stats_weights_[STATS_TYPE_AGILITY] += 1.9f;
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
stats_weights_[STATS_TYPE_HIT] += 2.0f;
stats_weights_[STATS_TYPE_CRIT] += 1.6f;
stats_weights_[STATS_TYPE_HASTE] += 1.4f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.8f;
stats_weights_[STATS_TYPE_HIT] += 2.1f;
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
stats_weights_[STATS_TYPE_HASTE] += 1.7f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
}
else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player))
{
stats_weights_[STATS_TYPE_AGILITY] += 2.4f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.3f;
stats_weights_[STATS_TYPE_AGILITY] += 2.2f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.4f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.3f;
stats_weights_[STATS_TYPE_HIT] += 1.9f;
stats_weights_[STATS_TYPE_CRIT] += 1.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.4f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
stats_weights_[STATS_TYPE_HASTE] += 2.1f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 15.0f;
}
else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY))
{
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
stats_weights_[STATS_TYPE_AGILITY] += 1.5f;
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f;
stats_weights_[STATS_TYPE_HIT] += 1.6f;
stats_weights_[STATS_TYPE_CRIT] += 1.3f;
stats_weights_[STATS_TYPE_HASTE] += 1.5f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
stats_weights_[STATS_TYPE_HIT] += 2.1f;
stats_weights_[STATS_TYPE_CRIT] += 1.1f;
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
}
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury
@@ -208,70 +208,86 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
}
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk
{
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.6f;
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.8f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.7f;
stats_weights_[STATS_TYPE_HIT] += 2.3f;
stats_weights_[STATS_TYPE_CRIT] += 2.2f;
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
stats_weights_[STATS_TYPE_HASTE] += 2.1f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
}
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
{
stats_weights_[STATS_TYPE_AGILITY] += 0.5f;
stats_weights_[STATS_TYPE_AGILITY] += 0.9f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f;
stats_weights_[STATS_TYPE_HIT] += 1.8f;
stats_weights_[STATS_TYPE_CRIT] += 1.0f;
stats_weights_[STATS_TYPE_HASTE] += 1.7f;
stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f;
stats_weights_[STATS_TYPE_HIT] += 2.2f;
stats_weights_[STATS_TYPE_CRIT] += 1.7f;
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
}
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
{
stats_weights_[STATS_TYPE_AGILITY] += 1.3f;
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
stats_weights_[STATS_TYPE_INTELLECT] += 0.15f;
stats_weights_[STATS_TYPE_INTELLECT] += 0.1f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.7f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f;
stats_weights_[STATS_TYPE_HIT] += 1.9f;
stats_weights_[STATS_TYPE_CRIT] += 1.3f;
stats_weights_[STATS_TYPE_HASTE] += 1.2f;
stats_weights_[STATS_TYPE_CRIT] += 1.7f;
stats_weights_[STATS_TYPE_HASTE] += 1.6f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 9.0f;
}
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement
{
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
stats_weights_[STATS_TYPE_AGILITY] += 1.4f;
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
stats_weights_[STATS_TYPE_INTELLECT] += 0.5f;
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.9f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
stats_weights_[STATS_TYPE_HIT] += 1.7f;
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.95f;
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.9f;
stats_weights_[STATS_TYPE_HIT] += 2.1f;
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f;
}
else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE ||
else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.5f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.4f;
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
stats_weights_[STATS_TYPE_SPELL_PENETRATION] += 1.0f;
stats_weights_[STATS_TYPE_HIT] += 1.1f;
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
}
else if (cls == CLASS_MAGE && tab == MAGE_TAB_FIRE)
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.7f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
stats_weights_[STATS_TYPE_HIT] += 1.2f;
stats_weights_[STATS_TYPE_CRIT] += 1.1f;
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
}
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL)
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.25f;
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
stats_weights_[STATS_TYPE_HIT] += 1.1f;
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
}
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION) || // heal
@@ -349,6 +365,8 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player)
{
if (player->HasAura(34484))
stats_weights_[STATS_TYPE_INTELLECT] += 1.1f;
if (player->HasAura(56341))
stats_weights_[STATS_TYPE_STAMINA] += 0.3f;
}
else if (cls == CLASS_WARRIOR)
{

View File

@@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Player* master = botAI->GetGroupMaster();
Player* gmaster = botAI->GetGroupMaster();
Player* realMaster = botAI->GetMaster();
AiObjectContext* context = botAI->GetAiObjectContext();
@@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
return false;
}
if (!master || bot == master)
if (!gmaster || bot == gmaster)
return true;
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
return true;
if (bot->GetDistance(master) > sPlayerbotAIConfig->rpgDistance * 2)
if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2)
return false;
Formation* formation = AI_VALUE(Formation*, "formation");
float distance = master->GetDistance2d(pos.getX(), pos.getY());
float distance = gmaster->GetDistance2d(pos.getX(), pos.getY());
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
{
Player* player = master;
if (!master->isMoving() ||
Player* player = gmaster;
if (gmaster && !gmaster->isMoving() ||
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
return true;
}
if ((inDungeon || !master->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == master && distance > 5.0f)
if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f)
return false;
if (!master->isMoving() && distance < 25.0f)
if (!gmaster->isMoving() && distance < 25.0f)
return true;
if (distance < formation->GetMaxDistance())

View File

@@ -162,8 +162,14 @@ bool CastMeleeDebuffSpellAction::isUseful()
bool CastAuraSpellAction::isUseful()
{
return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() &&
!botAI->HasAura(spell, GetTarget(), false, isOwner);
if (!GetTarget() || !CastSpellAction::isUseful())
return false;
Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration);
if (!aura)
return true;
if (beforeDuration && aura->GetDuration() < beforeDuration)
return true;
return false;
}
CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell)
@@ -251,8 +257,8 @@ Value<Unit*>* CastDebuffSpellOnMeleeAttackerAction::GetTargetValue()
return context->GetValue<Unit*>("melee attacker without aura", spell);
}
CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner)
: CastAuraSpellAction(botAI, spell, checkIsOwner)
CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner, uint32 beforeDuration)
: CastAuraSpellAction(botAI, spell, checkIsOwner, false, beforeDuration)
{
range = botAI->GetRange("spell");
}

View File

@@ -37,16 +37,20 @@ protected:
class CastAuraSpellAction : public CastSpellAction
{
public:
CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false)
CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
: CastSpellAction(botAI, spell)
{
this->isOwner = isOwner;
this->beforeDuration = beforeDuration;
this->checkDuration = checkDuration;
}
bool isUseful() override;
protected:
bool isOwner;
bool checkDuration;
uint32 beforeDuration;
};
class CastMeleeSpellAction : public CastSpellAction
@@ -107,7 +111,7 @@ public:
class CastBuffSpellAction : public CastAuraSpellAction
{
public:
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false);
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false, uint32 beforeDuration = 0);
std::string const GetTargetName() override { return "self target"; }
};

View File

@@ -814,39 +814,25 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
float combatDistance = bot->GetCombatReach() + target->GetCombatReach();
distance += combatDistance;
if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD)) // target is moving forward, predict the position
{
float needToGo = bot->GetExactDist(target) - distance;
float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f;
float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN);
targetMoveDist = std::min(5.0f, targetMoveDist);
tx += targetMoveDist * cos(target->GetOrientation());
ty += targetMoveDist * sin(target->GetOrientation());
if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(),
target->GetPositionZ(), tx, ty, tz))
{
// disable prediction if position is invalid
tx = target->GetPositionX();
ty = target->GetPositionY();
tz = target->GetPositionZ();
}
// Prediction may cause this, which makes ShortenPathUntilDist fail
if (bot->GetExactDist(tx, ty, tz) <= distance)
{
tx = target->GetPositionX();
ty = target->GetPositionY();
tz = target->GetPositionZ();
}
}
if (bot->GetExactDist(tx, ty, tz) <= distance)
return false;
PathGenerator path(bot);
path.CalculatePath(tx, ty, tz, false);
PathType type = path.GetPathType();
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
if (!(type & typeOk))
return false;
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance);
float shortenTo = distance;
// Avoid walking too far when moving towards each other
if (bot->GetDistance(tx, ty, tz) >= 10.0f)
shortenTo = std::max(distance, bot->GetDistance(tx, ty, tz) / 2);
if (bot->GetExactDist(tx, ty, tz) <= shortenTo)
return false;
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo);
G3D::Vector3 endPos = path.GetPath().back();
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);

View File

@@ -29,13 +29,14 @@ bool TellAttackersAction::Execute(Event event)
botAI->TellMaster("--- Attackers ---");
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
int32 count = 0;
for (ObjectGuid const guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
botAI->TellMaster(unit->GetName());
botAI->TellMaster(std::to_string(++count) + std::string(".") + unit->GetName());
}
botAI->TellMaster("--- Threat ---");

View File

@@ -165,16 +165,10 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
bool SummonAction::Teleport(Player* summoner, Player* player)
{
Player* master = GetMaster();
// if (master->GetMap() && master->GetMap()->IsDungeon()) {
// InstanceMap* map = master->GetMap()->ToInstanceMap();
// if (map) {
// if (map->CannotEnter(player, true) == Map::CANNOT_ENTER_MAX_PLAYERS) {
// botAI->TellError("I can not enter this dungeon");
// return false;
// }
// }
// }
// Player* master = GetMaster();
if (!summoner)
return false;
if (player->GetVehicle())
{
botAI->TellError("You cannot summon me while I'm on a vehicle");
@@ -197,13 +191,13 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on
bot->DurabilityRepairAll(false, 1.0f, false);
if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat)
if (summoner->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat)
{
botAI->TellError("You cannot summon me while you're in combat");
return false;
}
if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead)
if (!summoner->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead)
{
botAI->TellError("You cannot summon me while you're dead");
return false;
@@ -218,7 +212,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
bool revive =
sPlayerbotAIConfig->reviveBotWhenSummoned == 2 ||
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive());
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive());
if (bot->isDead() && revive)
{

View File

@@ -100,9 +100,12 @@ void BloodDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 5),
new NextAction("vampiric blood", ACTION_HIGH + 4),
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 4),
new NextAction("death strike", ACTION_HIGH + 3), nullptr)));
// triggers.push_back(new TriggerNode("army of the dead", NextAction::array(0, new NextAction("army of the dead",
// ACTION_HIGH + 6), nullptr)));
triggers.push_back(
new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
}

View File

@@ -165,7 +165,7 @@ public:
class CastGhoulFrenzyAction : public CastBuffSpellAction
{
public:
CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy") {}
CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy", false, 5000) {}
std::string const GetTargetName() override { return "pet target"; }
};
@@ -242,7 +242,7 @@ class CastDeathAndDecayAction : public CastSpellAction
{
public:
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
// ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
};
class CastHornOfWinterAction : public CastSpellAction

View File

@@ -10,6 +10,7 @@
#include "DKTriggers.h"
#include "FrostDKStrategy.h"
#include "GenericDKNonCombatStrategy.h"
#include "GenericTriggers.h"
#include "Playerbots.h"
#include "PullStrategy.h"
#include "UnholyDKStrategy.h"
@@ -73,10 +74,14 @@ public:
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker;
creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch;
creators["icy touch 8s"] = &DeathKnightTriggerFactoryInternal::icy_touch_8s;
creators["dd cd and icy touch 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_8s;
creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil;
creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker;
creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons;
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
creators["plague strike 8s"] = &DeathKnightTriggerFactoryInternal::plague_strike_8s;
creators["dd cd and plague strike 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_8s;
creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter;
creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze;
creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer;
@@ -87,8 +92,11 @@ public:
creators["chains of ice"] = &DeathKnightTriggerFactoryInternal::chains_of_ice;
creators["unbreakable armor"] = &DeathKnightTriggerFactoryInternal::unbreakable_armor;
creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune;
creators["high frost rune"] = &DeathKnightTriggerFactoryInternal::high_frost_rune;
creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune;
creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog;
creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation;
creators["dd cd and no desolation"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_no_desolation;
creators["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown;
creators["army of the dead"] = &DeathKnightTriggerFactoryInternal::army_of_the_dead;
}
@@ -98,11 +106,15 @@ private:
static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); }
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
static Trigger* plague_strike_8s(PlayerbotAI* botAI) { return new PlagueStrike8sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_plague_strike_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 8s"); }
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI)
{
return new PlagueStrikeDebuffOnAttackerTrigger(botAI);
}
static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); }
static Trigger* icy_touch_8s(PlayerbotAI* botAI) { return new IcyTouch8sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_icy_touch_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 8s"); }
static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); }
static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); }
static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); }
@@ -122,8 +134,11 @@ private:
static Trigger* chains_of_ice(PlayerbotAI* botAI) { return new ChainsOfIceSnareTrigger(botAI); }
static Trigger* unbreakable_armor(PlayerbotAI* botAI) { return new UnbreakableArmorTrigger(botAI); }
static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); }
static Trigger* high_frost_rune(PlayerbotAI* botAI) { return new HighFrostRuneTrigger(botAI); }
static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); }
static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); }
static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); }
static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); }
static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); }
static Trigger* army_of_the_dead(PlayerbotAI* botAI) { return new ArmyOfTheDeadTrigger(botAI); }
};

View File

@@ -39,14 +39,21 @@ bool PestilenceGlyphTrigger::IsActive()
bool HighBloodRuneTrigger::IsActive()
{
// bot->Say(std::to_string(bot->GetBaseRune(0)) + "_" + std::to_string(bot->GetRuneCooldown(0)) + " " +
// std::to_string(bot->GetBaseRune(1)) + "_" + std::to_string(bot->GetRuneCooldown(1)), LANG_UNIVERSAL);
return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1);
}
bool HighFrostRuneTrigger::IsActive()
{
return !bot->GetRuneCooldown(2) && !bot->GetRuneCooldown(3);
}
bool HighUnholyRuneTrigger::IsActive()
{
return !bot->GetRuneCooldown(4) && !bot->GetRuneCooldown(5);
}
bool DesolationTrigger::IsActive()
{
return bot->HasAura(66817) && !botAI->HasAura("desolation", GetTarget(), false, true, -1, true);
return bot->HasAura(66817) && BuffTrigger::IsActive();
}
bool DeathAndDecayCooldownTrigger::IsActive()
@@ -54,6 +61,6 @@ bool DeathAndDecayCooldownTrigger::IsActive()
uint32 spellId = AI_VALUE2(uint32, "spell id", name);
if (!spellId)
return true;
return bot->HasSpellCooldown(spellId);
return bot->GetSpellCooldownDelay(spellId) >= 3000;
}

View File

@@ -17,14 +17,26 @@ BUFF_TRIGGER(ImprovedIcyTalonsTrigger, "improved icy talons");
class PlagueStrikeDebuffTrigger : public DebuffTrigger
{
public:
PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", true, .0f) {}
PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f) {}
};
class PlagueStrike8sDebuffTrigger : public DebuffTrigger
{
public:
PlagueStrike8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {}
};
// DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever");
class IcyTouchDebuffTrigger : public DebuffTrigger
{
public:
IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", true, .0f) {}
IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f) {}
};
class IcyTouch8sDebuffTrigger : public DebuffTrigger
{
public:
IcyTouch8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {}
};
BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor");
@@ -139,6 +151,20 @@ public:
bool IsActive() override;
};
class HighFrostRuneTrigger : public Trigger
{
public:
HighFrostRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high frost rune") {}
bool IsActive() override;
};
class HighUnholyRuneTrigger : public Trigger
{
public:
HighUnholyRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high unholy rune") {}
bool IsActive() override;
};
class FreezingFogTrigger : public HasAuraTrigger
{
public:
@@ -148,7 +174,7 @@ public:
class DesolationTrigger : public BuffTrigger
{
public:
DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation") {}
DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation", 1, false, true, 10000) {}
bool IsActive() override;
};

View File

@@ -26,7 +26,7 @@ public:
// creators["icebound fortitude"] = &icebound_fortitude;
// creators["mind freeze"] = &mind_freeze;
// creators["hungering cold"] = &hungering_cold;
// creators["unbreakable armor"] = &unbreakable_armor;
creators["unbreakable armor"] = &unbreakable_armor;
// creators["improved icy talons"] = &improved_icy_talons;
}
@@ -70,6 +70,13 @@ private:
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("unbreakable armor",
/*P*/ NextAction::array(0, new NextAction("blood tap"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
};
FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
@@ -80,24 +87,32 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
NextAction** FrostDKStrategy::getDefaultActions()
{
return NextAction::array(
0, new NextAction("obliterate", ACTION_DEFAULT + 0.5f), new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
// new NextAction("death strike", ACTION_NORMAL + 3),
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.2f),
0, new NextAction("obliterate", ACTION_DEFAULT + 0.7f),
new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
}
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f), nullptr)));
triggers.push_back(new TriggerNode(
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_DEFAULT + 0.5f), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.2f), nullptr)));
triggers.push_back(new TriggerNode(
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
triggers.push_back(new TriggerNode(
"unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_NORMAL + 4), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode(
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode(
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
// triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune
// weapon", ACTION_NORMAL + 4), nullptr)));
}

View File

@@ -135,7 +135,7 @@ private:
{
return new ActionNode("death and decay",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("blood tap"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
@@ -176,33 +176,14 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(
new TriggerNode("mind freeze on enemy healer",
NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(new TriggerNode(
"horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr)));
// triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("reach melee",
// ACTION_NORMAL + 8), nullptr)));
triggers.push_back(new TriggerNode("critical health",
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5),
new NextAction("rune tap", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode("medium health",
NextAction::array(0, new NextAction("rune tap", ACTION_NORMAL + 4),
new NextAction("death strike", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode("icy touch on attacker",
NextAction::array(0, new NextAction("icy touch on attacker", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode("plague strike on attacker",
NextAction::array(0, new NextAction("plague strike on attacker", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("high aoe",
// NextAction::array(0,
// new NextAction("death and decay", ACTION_NORMAL + 5),
// new NextAction("pestilence", ACTION_NORMAL + 4),
// new NextAction("blood boil", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9),
new NextAction("pestilence", ACTION_NORMAL + 4),

View File

@@ -26,7 +26,7 @@ public:
// creators["summon gargoyle"] = &army of the dead;
// creators["anti magic shell"] = &anti_magic_shell;
// creators["anti magic zone"] = &anti_magic_zone;
// creators["ghoul frenzy"] = &ghoul_frenzy;
creators["ghoul frenzy"] = &ghoul_frenzy;
creators["corpse explosion"] = &corpse_explosion;
creators["icy touch"] = &icy_touch;
}
@@ -35,15 +35,21 @@ private:
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death strike",
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("ghoul frenzy",
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("corpse explosion",
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
@@ -51,14 +57,14 @@ private:
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("scourge strike",
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icy touch",
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
/*A*/ nullptr,
/*C*/ nullptr);
}
@@ -72,29 +78,57 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
NextAction** UnholyDKStrategy::getDefaultActions()
{
return NextAction::array(
0, new NextAction("death and decay", ACTION_DEFAULT + 0.5f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.4f),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.3f), new NextAction("death coil", ACTION_DEFAULT + 0.2f),
new NextAction("scourge strike", ACTION_NORMAL + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr);
0, new NextAction("death and decay", ACTION_HIGH + 5),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
new NextAction("death coil", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
}
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericDKStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode(
"death and decay cooldown", NextAction::array(0,
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
new NextAction("blood boil", ACTION_DEFAULT + 0.7f),
new NextAction("icy touch", ACTION_DEFAULT + 0.6f),
new NextAction("plague strike", ACTION_DEFAULT + 0.5f),
nullptr)));
triggers.push_back(new TriggerNode("dd cd and no desolation",
NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr)));
// triggers.push_back(
// new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
// triggers.push_back(new TriggerNode(
// "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(
"high frost rune", NextAction::array(0,
new NextAction("icy touch", ACTION_NORMAL + 3), nullptr)));
triggers.push_back(new TriggerNode(
"high unholy rune", NextAction::array(0,
new NextAction("plague strike", ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr)));
triggers.push_back(
new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
triggers.push_back(new TriggerNode(
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
triggers.push_back(new TriggerNode("critical health",
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode("no desolation",
NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode(
"death and decay cooldown", NextAction::array(0, new NextAction("ghoul frenzy", ACTION_NORMAL + 5.0f),
new NextAction("scourge strike", ACTION_NORMAL + 4.0f),
new NextAction("blood boil", ACTION_NORMAL + 3.0f),
new NextAction("icy touch", ACTION_NORMAL + 2.0f),
new NextAction("plague strike", ACTION_NORMAL + 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr)));
}
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -152,7 +152,16 @@ bool OutNumberedTrigger::IsActive()
bool BuffTrigger::IsActive()
{
Unit* target = GetTarget();
return SpellTrigger::IsActive() && !botAI->HasAura(spell, target, false, checkIsOwner);
if (!target)
return false;
if (!SpellTrigger::IsActive())
return false;
Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration);
if (!aura)
return true;
if (beforeDuration && aura->GetDuration() < beforeDuration)
return true;
return false;
}
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()

View File

@@ -308,10 +308,12 @@ public:
class BuffTrigger : public SpellTrigger
{
public:
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false)
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
: SpellTrigger(botAI, spell, checkInterval)
{
this->checkIsOwner = checkIsOwner;
this->checkDuration = checkDuration;
this->beforeDuration = beforeDuration;
}
public:
@@ -320,6 +322,8 @@ public:
protected:
bool checkIsOwner;
bool checkDuration;
uint32 beforeDuration;
};
class BuffOnPartyTrigger : public BuffTrigger
@@ -379,8 +383,8 @@ class DebuffTrigger : public BuffTrigger
{
public:
DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false,
float needLifeTime = 8.0f)
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner), needLifeTime(needLifeTime)
float needLifeTime = 8.0f, uint32 beforeDuration = 0)
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner, false, beforeDuration), needLifeTime(needLifeTime)
{
}

View File

@@ -46,14 +46,14 @@ WorldLocation ArrowFormation::GetLocationInternal()
float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
float z = master->GetPositionZ();
float z = master->GetPositionZ() + master->GetHoverHeight();
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
master->GetPositionZ(), x, y, z))
{
x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
z = master->GetPositionZ() + master->GetHoverHeight();
z = master->GetMapHeight(x, y, z);
master->UpdateAllowedPositionZ(x, y, z);
}
return WorldLocation(master->GetMapId(), x, y, z);
}

View File

@@ -50,6 +50,7 @@ public:
CasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
{
result = nullptr;
}
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
@@ -86,13 +87,15 @@ public:
{
float new_time = new_unit->GetHealth() / dps_;
float old_time = old_unit->GetHealth() / dps_;
// [5-20] > (5-0] > (20-inf)
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
// [5-30] > (5-0] > (20-inf)
int new_level = GetIntervalLevel(new_unit);
int old_level = GetIntervalLevel(old_unit);
if (new_level != old_level)
{
return true;
return new_level > old_level;
}
int32_t level = GetIntervalLevel(new_unit);
if (level % 10 == 2 || level % 10 == 1)
int32_t level = new_level;
if (level % 10 == 2 || level % 10 == 0)
{
return new_time < old_time;
}
@@ -116,15 +119,15 @@ public:
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
attackRange += 5.0f;
int level = dis < attackRange ? 10 : 0;
if (time >= 3 && time <= 20)
if (time >= 5 && time <= 30)
{
return level + 2;
}
if (time > 20)
if (time > 30)
{
return level + 1;
return level;
}
return level;
return level + 1;
}
protected:
@@ -176,12 +179,14 @@ public:
float new_time = new_unit->GetHealth() / dps_;
float old_time = old_unit->GetHealth() / dps_;
// [5-20] > (5-0] > (20-inf)
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
int new_level = GetIntervalLevel(new_unit);
int old_level = GetIntervalLevel(old_unit);
if (new_level != old_level)
{
return true;
return new_level > old_level;
}
// attack enemy in range and with lowest health
int level = GetIntervalLevel(new_unit);
int level = new_level;
if (level == 10)
{
return new_time < old_time;
@@ -249,12 +254,14 @@ public:
float new_time = new_unit->GetHealth() / dps_;
float old_time = old_unit->GetHealth() / dps_;
// [5-20] > (5-0] > (20-inf)
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
int new_level = GetIntervalLevel(new_unit);
int old_level = GetIntervalLevel(old_unit);
if (new_level != old_level)
{
return true;
return new_level > old_level;
}
// attack enemy in range and with lowest health
int level = GetIntervalLevel(new_unit);
int level = new_level;
Player* bot = botAI->GetBot();
if (level == 10)
{

View File

@@ -88,7 +88,7 @@ public:
float angle = GetFollowAngle();
float x = master->GetPositionX() + cos(angle) * range;
float y = master->GetPositionY() + sin(angle) * range;
float z = master->GetPositionZ();
float z = master->GetPositionZ() + master->GetHoverHeight();
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
master->GetPositionZ(), x, y, z))
{
@@ -138,15 +138,14 @@ public:
float x = master->GetPositionX() + cos(angle) * range + dx;
float y = master->GetPositionY() + sin(angle) * range + dy;
float z = master->GetPositionZ();
z = bot->GetMapHeight(x, y, z + 5.0f);
float z = master->GetPositionZ() + master->GetHoverHeight();
if (!master->GetMap()->CheckCollisionAndGetValidCoords(
master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z))
{
x = master->GetPositionX() + cos(angle) * range + dx;
y = master->GetPositionY() + sin(angle) * range + dy;
z = master->GetPositionZ() + master->GetHoverHeight();
z = master->GetMapHeight(x, y, z);
master->UpdateAllowedPositionZ(x, y, z);
}
// bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
// bot->GetPositionZ(), x, y, z);
@@ -155,15 +154,14 @@ public:
float x = master->GetPositionX() + cos(angle) * range + dx;
float y = master->GetPositionY() + sin(angle) * range + dy;
float z = master->GetPositionZ();
z = bot->GetMapHeight(x, y, z + 5.0f);
float z = master->GetPositionZ() + master->GetHoverHeight();
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
master->GetPositionZ(), x, y, z))
{
x = master->GetPositionX() + cos(angle) * range + dx;
y = master->GetPositionY() + sin(angle) * range + dy;
z = master->GetPositionZ() + master->GetHoverHeight();
z = master->GetMapHeight(x, y, z);
master->UpdateAllowedPositionZ(x, y, z);
}
return WorldLocation(master->GetMapId(), x, y, z);
}
@@ -221,8 +219,8 @@ public:
{
x = target->GetPositionX() + cos(angle) * range;
y = target->GetPositionY() + sin(angle) * range;
z = target->GetPositionZ() + target->GetHoverHeight();
z = target->GetMapHeight(x, y, z);
z = target->GetPositionZ();
target->UpdateAllowedPositionZ(x, y, z);
}
return WorldLocation(bot->GetMapId(), x, y, z);
}
@@ -389,7 +387,7 @@ public:
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
z = master->GetPositionZ() + master->GetHoverHeight();
z = master->GetMapHeight(x, y, z);
master->UpdateAllowedPositionZ(x, y, z);
}
return WorldLocation(bot->GetMapId(), minX, minY, z);
}
@@ -403,7 +401,7 @@ public:
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
z = master->GetPositionZ() + master->GetHoverHeight();
z = master->GetMapHeight(x, y, z);
master->UpdateAllowedPositionZ(x, y, z);
}
return WorldLocation(bot->GetMapId(), x, y, z);
}

View File

@@ -77,7 +77,7 @@ GuidVector NearestTrapWithDamageValue::Calculate()
continue;
}
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo->IsPositive())
if (!spellInfo || spellInfo->IsPositive())
{
continue;
}

View File

@@ -34,8 +34,9 @@ ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStr
NextAction** ArmsWarriorStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("bladestorm", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
return NextAction::array(0, new NextAction("bladestorm", ACTION_DEFAULT + 0.2f),
new NextAction("mortal strike", ACTION_DEFAULT + 0.1f),
new NextAction("melee", ACTION_DEFAULT), nullptr);
}
void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@@ -63,9 +64,8 @@ void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"victory rush", NextAction::array(0, new NextAction("victory rush", ACTION_INTERRUPT), nullptr)));
triggers.push_back(new TriggerNode(
"medium rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 10), nullptr)));
/*triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("slam", ACTION_HIGH
* + 1), nullptr)));*/
"high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 10), nullptr)));
triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("slam", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
@@ -75,6 +75,6 @@ void ArmsWarriorStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
"rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode(
"critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("medium rage available",
triggers.push_back(new TriggerNode("medium aoe",
NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr)));
}