From 927d893945bc93be2ab39dec965e0870ec4d06a6 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 12:33:54 +0800 Subject: [PATCH 1/7] Improve combat reach and dps target, allowing spell interruption --- src/PlayerbotAI.cpp | 54 ++++++++++------------- src/factory/PlayerbotFactory.cpp | 3 +- src/strategy/actions/MovementActions.cpp | 36 +++++---------- src/strategy/actions/TellTargetAction.cpp | 3 +- src/strategy/values/DpsTargetValue.cpp | 37 +++++++++------- 5 files changed, 60 insertions(+), 73 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 0504b6b7..2c917074 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -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; } @@ -1135,21 +1133,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("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(castTimeSpent) < globalCooldown) - SetNextCheckDelay(globalCooldown - castTimeSpent); - else - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); - lastSpell.id = 0; } @@ -1513,14 +1505,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()); } } @@ -3617,6 +3610,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(); diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a6dc88b7..5b5eba2e 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -3880,7 +3880,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 +3940,6 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) { continue; } - StatsWeightCalculator calculator(bot); float score = calculator.CalculateEnchant(enchant_id); if (score >= bestScore) { diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 57eb4255..bafccd83 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -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); diff --git a/src/strategy/actions/TellTargetAction.cpp b/src/strategy/actions/TellTargetAction.cpp index d1d05384..f6f05639 100644 --- a/src/strategy/actions/TellTargetAction.cpp +++ b/src/strategy/actions/TellTargetAction.cpp @@ -29,13 +29,14 @@ bool TellAttackersAction::Execute(Event event) botAI->TellMaster("--- Attackers ---"); GuidVector attackers = context->GetValue("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 ---"); diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index 5fc276df..88c52ccf 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -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) { From 593b30bbb047a080ab5dc4b8f898a935082730bd Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:29:38 +0800 Subject: [PATCH 2/7] Arm warrior and dk --- conf/playerbots.conf.dist | 14 +- src/factory/StatsCollector.cpp | 14 +- src/factory/StatsCollector.h | 3 +- src/factory/StatsWeightCalculator.cpp | 126 ++++++++++-------- src/strategy/deathknight/BloodDKStrategy.cpp | 8 +- .../deathknight/DKAiObjectContext.cpp | 4 + src/strategy/deathknight/DKTriggers.cpp | 15 ++- src/strategy/deathknight/DKTriggers.h | 14 ++ src/strategy/deathknight/FrostDKStrategy.cpp | 32 +++-- .../deathknight/GenericDKStrategy.cpp | 15 +-- src/strategy/deathknight/UnholyDKStrategy.cpp | 56 +++++--- src/strategy/values/Arrow.cpp | 4 +- src/strategy/values/Formations.cpp | 20 ++- src/strategy/warrior/ArmsWarriorStrategy.cpp | 12 +- 14 files changed, 211 insertions(+), 126 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 4eabcb34..87008706 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -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 = --2301303050032151000150013113151 +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 diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 1f6c6d65..bc73fcb6 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -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; diff --git a/src/factory/StatsCollector.h b/src/factory/StatsCollector.h index fa0634ce..b51dddf9 100644 --- a/src/factory/StatsCollector.h +++ b/src/factory/StatsCollector.h @@ -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 \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index c3a328f0..f6270c73 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -27,10 +27,10 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) type_ = CollectorType::MELEE; else type_ = CollectorType::RANGED; - collector_ = std::make_unique(type_); - cls = player->getClass(); tab = AiFactory::GetPlayerSpecTab(player); + collector_ = std::make_unique(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_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.7f; - stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f; + 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) { diff --git a/src/strategy/deathknight/BloodDKStrategy.cpp b/src/strategy/deathknight/BloodDKStrategy.cpp index f76d6795..7daf494d 100644 --- a/src/strategy/deathknight/BloodDKStrategy.cpp +++ b/src/strategy/deathknight/BloodDKStrategy.cpp @@ -100,9 +100,9 @@ void BloodDKStrategy::InitTriggers(std::vector& 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))); + } diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 9023f8ac..5f7eb941 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -87,6 +87,8 @@ 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["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown; @@ -122,6 +124,8 @@ 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* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); } diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index 281ed586..ebec33cc 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -39,11 +39,18 @@ 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); @@ -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) >= 2000; } \ No newline at end of file diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index ecbf7964..7f6c6c81 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -139,6 +139,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: diff --git a/src/strategy/deathknight/FrostDKStrategy.cpp b/src/strategy/deathknight/FrostDKStrategy.cpp index 87d12b17..b05060c7 100644 --- a/src/strategy/deathknight/FrostDKStrategy.cpp +++ b/src/strategy/deathknight/FrostDKStrategy.cpp @@ -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,23 +87,28 @@ 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& 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("empower rune weapon", NextAction::array(0, new NextAction("empower rune // weapon", ACTION_NORMAL + 4), nullptr))); diff --git a/src/strategy/deathknight/GenericDKStrategy.cpp b/src/strategy/deathknight/GenericDKStrategy.cpp index 23693bcf..5fb8556f 100644 --- a/src/strategy/deathknight/GenericDKStrategy.cpp +++ b/src/strategy/deathknight/GenericDKStrategy.cpp @@ -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,18 +176,17 @@ void GenericDKStrategy::InitTriggers(std::vector& 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("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( diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index a5174f36..ac106a7f 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -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*/ 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,43 @@ 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_DEFAULT + 1.0f), + new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f), + new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f), + new NextAction("death coil", ACTION_DEFAULT + 0.2f), + new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), + new NextAction("melee", ACTION_DEFAULT), nullptr); } void UnholyDKStrategy::InitTriggers(std::vector& 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), + nullptr))); + + triggers.push_back(new TriggerNode( + "high frost rune", NextAction::array(0, new NextAction("icy touch", ACTION_DEFAULT + 0.6f), nullptr))); + + triggers.push_back(new TriggerNode( + "high unholy rune", NextAction::array(0, + new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), + new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); + + triggers.push_back(new TriggerNode( + "high blood rune", NextAction::array(0, new NextAction("blood boil", ACTION_DEFAULT + 0.7f), 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& triggers) diff --git a/src/strategy/values/Arrow.cpp b/src/strategy/values/Arrow.cpp index f52dc670..0a8afb50 100644 --- a/src/strategy/values/Arrow.cpp +++ b/src/strategy/values/Arrow.cpp @@ -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); } diff --git a/src/strategy/values/Formations.cpp b/src/strategy/values/Formations.cpp index 9854b420..8c918c56 100644 --- a/src/strategy/values/Formations.cpp +++ b/src/strategy/values/Formations.cpp @@ -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); } diff --git a/src/strategy/warrior/ArmsWarriorStrategy.cpp b/src/strategy/warrior/ArmsWarriorStrategy.cpp index bf56e891..540b6cd0 100644 --- a/src/strategy/warrior/ArmsWarriorStrategy.cpp +++ b/src/strategy/warrior/ArmsWarriorStrategy.cpp @@ -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& triggers) @@ -63,9 +64,8 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& 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& 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))); } From f20d3aea6c91c32106bfba9e5297caf482cf36c3 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:29:56 +0800 Subject: [PATCH 3/7] Unholy dk --- conf/playerbots.conf.dist | 4 +-- src/strategy/actions/GenericSpellActions.cpp | 14 +++++--- src/strategy/actions/GenericSpellActions.h | 8 +++-- src/strategy/deathknight/BloodDKStrategy.cpp | 5 ++- src/strategy/deathknight/DKActions.h | 4 +-- .../deathknight/DKAiObjectContext.cpp | 11 ++++++ src/strategy/deathknight/DKTriggers.cpp | 4 +-- src/strategy/deathknight/DKTriggers.h | 18 ++++++++-- src/strategy/deathknight/FrostDKStrategy.cpp | 5 ++- .../deathknight/GenericDKStrategy.cpp | 18 ---------- src/strategy/deathknight/UnholyDKStrategy.cpp | 36 +++++++++++++------ src/strategy/triggers/GenericTriggers.cpp | 11 +++++- src/strategy/triggers/GenericTriggers.h | 10 ++++-- 13 files changed, 98 insertions(+), 50 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 87008706..37228098 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -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 = --2301303050032151000150013113151 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151 +AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013132051 +AiPlayerbot.PremadeSpecLink.6.2.80 = -320043500002-2301303050032151000150013133051 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 diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 2425a223..9285f20a 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -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* CastDebuffSpellOnMeleeAttackerAction::GetTargetValue() return context->GetValue("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"); } diff --git a/src/strategy/actions/GenericSpellActions.h b/src/strategy/actions/GenericSpellActions.h index b9870011..2ab49bb0 100644 --- a/src/strategy/actions/GenericSpellActions.h +++ b/src/strategy/actions/GenericSpellActions.h @@ -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"; } }; diff --git a/src/strategy/deathknight/BloodDKStrategy.cpp b/src/strategy/deathknight/BloodDKStrategy.cpp index 7daf494d..03bc454c 100644 --- a/src/strategy/deathknight/BloodDKStrategy.cpp +++ b/src/strategy/deathknight/BloodDKStrategy.cpp @@ -104,5 +104,8 @@ void BloodDKStrategy::InitTriggers(std::vector& triggers) new NextAction("death strike", ACTION_HIGH + 3), 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))); } diff --git a/src/strategy/deathknight/DKActions.h b/src/strategy/deathknight/DKActions.h index 2fe9c708..355a5c52 100644 --- a/src/strategy/deathknight/DKActions.h +++ b/src/strategy/deathknight/DKActions.h @@ -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 diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 5f7eb941..45d4b65f 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -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; @@ -91,6 +96,7 @@ public: 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; } @@ -100,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); } @@ -128,6 +138,7 @@ private: 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); } }; diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index ebec33cc..2d1cbb15 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -53,7 +53,7 @@ bool HighUnholyRuneTrigger::IsActive() } bool DesolationTrigger::IsActive() { - return bot->HasAura(66817) && !botAI->HasAura("desolation", GetTarget(), false, true, -1, true); + return bot->HasAura(66817) && BuffTrigger::IsActive(); } bool DeathAndDecayCooldownTrigger::IsActive() @@ -62,5 +62,5 @@ bool DeathAndDecayCooldownTrigger::IsActive() if (!spellId) return true; - return bot->GetSpellCooldownDelay(spellId) >= 2000; + return bot->GetSpellCooldownDelay(spellId) >= 3000; } \ No newline at end of file diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index 7f6c6c81..ef82394b 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -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"); @@ -162,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; }; diff --git a/src/strategy/deathknight/FrostDKStrategy.cpp b/src/strategy/deathknight/FrostDKStrategy.cpp index b05060c7..6ec84558 100644 --- a/src/strategy/deathknight/FrostDKStrategy.cpp +++ b/src/strategy/deathknight/FrostDKStrategy.cpp @@ -109,7 +109,10 @@ void FrostDKStrategy::InitTriggers(std::vector& triggers) 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("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))); } diff --git a/src/strategy/deathknight/GenericDKStrategy.cpp b/src/strategy/deathknight/GenericDKStrategy.cpp index 5fb8556f..55146a56 100644 --- a/src/strategy/deathknight/GenericDKStrategy.cpp +++ b/src/strategy/deathknight/GenericDKStrategy.cpp @@ -184,24 +184,6 @@ void GenericDKStrategy::InitTriggers(std::vector& triggers) 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), diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index ac106a7f..c05d0d19 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -42,7 +42,7 @@ private: static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("ghoul frenzy", - /*P*/ nullptr, + /*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr), /*A*/ nullptr, /*C*/ nullptr); } @@ -78,11 +78,11 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI NextAction** UnholyDKStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("death and decay", ACTION_DEFAULT + 1.0f), + 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("death coil", ACTION_DEFAULT + 0.2f), - new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), + new NextAction("horn of winter", ACTION_DEFAULT + 0.2f), + new NextAction("death coil", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr); } @@ -95,24 +95,38 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) 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( - "high frost rune", NextAction::array(0, new NextAction("icy touch", ACTION_DEFAULT + 0.6f), nullptr))); + "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( - "high unholy rune", NextAction::array(0, - new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f), - new NextAction("plague strike", ACTION_DEFAULT + 0.5f), nullptr))); + "high frost rune", NextAction::array(0, + new NextAction("icy touch", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode( - "high blood rune", NextAction::array(0, new NextAction("blood boil", ACTION_DEFAULT + 0.7f), nullptr))); + "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("no desolation", - NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr))); triggers.push_back( new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 7826616f..f700e472 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -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* BuffOnPartyTrigger::GetTargetValue() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index b7217447..e9990c3e 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -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) { } From 95f0768a8ba1061d336c2496ba7cd96cb85e5afd Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 6 Sep 2024 20:51:36 +0800 Subject: [PATCH 4/7] Fix avoid aoe crash --- src/strategy/values/NearestGameObjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index 29b3e45a..a600bc65 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -77,7 +77,7 @@ GuidVector NearestTrapWithDamageValue::Calculate() continue; } const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo->IsPositive()) + if (!spellInfo || spellInfo->IsPositive()) { continue; } From 9d7a300c6d47def47cf25045bc2d43a53e251d5d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 00:04:37 +0800 Subject: [PATCH 5/7] Unholy dk --- conf/playerbots.conf.dist | 4 ++-- src/factory/StatsWeightCalculator.cpp | 8 ++++---- src/strategy/deathknight/UnholyDKStrategy.cpp | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 37228098..7888b6b4 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -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 = --2301303050032151000150013132051 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320043500002-2301303050032151000150013133051 +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 diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index f6270c73..e77ac102 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -223,10 +223,10 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) 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.4f; - stats_weights_[STATS_TYPE_HASTE] += 1.7f; + 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; } diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index c05d0d19..fb1baca4 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -101,10 +101,10 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) 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("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, @@ -117,11 +117,11 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) 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 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("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))); From d3f380d04b2d2b53daf91af424b729f121b7adf3 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 11:52:29 +0800 Subject: [PATCH 6/7] Spellcooldown and instance refresh on randomize --- src/factory/PlayerbotFactory.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 5b5eba2e..eb4ee127 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -207,7 +207,8 @@ void PlayerbotFactory::Randomize(bool incremental) { ClearAllItems(); } - // bot->SaveToDB(false, false); + bot->RemoveAllSpellCooldown(); + UnbindInstance(); bot->GiveLevel(level); bot->InitStatsForLevel(); From 311bdbd91b9f127226f29f0d9e9350c2ec806cbf Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 7 Sep 2024 12:08:38 +0800 Subject: [PATCH 7/7] Serveral crashes fix --- src/PlayerbotMgr.cpp | 4 +++- .../actions/ChooseRpgTargetAction.cpp | 16 +++++++-------- .../actions/UseMeetingStoneAction.cpp | 20 +++++++------------ 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 491e93ec..d1b92d7a 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -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++; } diff --git a/src/strategy/actions/ChooseRpgTargetAction.cpp b/src/strategy/actions/ChooseRpgTargetAction.cpp index dfd1cef5..4b00cfe5 100644 --- a/src/strategy/actions/ChooseRpgTargetAction.cpp +++ b/src/strategy/actions/ChooseRpgTargetAction.cpp @@ -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()) diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 0c0b7285..f5d61b01 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -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) {