From f0c6aaff0b824b3b0c676cd36697c34b80cc833c Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 5 Jul 2025 20:14:29 +0800 Subject: [PATCH] Random bots gear related enhancements --- conf/playerbots.conf.dist | 5 ++ src/AiFactory.cpp | 6 +- src/PlayerbotAIConfig.cpp | 2 + src/PlayerbotAIConfig.h | 1 + src/RandomPlayerbotMgr.cpp | 39 ++++++--- src/factory/PlayerbotFactory.cpp | 84 +++++++++++++++---- src/factory/StatsCollector.cpp | 16 ++-- src/factory/StatsCollector.h | 4 +- src/factory/StatsWeightCalculator.cpp | 31 ++++--- src/factory/StatsWeightCalculator.h | 1 + .../AutoMaintenanceOnLevelupAction.cpp | 3 +- src/strategy/actions/EquipAction.cpp | 3 +- src/strategy/actions/MovementActions.cpp | 5 +- src/strategy/mage/ArcaneMageStrategy.cpp | 2 +- src/strategy/mage/FireMageStrategy.cpp | 3 +- src/strategy/rpg/NewRpgBaseAction.cpp | 45 ++++++---- src/strategy/values/GrindTargetValue.cpp | 3 +- 17 files changed, 174 insertions(+), 79 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 793ad471..df684144 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -656,6 +656,11 @@ AiPlayerbot.RandomGearQualityLimit = 3 # Default: 0 (no limit) AiPlayerbot.RandomGearScoreLimit = 0 +# Equipment quality limitation for randombots (0 = disabled, 1 = enabled) +# If disabled, random bots can only upgrade equipment through looting and quests +# Default: 1 (enabled) +AiPlayerbot.IncrementalGearInit = 1 + # Set minimum level of bots that will enchant their equipment (Maxlevel + 1 to disable) # Default: 60 AiPlayerbot.MinEnchantingBotLevel = 60 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index adf53668..a40c7765 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -80,13 +80,13 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) switch (bot->getClass()) { case CLASS_MAGE: - tab = 1; + tab = MAGE_TAB_FROST; break; case CLASS_PALADIN: - tab = 2; + tab = PALADIN_TAB_RETRIBUTION; break; case CLASS_PRIEST: - tab = 1; + tab = PRIEST_TAB_HOLY; break; } diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 09ca801c..b05e9b7e 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -117,6 +117,8 @@ bool PlayerbotAIConfig::Initialize() tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", false); randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.0f); + + incrementalGearInit = sConfigMgr->GetOption("AiPlayerbot.IncrementalGearInit", true); randomGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearQualityLimit", 3); randomGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearScoreLimit", 0); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 721c75b5..a636ca21 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -87,6 +87,7 @@ public: std::vector randomBotQuestIds; uint32 randomBotTeleportDistance; float randomGearLoweringChance; + bool incrementalGearInit; int32 randomGearQualityLimit; int32 randomGearScoreLimit; float randomBotMinLevelChance, randomBotMaxLevelChance; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 5c9ce9f6..0168fb6a 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1699,7 +1699,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache() "position_y, " "position_z, " "orientation, " - "t.faction " + "t.faction, " + "t.entry " "FROM " "creature c " "INNER JOIN creature_template t on c.id1 = t.entry " @@ -1721,6 +1722,11 @@ void RandomPlayerbotMgr::PrepareTeleportCache() float z = fields[3].Get(); float orient = fields[4].Get(); uint32 faction = fields[5].Get(); + uint32 tEntry = fields[6].Get(); + + if (tEntry == 3838 || tEntry == 29480) + continue; + const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction); WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI); @@ -1788,13 +1794,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache() "AND t.npcflag != 135298 " "AND t.minlevel != 55 " "AND t.minlevel != 65 " - "AND t.faction != 35 " - "AND t.faction != 474 " - "AND t.faction != 69 " - "AND t.entry != 30606 " - "AND t.entry != 30608 " - "AND t.entry != 29282 " - "AND t.faction != 69 " + "AND t.faction not in (35, 474, 69, 57) " + "AND t.entry not in (30606, 30608, 29282) " "AND map IN ({}) " "ORDER BY " "t.minlevel;", @@ -2757,14 +2758,19 @@ void RandomPlayerbotMgr::PrintStats() std::map perRace; std::map perClass; + + std::map lvlPerRace; + std::map lvlPerClass; for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race) { perRace[race] = 0; + lvlPerRace[race] = 0; } for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls) { perClass[cls] = 0; + lvlPerClass[cls] = 0; } uint32 dps = 0; @@ -2802,6 +2808,9 @@ void RandomPlayerbotMgr::PrintStats() ++perRace[bot->getRace()]; ++perClass[bot->getClass()]; + lvlPerClass[bot->getClass()] += bot->GetLevel(); + lvlPerRace[bot->getRace()] += bot->GetLevel(); + PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI->AllowActivity()) ++active; @@ -2894,15 +2903,21 @@ void RandomPlayerbotMgr::PrintStats() LOG_INFO("playerbots", "Bots race:"); for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race) { - if (perRace[race]) - LOG_INFO("playerbots", " {}: {}", ChatHelper::FormatRace(race).c_str(), perRace[race]); + if (perRace[race]) { + uint32 lvl = lvlPerRace[race] * 10 / perRace[race]; + float flvl = lvl / 10.0f; + LOG_INFO("playerbots", " {}: {}, avg lvl: {}", ChatHelper::FormatRace(race).c_str(), perRace[race], flvl); + } } LOG_INFO("playerbots", "Bots class:"); for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls) { - if (perClass[cls]) - LOG_INFO("playerbots", " {}: {}", ChatHelper::FormatClass(cls).c_str(), perClass[cls]); + if (perClass[cls]) { + uint32 lvl = lvlPerClass[cls] * 10 / perClass[cls]; + float flvl = lvl / 10.0f; + LOG_INFO("playerbots", " {}: {}, avg lvl: {}", ChatHelper::FormatClass(cls).c_str(), perClass[cls], flvl); + } } LOG_INFO("playerbots", "Bots role:"); diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index ccdb1837..2fe6cdfa 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -224,24 +224,22 @@ void PlayerbotFactory::Randomize(bool incremental) { bot->resetTalents(true); } - // bot->SaveToDB(false, false); - ClearSkills(); - // bot->SaveToDB(false, false); - ClearSpells(); - // bot->SaveToDB(false, false); if (!incremental) { + ClearSkills(); + ClearSpells(); ResetQuests(); + if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) + { + ClearAllItems(); + } } - if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) - { - ClearAllItems(); - } + ClearInventory(); bot->RemoveAllSpellCooldown(); UnbindInstance(); bot->GiveLevel(level); - bot->InitStatsForLevel(); + bot->InitStatsForLevel(true); CancelAuras(); // bot->SaveToDB(false, false); if (pmo) @@ -280,7 +278,6 @@ void PlayerbotFactory::Randomize(bool incremental) LOG_DEBUG("playerbots", "Initializing skills (step 1)..."); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1"); InitSkills(); - // InitTradeSkills(); if (pmo) pmo->finish(); @@ -337,7 +334,8 @@ void PlayerbotFactory::Randomize(bool incremental) if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit); + if (sPlayerbotAIConfig->incrementalGearInit || !incremental) + InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit); } // bot->SaveToDB(false, false); if (pmo) @@ -1024,9 +1022,21 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa /// @todo: match current talent with template specTab = AiFactory::GetPlayerSpecTab(bot); /// @todo: fix cat druid hardcode - if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && - !bot->HasAura(16931)) - specTab = 3; + if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20) + { + bool isCat = !bot->HasAura(16931); + if (!isCat && bot->GetLevel() == 20) + { + uint32 bearP = sPlayerbotAIConfig->randomClassSpecProb[cls][1]; + uint32 catP = sPlayerbotAIConfig->randomClassSpecProb[cls][3]; + if (urand(1, bearP + catP) <= catP) + isCat = true; + } + if (isCat) + { + specTab = 3; + } + } } else { @@ -1599,9 +1609,51 @@ void Shuffle(std::vector& items) void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { + if (incremental && !sPlayerbotAIConfig->incrementalGearInit) + return; + + if (level < 5) { + // original items + if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(bot->getRace(), bot->getClass(), bot->getGender())) + { + for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j) + { + if (oEntry->ItemId[j] <= 0) + continue; + + uint32 itemId = oEntry->ItemId[j]; + + // skip hearthstone + if (itemId == 6948) + continue; + + // just skip, reported in ObjectMgr::LoadItemTemplates + ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId); + if (!iProto) + continue; + + // BuyCount by default + uint32 count = iProto->BuyCount; + + // special amount for food/drink + if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD) + { + continue; + } + + if (bot->HasItemCount(itemId, count)) { + continue; + } + + bot->StoreNewItemInBestSlots(itemId, count); + } + } + return; + } + std::unordered_map> items; // int tab = AiFactory::GetPlayerSpecTab(bot); - + uint32 blevel = bot->GetLevel(); int32 delta = std::min(blevel, 10u); diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 3ce59a71..4f719fea 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -29,12 +29,12 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto) { if (proto->IsRangedWeapon()) { - uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay; + float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay; stats[STATS_TYPE_RANGED_DPS] += val; } else if (proto->IsWeapon()) { - uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay; + float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay; stats[STATS_TYPE_MELEE_DPS] += val; } stats[STATS_TYPE_ARMOR] += proto->Armor; @@ -436,10 +436,10 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val) switch (itemStatType) { case ITEM_MOD_MANA: - stats[STATS_TYPE_MANA_REGENERATION] += val / 10; + stats[STATS_TYPE_MANA_REGENERATION] += (float)val / 10; break; case ITEM_MOD_HEALTH: - stats[STATS_TYPE_STAMINA] += val / 15; + stats[STATS_TYPE_STAMINA] += (float)val / 15; break; case ITEM_MOD_AGILITY: stats[STATS_TYPE_AGILITY] += val; @@ -747,11 +747,11 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu } } -int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) +float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) { // float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal. - int32 basePoints = effectInfo.BasePoints; - int32 randomPoints = int32(effectInfo.DieSides); + float basePoints = effectInfo.BasePoints; + int32 randomPoints = effectInfo.DieSides; switch (randomPoints) { @@ -761,7 +761,7 @@ int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) basePoints += 1; break; default: - int32 randvalue = (1 + randomPoints) / 2; + float randvalue = (1 + randomPoints) / 2.0f; basePoints += randvalue; break; } diff --git a/src/factory/StatsCollector.h b/src/factory/StatsCollector.h index 797d7150..a8e6bc54 100644 --- a/src/factory/StatsCollector.h +++ b/src/factory/StatsCollector.h @@ -71,7 +71,7 @@ public: bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true); public: - int32 stats[STATS_TYPE_MAX]; + float stats[STATS_TYPE_MAX]; private: void CollectByItemStatType(uint32 itemStatType, int32 val); @@ -80,7 +80,7 @@ private: void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, uint32 triggerCooldown); - int32 AverageValue(const SpellEffectInfo& effectInfo); + float AverageValue(const SpellEffectInfo& effectInfo); private: CollectorType type_; diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index 5c410494..69389b9c 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -33,6 +33,7 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) else type_ = CollectorType::RANGED; cls = player->getClass(); + lvl = player->GetLevel(); tab = AiFactory::GetPlayerSpecTab(player); collector_ = std::make_unique(type_, cls); @@ -70,7 +71,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId Reset(); collector_->CollectItemStats(proto); - + if (randomPropertyIds != 0) CalculateRandomProperty(randomPropertyIds, itemId); @@ -181,6 +182,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_ARMOR] += 0.001f; stats_weights_[STATS_TYPE_BONUS] += 1.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f; + stats_weights_[STATS_TYPE_RANGED_DPS] += 0.01f; if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL)) { @@ -529,13 +531,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) // enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) || (cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) || + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && + player_->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) { weight_ *= 0.1; } - } // spec with double hand // fury without duel wield, arms, bear, retribution, blood dk @@ -551,15 +553,11 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) weight_ *= 0.1; } // caster's main hand (cannot duel weapon but can equip two-hands stuff) - if (cls == CLASS_MAGE || - cls == CLASS_PRIEST || - cls == CLASS_WARLOCK || - cls == CLASS_DRUID || + if (cls == CLASS_MAGE || cls == CLASS_PRIEST || cls == CLASS_WARLOCK || cls == CLASS_DRUID || (cls == CLASS_SHAMAN && !player_->CanDualWield())) { weight_ *= 0.65; } - } // fury with titan's grip if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || @@ -568,16 +566,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 0.1; } - + if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) { weight_ *= 0.1; } - if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && - proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) + if (lvl >= 10 && cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && + proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) { - weight_ *= 0.5; + weight_ *= 1.5; } if (cls == CLASS_ROGUE && player_->HasAura(13964) && @@ -660,7 +658,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else validPoints = 0; } - collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints); + collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], validPoints); } { @@ -677,8 +675,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else validPoints = 0; - collector_->stats[STATS_TYPE_EXPERTISE] = - std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints); + collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], validPoints); } } @@ -695,7 +692,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else validPoints = 0; - collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints); + collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], validPoints); } } @@ -714,7 +711,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) validPoints = 0; collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = - std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints); + std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], validPoints); } } } diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index 10640d30..5b08cb18 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -57,6 +57,7 @@ private: CollectorType hitOverflowType_; std::unique_ptr collector_; uint8 cls; + uint8 lvl; int tab; bool enable_overflow_penalty_; bool enable_item_set_bonus_; diff --git a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp index c50e2e25..600c5d14 100644 --- a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp @@ -163,7 +163,8 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() PlayerbotFactory factory(bot, bot->GetLevel()); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - factory.InitEquipment(true); + if (sPlayerbotAIConfig->incrementalGearInit) + factory.InitEquipment(true); } factory.InitAmmo(); return; diff --git a/src/strategy/actions/EquipAction.cpp b/src/strategy/actions/EquipAction.cpp index a2a390b8..08e1d390 100644 --- a/src/strategy/actions/EquipAction.cpp +++ b/src/strategy/actions/EquipAction.cpp @@ -187,7 +187,8 @@ void EquipAction::EquipItem(Item* item) // Priority 1: Replace main hand if the new weapon is strictly better // and if conditions allow (e.g. no conflicting 2H logic) bool betterThanMH = (newItemScore > mainHandScore); - bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) || + // If a one-handed weapon is better, we can still use it instead of a two-handed weapon + bool mhConditionOK = (invType != INVTYPE_2HWEAPON || (isTwoHander && !canTitanGrip) || (canTitanGrip && isValidTGWeapon)); diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 29525332..d3e596e3 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1801,7 +1801,6 @@ const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y, bool FleeAction::Execute(Event event) { - // return Flee(AI_VALUE(Unit*, "current target")); return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true); } @@ -1811,6 +1810,10 @@ bool FleeAction::isUseful() { return false; } + Unit* target = AI_VALUE(Unit*, "current target"); + if (target && target->IsInWorld() && !bot->IsWithinMeleeRange(target)) + return false; + return true; } diff --git a/src/strategy/mage/ArcaneMageStrategy.cpp b/src/strategy/mage/ArcaneMageStrategy.cpp index a350fc74..5b585721 100644 --- a/src/strategy/mage/ArcaneMageStrategy.cpp +++ b/src/strategy/mage/ArcaneMageStrategy.cpp @@ -60,7 +60,7 @@ ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy NextAction** ArcaneMageStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f), - // new NextAction("arcane barrage", ACTION_DEFAULT + 0.2f), // cast during movement + new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // arcane immune target new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement new NextAction("shoot", ACTION_DEFAULT), nullptr); } diff --git a/src/strategy/mage/FireMageStrategy.cpp b/src/strategy/mage/FireMageStrategy.cpp index 1d3e132f..9d81beae 100644 --- a/src/strategy/mage/FireMageStrategy.cpp +++ b/src/strategy/mage/FireMageStrategy.cpp @@ -10,7 +10,8 @@ NextAction** FireMageStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.2f), + return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.3f), + new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // fire immune target new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement new NextAction("shoot", ACTION_DEFAULT), NULL); } diff --git a/src/strategy/rpg/NewRpgBaseAction.cpp b/src/strategy/rpg/NewRpgBaseAction.cpp index 8e3c70e5..7c50ed46 100644 --- a/src/strategy/rpg/NewRpgBaseAction.cpp +++ b/src/strategy/rpg/NewRpgBaseAction.cpp @@ -37,13 +37,6 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) botAI->rpgInfo.SetMoveFarTo(dest); } - float dis = bot->GetExactDist(dest); - if (dis < pathFinderDis) - { - return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false, - false, true); - } - // performance optimization if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL)) { @@ -70,6 +63,13 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), zone_name); return bot->TeleportTo(dest); } + + float dis = bot->GetExactDist(dest); + if (dis < pathFinderDis) + { + return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false, + false, true); + } float minDelta = M_PI; const float x = bot->GetPositionX(); @@ -852,10 +852,18 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) float loRange = 2500.0f; if (bot->GetLevel() < 5) { - hiRange /= 10; - loRange /= 10; + hiRange /= 3; + loRange /= 3; } std::vector lo_prepared_locs, hi_prepared_locs; + + bool inCity = false; + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetZoneId())) + { + if (zone->flags & AREA_FLAG_CAPITAL) + inCity = true; + } + for (auto& loc : locs) { if (bot->GetMapId() != loc.GetMapId()) @@ -863,17 +871,17 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) if (bot->GetExactDist(loc) > 2500.0f) continue; - - if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) != + + if (!inCity && bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) != bot->GetZoneId()) continue; - if (bot->GetExactDist(loc) < 500.0f) + if (bot->GetExactDist(loc) < hiRange) { hi_prepared_locs.push_back(loc); } - if (bot->GetExactDist(loc) < 2500.0f) + if (bot->GetExactDist(loc) < loRange) { lo_prepared_locs.push_back(loc); } @@ -900,6 +908,15 @@ WorldPosition NewRpgBaseAction::SelectRandomInnKeeperPos(Player* bot) const std::vector& locs = IsAlliance(bot->getRace()) ? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()] : sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()]; + + bool inCity = false; + + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetZoneId())) + { + if (zone->flags & AREA_FLAG_CAPITAL) + inCity = true; + } + std::vector prepared_locs; for (auto& loc : locs) { @@ -910,7 +927,7 @@ WorldPosition NewRpgBaseAction::SelectRandomInnKeeperPos(Player* bot) if (bot->GetExactDist(loc) > range) continue; - if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) != + if (!inCity && bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) != bot->GetZoneId()) continue; diff --git a/src/strategy/values/GrindTargetValue.cpp b/src/strategy/values/GrindTargetValue.cpp index da8fed80..71b5dbf6 100644 --- a/src/strategy/values/GrindTargetValue.cpp +++ b/src/strategy/values/GrindTargetValue.cpp @@ -116,12 +116,11 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) botAI->rpgInfo.status == RPG_GO_INNKEEPER || botAI->rpgInfo.status == RPG_DO_QUEST; - bool notHostile = !bot->IsHostileTo(unit); /*|| (unit->ToCreature() && unit->ToCreature()->IsCivilian());*/ float aggroRange = 30.0f; if (unit->ToCreature()) aggroRange = std::min(30.0f, unit->ToCreature()->GetAggroRange(bot) + 10.0f); bool outOfAggro = unit->ToCreature() && bot->GetDistance(unit) > aggroRange; - if (inactiveGrindStatus && (outOfAggro || notHostile)) + if (inactiveGrindStatus && outOfAggro) { if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end()) needForQuestMap[unit->GetEntry()] = needForQuest(unit);