Compare commits

..

No commits in common. "92fa97c3aa3d9391b895f525956b0f07678adcaa" and "4a63ee37e29f44010a0c926d33c114816abc2946" have entirely different histories.

26 changed files with 369 additions and 663 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'send_mail_disabled'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'send_mail_disabled'
);
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
(1899, 'send_mail_disabled', 'I cannot send mail', 0, 0, '우편을 보낼 수 없습니다', 'Je ne peux pas envoyer de courrier', 'Ich kann keine Post senden', '我不能寄送邮件', '我不能寄送郵件', 'No puedo enviar correo', 'No puedo enviar correo', 'Я не могу отправить почту');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('send_mail_disabled', 100);

View File

@ -12,8 +12,8 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/)
{ {
AutoPickTalents(); AutoPickTalents();
AutoLearnSpell(); AutoLearnSpell();
AutoTeleportForLevel();
AutoUpgradeEquip(); AutoUpgradeEquip();
AutoTeleportForLevel();
return true; return true;
} }
@ -21,11 +21,13 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/)
void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel() void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel()
{ {
if (!sPlayerbotAIConfig.autoTeleportForLevel || !sRandomPlayerbotMgr.IsRandomBot(bot)) if (!sPlayerbotAIConfig.autoTeleportForLevel || !sRandomPlayerbotMgr.IsRandomBot(bot))
{
return; return;
}
if (botAI->HasRealPlayerMaster()) if (botAI->HasRealPlayerMaster())
{
return; return;
}
sRandomPlayerbotMgr.RandomTeleportForLevel(bot); sRandomPlayerbotMgr.RandomTeleportForLevel(bot);
return; return;
} }
@ -87,17 +89,21 @@ void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
{ {
Quest const* quest = i->second; Quest const* quest = i->second;
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10 || // only process class-specific quests to learn class-related spells, cuz
quest->GetMinLevel() > bot->GetLevel()) // we don't want all these bunch of entries to be handled!
{ if (!quest->GetRequiredClasses())
continue; continue;
}
// skip quests that are repeatable, too low level, or above bots' level
if (quest->IsRepeatable() || quest->GetMinLevel() < 10 || quest->GetMinLevel() > bot->GetLevel())
continue;
// skip if bot doesnt satisfy class, race, or skill requirements
if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) || if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) ||
!bot->SatisfyQuestSkill(quest, false)) !bot->SatisfyQuestSkill(quest, false))
{
continue; continue;
}
// use the same logic and impl from Player::learnQuestRewardedSpells
int32 spellId = quest->GetRewSpellCast(); int32 spellId = quest->GetRewSpellCast();
if (!spellId) if (!spellId)
@ -107,26 +113,31 @@ void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
if (!spellInfo) if (!spellInfo)
continue; continue;
// xinef: find effect with learn spell and check if we have this spell
bool found = false; bool found = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{ {
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell && if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell &&
!bot->HasSpell(spellInfo->Effects[i].TriggerSpell)) !bot->HasSpell(spellInfo->Effects[i].TriggerSpell))
{ {
// pusywizard: don't re-add profession specialties!
if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell)) if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell))
if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL) if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL)
break; break; // pussywizard: break and not cast the spell (found is false)
found = true; found = true;
break; break;
} }
} }
// xinef: we know the spell, continue
if (!found) if (!found)
continue; continue;
bot->CastSpell(bot, spellId, true); bot->CastSpell(bot, spellId, true);
// Check if RewardDisplaySpell is set to output the proper spell learned
// after processing quests. Output the original RewardSpell otherwise.
uint32 rewSpellId = quest->GetRewSpell(); uint32 rewSpellId = quest->GetRewSpell();
if (rewSpellId) if (rewSpellId)
{ {
@ -156,11 +167,12 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
{ {
if (!sRandomPlayerbotMgr.IsRandomBot(bot)) if (!sPlayerbotAIConfig.autoUpgradeEquip || !sRandomPlayerbotMgr.IsRandomBot(bot))
return; return;
PlayerbotFactory factory(bot, bot->GetLevel()); PlayerbotFactory factory(bot, bot->GetLevel());
// Clean up old consumables before adding new ones
factory.CleanupConsumables(); factory.CleanupConsumables();
factory.InitAmmo(); factory.InitAmmo();
@ -169,6 +181,9 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
factory.InitConsumables(); factory.InitConsumables();
factory.InitPotions(); factory.InitPotions();
if (sPlayerbotAIConfig.autoUpgradeEquip) if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
factory.InitEquipment(true); {
if (sPlayerbotAIConfig.incrementalGearInit)
factory.InitEquipment(true);
}
} }

View File

@ -154,11 +154,9 @@ void EquipAction::EquipItem(Item* item)
calculator.SetOverflowPenalty(false); calculator.SetOverflowPenalty(false);
// Calculate item scores once and store them // Calculate item scores once and store them
float newItemScore = calculator.CalculateItem(itemId, item->GetItemRandomPropertyId()); float newItemScore = calculator.CalculateItem(itemId);
float mainHandScore = mainHandItem float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId, mainHandItem->GetItemRandomPropertyId()) : 0.0f; float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
float offHandScore = offHandItem
? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId, offHandItem->GetItemRandomPropertyId()) : 0.0f;
// Determine where this weapon can go // Determine where this weapon can go
bool canGoMain = (invType == INVTYPE_WEAPON || bool canGoMain = (invType == INVTYPE_WEAPON ||

View File

@ -6,7 +6,6 @@
#include "GenericSpellActions.h" #include "GenericSpellActions.h"
#include <ctime> #include <ctime>
#include <unordered_set>
#include "Event.h" #include "Event.h"
#include "ItemTemplate.h" #include "ItemTemplate.h"
@ -24,116 +23,6 @@
using ai::buff::MakeAuraQualifierForBuff; using ai::buff::MakeAuraQualifierForBuff;
using ai::spell::HasSpellOrCategoryCooldown; using ai::spell::HasSpellOrCategoryCooldown;
namespace
{
std::unordered_set<uint32> const& GetMixedTriggerTrinketSpellIds()
{
static std::unordered_set<uint32> const mixedTriggerSpellIds = []()
{
std::unordered_set<uint32> onUseSpellIds;
std::unordered_set<uint32> onEquipSpellIds;
std::unordered_set<uint32> mixedSpellIds;
auto const* itemTemplates = sObjectMgr->GetItemTemplateStore();
if (!itemTemplates)
return mixedSpellIds;
auto const markSpellId = [&](int32 spellId, uint8 spellTrigger)
{
if (spellId <= 0)
return;
if (spellTrigger == ITEM_SPELLTRIGGER_ON_USE)
{
if (onEquipSpellIds.find(spellId) != onEquipSpellIds.end())
mixedSpellIds.insert(spellId);
onUseSpellIds.insert(spellId);
}
else if (spellTrigger == ITEM_SPELLTRIGGER_ON_EQUIP)
{
if (onUseSpellIds.find(spellId) != onUseSpellIds.end())
mixedSpellIds.insert(spellId);
onEquipSpellIds.insert(spellId);
}
};
for (auto const& itr : *itemTemplates)
{
ItemTemplate const& proto = itr.second;
if (proto.InventoryType != INVTYPE_TRINKET)
continue;
for (uint8 spellIndex = 0; spellIndex < MAX_ITEM_PROTO_SPELLS; ++spellIndex)
{
auto const& spellData = proto.Spells[spellIndex];
markSpellId(spellData.SpellId, spellData.SpellTrigger);
}
}
return mixedSpellIds;
}();
return mixedTriggerSpellIds;
}
bool IsManaRestoreEffect(SpellEffectInfo const& effectInfo)
{
return (effectInfo.Effect == SPELL_EFFECT_ENERGIZE &&
effectInfo.MiscValue == POWER_MANA) ||
(effectInfo.Effect == SPELL_EFFECT_APPLY_AURA &&
effectInfo.ApplyAuraName == SPELL_AURA_PERIODIC_ENERGIZE &&
effectInfo.MiscValue == POWER_MANA);
}
bool IsManaEfficiencyEffect(SpellEffectInfo const& effectInfo)
{
return effectInfo.Effect == SPELL_EFFECT_APPLY_AURA &&
(((effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_REGEN ||
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_REGEN_PERCENT) &&
effectInfo.MiscValue == POWER_MANA) ||
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_COST_SCHOOL ||
effectInfo.ApplyAuraName == SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT ||
effectInfo.ApplyAuraName == SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
}
bool IsDefensiveTankEffect(SpellEffectInfo const& effectInfo)
{
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
return false;
uint32 const tankRatingsMask =
(1u << CR_DEFENSE_SKILL) |
(1u << CR_DODGE) |
(1u << CR_PARRY) |
(1u << CR_BLOCK) |
(1u << CR_HIT_TAKEN_MELEE) |
(1u << CR_HIT_TAKEN_RANGED) |
(1u << CR_HIT_TAKEN_SPELL) |
(1u << CR_CRIT_TAKEN_MELEE) |
(1u << CR_CRIT_TAKEN_RANGED) |
(1u << CR_CRIT_TAKEN_SPELL);
switch (effectInfo.ApplyAuraName)
{
case SPELL_AURA_MOD_RESISTANCE:
return (effectInfo.MiscValue & SPELL_SCHOOL_MASK_NORMAL) != 0;
case SPELL_AURA_MOD_RATING:
return (effectInfo.MiscValue & tankRatingsMask) != 0;
case SPELL_AURA_MOD_INCREASE_HEALTH:
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
case SPELL_AURA_MOD_PARRY_PERCENT:
case SPELL_AURA_MOD_DODGE_PERCENT:
case SPELL_AURA_MOD_BLOCK_PERCENT:
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
return true;
default:
return false;
}
}
}
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell) CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell) {} : Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell) {}
@ -540,109 +429,52 @@ bool UseTrinketAction::UseTrinket(Item* item)
uint8 bagIndex = item->GetBagSlot(); uint8 bagIndex = item->GetBagSlot();
uint8 slot = item->GetSlot(); uint8 slot = item->GetSlot();
// uint8 spell_index = 0; //not used, line marked for removal.
uint8 cast_count = 1; uint8 cast_count = 1;
ObjectGuid item_guid = item->GetGUID(); ObjectGuid item_guid = item->GetGUID();
uint32 glyphIndex = 0; uint32 glyphIndex = 0;
uint8 castFlags = 0; uint8 castFlags = 0;
uint32 targetFlag = TARGET_FLAG_NONE; uint32 targetFlag = TARGET_FLAG_NONE;
uint32 spellId = 0; uint32 spellId = 0;
int32 itemSpellCooldown = 0;
uint32 itemSpellCategory = 0;
int32 itemSpellCategoryCooldown = 0;
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{ {
if (item->GetTemplate()->Spells[i].SpellId > 0 && if (item->GetTemplate()->Spells[i].SpellId > 0 &&
item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
{ {
spellId = item->GetTemplate()->Spells[i].SpellId; spellId = item->GetTemplate()->Spells[i].SpellId;
itemSpellCooldown = item->GetTemplate()->Spells[i].SpellCooldown;
itemSpellCategory = item->GetTemplate()->Spells[i].SpellCategory;
itemSpellCategoryCooldown = item->GetTemplate()->Spells[i].SpellCategoryCooldown;
uint64 const itemCooldownKey = (static_cast<uint64>(item->GetEntry()) << 32) | spellId;
uint32 const now = getMSTime();
if (itemSpellCooldown > 0)
{
auto const itemCooldownItr = trinketItemCooldownExpiries.find(itemCooldownKey);
if (itemCooldownItr != trinketItemCooldownExpiries.end())
{
if (itemCooldownItr->second > now)
return false;
trinketItemCooldownExpiries.erase(itemCooldownItr);
}
}
if (itemSpellCategory && itemSpellCategoryCooldown > 0)
{
auto const categoryCooldownItr = trinketCategoryCooldownExpiries.find(itemSpellCategory);
if (categoryCooldownItr != trinketCategoryCooldownExpiries.end())
{
if (categoryCooldownItr->second > now)
return false;
trinketCategoryCooldownExpiries.erase(categoryCooldownItr);
}
}
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo || !spellInfo->IsPositive()) if (!spellInfo || !spellInfo->IsPositive())
return false; return false;
bool applyAura = false; bool applyAura = false;
bool restoresMana = false;
bool improvesManaEfficiency = false;
bool defensiveTankEffect = false;
for (int i = 0; i < MAX_SPELL_EFFECTS; i++) for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
{ {
const SpellEffectInfo& effectInfo = spellInfo->Effects[i]; const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA)
applyAura = true;
restoresMana = restoresMana || IsManaRestoreEffect(effectInfo);
improvesManaEfficiency = improvesManaEfficiency || IsManaEfficiencyEffect(effectInfo);
defensiveTankEffect = defensiveTankEffect || IsDefensiveTankEffect(effectInfo);
}
if (!applyAura && !restoresMana)
return false;
if (restoresMana || improvesManaEfficiency)
{
if (!AI_VALUE2(bool, "has mana", "self target"))
return false;
uint8 const manaPct = AI_VALUE2(uint8, "mana", "self target");
if ((restoresMana && manaPct >= sPlayerbotAIConfig.mediumMana) ||
manaPct >= sPlayerbotAIConfig.highMana)
{ {
return false; applyAura = true;
break;
} }
} }
if (defensiveTankEffect) if (!applyAura)
{
uint8 const healthPct = AI_VALUE2(uint8, "health", "self target");
if (healthPct > sPlayerbotAIConfig.lowHealth)
return false;
}
auto const& mixedTriggerTrinketSpellIds = GetMixedTriggerTrinketSpellIds();
// Exclude trinkets that expose the same spell as both ON_EQUIP and ON_USE across
// item templates. Those are equip/proc effects leaking into the active-use path,
// as seen with the error versions of Oracle Talisman of Ablution (44870) and
// Frenzyheart Insignia of Fury (44869).
if (mixedTriggerTrinketSpellIds.find(spellId) != mixedTriggerTrinketSpellIds.end())
return false; return false;
if (!botAI->CanCastSpell(spellId, bot, false, nullptr, item)) uint32 spellProcFlag = spellInfo->ProcFlags;
return false;
// Handle items with procflag "if you kill a target that grants honor or experience"
// Bots will "learn" the trinket proc, so CanCastSpell() will be true
// e.g. on Item https://www.wowhead.com/wotlk/item=44074/oracle-talisman-of-ablution leading to
// constant casting of the proc spell onto themselfes https://www.wowhead.com/wotlk/spell=59787/oracle-ablutions
// This will lead to multiple hundreds of entries in m_appliedAuras -> Once killing an enemy -> Big diff time spikes
if (spellProcFlag != 0) return false;
if (!botAI->CanCastSpell(spellId, bot, false))
return false;
break; break;
} }
} }
if (!spellId) if (!spellId)
return false; return false;
@ -651,25 +483,7 @@ bool UseTrinketAction::UseTrinket(Item* item)
targetFlag = TARGET_FLAG_NONE; targetFlag = TARGET_FLAG_NONE;
packet << targetFlag << bot->GetPackGUID(); packet << targetFlag << bot->GetPackGUID();
bot->GetSession()->HandleUseItemOpcode(packet); bot->GetSession()->HandleUseItemOpcode(packet);
uint32 const now = getMSTime();
uint32 const cooldownDelay = bot->GetSpellCooldownDelay(spellId);
if (cooldownDelay > 0)
{
if (itemSpellCooldown > 0)
{
uint64 const itemCooldownKey = (static_cast<uint64>(item->GetEntry()) << 32) | spellId;
trinketItemCooldownExpiries[itemCooldownKey] = now + static_cast<uint32>(itemSpellCooldown);
}
if (itemSpellCategory && itemSpellCategoryCooldown > 0)
{
trinketCategoryCooldownExpiries[itemSpellCategory] = now + static_cast<uint32>(itemSpellCategoryCooldown);
}
}
return true; return true;
} }

View File

@ -334,10 +334,6 @@ public:
protected: protected:
bool UseTrinket(Item* trinket); bool UseTrinket(Item* trinket);
private:
std::unordered_map<uint64, uint32> trinketItemCooldownExpiries;
std::unordered_map<uint32, uint32> trinketCategoryCooldownExpiries;
}; };
class CastSpellOnEnemyHealerAction : public CastSpellAction class CastSpellOnEnemyHealerAction : public CastSpellAction

View File

@ -44,21 +44,6 @@ bool ResetAiAction::Execute(Event event)
} }
} }
} }
if (Player* master = botAI->GetMaster())
{
Group* botGroup = bot->GetGroup();
Group* masterGroup = master->GetGroup();
if (botGroup && (!masterGroup || masterGroup != botGroup))
botAI->SetMaster(nullptr);
}
if (sRandomPlayerbotMgr.IsRandomBot(bot) && !bot->InBattleground())
{
if (bot->GetGroup() && (!botAI->GetMaster() || GET_PLAYERBOT_AI(botAI->GetMaster())))
{
if (Player* newMaster = botAI->FindNewMaster())
botAI->SetMaster(newMaster);
}
}
PlayerbotRepository::instance().Reset(botAI); PlayerbotRepository::instance().Reset(botAI);
botAI->ResetStrategies(false); botAI->ResetStrategies(false);
botAI->TellMaster("AI was reset to defaults"); botAI->TellMaster("AI was reset to defaults");

View File

@ -34,23 +34,24 @@ bool SendMailAction::Execute(Event event)
Player* receiver = GetMaster(); Player* receiver = GetMaster();
Player* tellTo = receiver; Player* tellTo = receiver;
std::vector<std::string> ss = split(text, ' ');
if (ss.size() > 1)
{
if (Player* p = ObjectAccessor::FindPlayer(ObjectGuid(uint64(ss[ss.size() - 1].c_str()))))
receiver = p;
}
if (!receiver) if (!receiver)
receiver = event.getOwner(); receiver = event.getOwner();
if (!receiver || receiver == bot) if (!receiver || receiver == bot)
{
return false; return false;
}
if (!tellTo) if (!tellTo)
tellTo = receiver; tellTo = receiver;
if (!sPlayerbotAIConfig.botSendMailEnabled)
{
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(
"send_mail_disabled", "I cannot send mail", {}),
LANG_UNIVERSAL, tellTo);
return false;
}
if (!mailboxFound && !randomBot) if (!mailboxFound && !randomBot)
{ {
bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault(

View File

@ -16,7 +16,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back( triggers.push_back(
new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) })); new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) }));
triggers.push_back( triggers.push_back(
new TriggerNode("group set leader", { NextAction("reset botAI", relevance) })); new TriggerNode("group set leader", { /*NextAction("leader", relevance),*/ }));
triggers.push_back(new TriggerNode( triggers.push_back(new TriggerNode(
"not enough money", { NextAction("tell not enough money", relevance) })); "not enough money", { NextAction("tell not enough money", relevance) }));
triggers.push_back( triggers.push_back(

View File

@ -38,16 +38,3 @@ bool BwlUseHourglassSandAction::Execute(Event /*event*/)
{ {
return botAI->CastSpell(SPELL_HOURGLASS_SAND, bot); return botAI->CastSpell(SPELL_HOURGLASS_SAND, bot);
} }
bool BwlNefarianFearWardAction::Execute(Event /*event*/)
{
Unit* nefarian = AI_VALUE2(Unit*, "find target", "nefarian");
if (!nefarian)
return false;
Unit* victim = nefarian->GetVictim();
if (!victim)
return false;
return botAI->CastSpell("fear ward", victim);
}

View File

@ -29,11 +29,4 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class BwlNefarianFearWardAction : public Action
{
public:
BwlNefarianFearWardAction(PlayerbotAI* botAI) : Action(botAI, "bwl nefarian fear ward") {}
bool Execute(Event event) override;
};
#endif #endif

View File

@ -13,14 +13,12 @@ public:
creators["bwl check onyxia scale cloak"] = &RaidBwlActionContext::bwl_check_onyxia_scale_cloak; creators["bwl check onyxia scale cloak"] = &RaidBwlActionContext::bwl_check_onyxia_scale_cloak;
creators["bwl turn off suppression device"] = &RaidBwlActionContext::bwl_turn_off_suppression_device; creators["bwl turn off suppression device"] = &RaidBwlActionContext::bwl_turn_off_suppression_device;
creators["bwl use hourglass sand"] = &RaidBwlActionContext::bwl_use_hourglass_sand; creators["bwl use hourglass sand"] = &RaidBwlActionContext::bwl_use_hourglass_sand;
creators["bwl nefarian fear ward"] = &RaidBwlActionContext::bwl_nefarian_fear_ward;
} }
private: private:
static Action* bwl_check_onyxia_scale_cloak(PlayerbotAI* botAI) { return new BwlOnyxiaScaleCloakAuraCheckAction(botAI); } static Action* bwl_check_onyxia_scale_cloak(PlayerbotAI* botAI) { return new BwlOnyxiaScaleCloakAuraCheckAction(botAI); }
static Action* bwl_turn_off_suppression_device(PlayerbotAI* botAI) { return new BwlTurnOffSuppressionDeviceAction(botAI); } static Action* bwl_turn_off_suppression_device(PlayerbotAI* botAI) { return new BwlTurnOffSuppressionDeviceAction(botAI); }
static Action* bwl_use_hourglass_sand(PlayerbotAI* botAI) { return new BwlUseHourglassSandAction(botAI); } static Action* bwl_use_hourglass_sand(PlayerbotAI* botAI) { return new BwlUseHourglassSandAction(botAI); }
static Action* bwl_nefarian_fear_ward(PlayerbotAI* botAI) { return new BwlNefarianFearWardAction(botAI); }
}; };
#endif #endif

View File

@ -11,15 +11,11 @@ public:
{ {
creators["bwl suppression device"] = &RaidBwlTriggerContext::bwl_suppression_device; creators["bwl suppression device"] = &RaidBwlTriggerContext::bwl_suppression_device;
creators["bwl affliction bronze"] = &RaidBwlTriggerContext::bwl_affliction_bronze; creators["bwl affliction bronze"] = &RaidBwlTriggerContext::bwl_affliction_bronze;
creators["bwl wild magic"] = &RaidBwlTriggerContext::bwl_wild_magic;
creators["bwl nefarian fear ward"] = &RaidBwlTriggerContext::bwl_nefarian_fear_ward;
} }
private: private:
static Trigger* bwl_suppression_device(PlayerbotAI* ai) { return new BwlSuppressionDeviceTrigger(ai); } static Trigger* bwl_suppression_device(PlayerbotAI* ai) { return new BwlSuppressionDeviceTrigger(ai); }
static Trigger* bwl_affliction_bronze(PlayerbotAI* ai) { return new BwlAfflictionBronzeTrigger(ai); } static Trigger* bwl_affliction_bronze(PlayerbotAI* ai) { return new BwlAfflictionBronzeTrigger(ai); }
static Trigger* bwl_wild_magic(PlayerbotAI* ai) { return new BwlWildMagicTrigger(ai); }
static Trigger* bwl_nefarian_fear_ward(PlayerbotAI* ai) { return new BwlNefarianFearWardTrigger(ai); }
}; };
#endif #endif

View File

@ -10,10 +10,4 @@ void RaidBwlStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("bwl affliction bronze", { triggers.push_back(new TriggerNode("bwl affliction bronze", {
NextAction("bwl use hourglass sand", ACTION_RAID) })); NextAction("bwl use hourglass sand", ACTION_RAID) }));
triggers.push_back(new TriggerNode("bwl wild magic", {
NextAction("ice block", ACTION_RAID) }));
triggers.push_back(new TriggerNode("bwl nefarian fear ward", {
NextAction("bwl nefarian fear ward", ACTION_RAID) }));
} }

View File

@ -27,24 +27,3 @@ bool BwlAfflictionBronzeTrigger::IsActive()
{ {
return bot->HasAura(SPELL_BROOD_AFFLICTION_BRONZE); return bot->HasAura(SPELL_BROOD_AFFLICTION_BRONZE);
} }
bool BwlWildMagicTrigger::IsActive()
{
return bot->getClass() == CLASS_MAGE && bot->HasAura(SPELL_WILD_MAGIC);
}
bool BwlNefarianFearWardTrigger::IsActive()
{
if (bot->getClass() != CLASS_PRIEST)
return false;
Unit* nefarian = AI_VALUE2(Unit*, "find target", "nefarian");
if (!nefarian || !nefarian->IsInCombat())
return false;
Unit* victim = nefarian->GetVictim();
if (!victim)
return false;
return !botAI->HasAura("fear ward", victim);
}

View File

@ -21,20 +21,4 @@ public:
bool IsActive() override; bool IsActive() override;
}; };
// Nefarian
class BwlWildMagicTrigger : public Trigger
{
public:
BwlWildMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "bwl wild magic") {}
bool IsActive() override;
};
class BwlNefarianFearWardTrigger : public Trigger
{
public:
BwlNefarianFearWardTrigger(PlayerbotAI* botAI) : Trigger(botAI, "bwl nefarian fear ward") {}
bool IsActive() override;
};
#endif #endif

View File

@ -12,10 +12,7 @@ namespace BlackwingLairHelpers
// Chromaggus // Chromaggus
SPELL_BROOD_AFFLICTION_BRONZE = 23170, SPELL_BROOD_AFFLICTION_BRONZE = 23170,
SPELL_HOURGLASS_SAND = 23645, SPELL_HOURGLASS_SAND = 23645
// Nefarian
SPELL_WILD_MAGIC = 23410
}; };
enum BlackwingLairGameObjects enum BlackwingLairGameObjects

View File

@ -552,8 +552,9 @@ void PlayerbotFactory::Prepare()
void PlayerbotFactory::Randomize(bool incremental) void PlayerbotFactory::Randomize(bool incremental)
{ {
// if (sPlayerbotAIConfig.disableRandomLevels) // if (sPlayerbotAIConfig.disableRandomLevels)
// {
// return; // return;
// }
LOG_DEBUG("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"), LOG_DEBUG("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"),
bot->GetName().c_str(), level, bot->getClass()); bot->GetName().c_str(), level, bot->getClass());
// LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); // LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full"));
@ -561,22 +562,16 @@ void PlayerbotFactory::Randomize(bool incremental)
LOG_DEBUG("playerbots", "Resetting player..."); LOG_DEBUG("playerbots", "Resetting player...");
PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset");
if (!sPlayerbotAIConfig.equipAndSpecPersistence || if (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel)
level < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
{
bot->resetTalents(true); bot->resetTalents(true);
}
if (!incremental) if (!incremental)
{ {
ClearSkills(); ClearSkills();
ClearSpells(); ClearSpells();
ResetQuests(); ResetQuests();
if (!sPlayerbotAIConfig.equipAndSpecPersistence || if (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel)
level < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
{
ClearAllItems(); ClearAllItems();
}
} }
ClearInventory(); ClearInventory();
bot->RemoveAllSpellCooldown(); bot->RemoveAllSpellCooldown();
@ -627,8 +622,8 @@ void PlayerbotFactory::Randomize(bool incremental)
pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents"); pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents");
LOG_DEBUG("playerbots", "Initializing talents..."); LOG_DEBUG("playerbots", "Initializing talents...");
if (!incremental || !sPlayerbotAIConfig.equipAndSpecPersistence || if (!incremental || !sPlayerbotAIConfig.equipmentPersistence ||
bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel) bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
{ {
uint32 specIndex = InitTalentsTree(); uint32 specIndex = InitTalentsTree();
sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specIndex + 1); sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specIndex + 1);
@ -675,10 +670,11 @@ void PlayerbotFactory::Randomize(bool incremental)
pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip"); pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip");
LOG_DEBUG("playerbots", "Initializing equipmemt..."); LOG_DEBUG("playerbots", "Initializing equipmemt...");
if (!incremental || !sPlayerbotAIConfig.equipAndSpecPersistence || if (!incremental || !sPlayerbotAIConfig.equipmentPersistence ||
bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel) bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
{ {
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig.twoRoundsGearInit); if (sPlayerbotAIConfig.incrementalGearInit || !incremental)
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig.twoRoundsGearInit);
} }
// bot->SaveToDB(false, false); // bot->SaveToDB(false, false);
if (pmo) if (pmo)
@ -815,8 +811,7 @@ void PlayerbotFactory::Randomize(bool incremental)
void PlayerbotFactory::Refresh() void PlayerbotFactory::Refresh()
{ {
// Prepare(); // Prepare();
// if (!sPlayerbotAIConfig.equipAndSpecPersistence || // if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
// bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
// { // {
// InitEquipment(true); // InitEquipment(true);
// } // }
@ -836,13 +831,14 @@ void PlayerbotFactory::Refresh()
InitSpecialSpells(); InitSpecialSpells();
InitMounts(); InitMounts();
InitKeyring(); InitKeyring();
if (!sPlayerbotAIConfig.equipAndSpecPersistence || if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
{ {
InitTalentsTree(true, true, true); InitTalentsTree(true, true, true);
} }
if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
{
ApplyEnchantAndGemsNew(); ApplyEnchantAndGemsNew();
}
bot->DurabilityRepairAll(false, 1.0f, false); bot->DurabilityRepairAll(false, 1.0f, false);
if (bot->isDead()) if (bot->isDead())
bot->ResurrectPlayer(1.0f, false); bot->ResurrectPlayer(1.0f, false);
@ -2041,6 +2037,9 @@ void Shuffle(std::vector<uint32>& items)
void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
{ {
if (incremental && !sPlayerbotAIConfig.incrementalGearInit)
return;
if (level < 5) if (level < 5)
{ {
// original items // original items
@ -2082,7 +2081,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
return; return;
} }
std::unordered_map<uint8, std::vector<std::pair<uint32, int32>>> items; std::unordered_map<uint8, std::vector<uint32>> items;
// int tab = AiFactory::GetPlayerSpecTab(bot); // int tab = AiFactory::GetPlayerSpecTab(bot);
uint32 blevel = bot->GetLevel(); uint32 blevel = bot->GetLevel();
@ -2229,17 +2228,13 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE && if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE &&
proto->Class != ITEM_CLASS_WEAPON) proto->Class != ITEM_CLASS_WEAPON)
continue; continue;
items[slot].push_back(itemId);
int32 bestRandomProp = 0;
if (proto->RandomProperty || proto->RandomSuffix)
bestRandomProp = calculator.PickBestRandomPropertyId(itemId);
items[slot].push_back({itemId, bestRandomProp});
} }
} }
} }
} while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_POOR); } while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_POOR);
std::vector<std::pair<uint32, int32>>& ids = items[slot]; std::vector<uint32>& ids = items[slot];
if (ids.empty()) if (ids.empty())
{ {
continue; continue;
@ -2247,15 +2242,13 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
float bestScoreForSlot = -1; float bestScoreForSlot = -1;
uint32 bestItemForSlot = 0; uint32 bestItemForSlot = 0;
int32 bestRandomPropForSlot = 0;
for (int index = 0; index < ids.size(); index++) for (int index = 0; index < ids.size(); index++)
{ {
uint32 newItemId = ids[index].first; uint32 newItemId = ids[index];
int32 newItemProp = ids[index].second;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
float cur_score = calculator.CalculateItem(newItemId, newItemProp, slot); float cur_score = calculator.CalculateItem(newItemId, 0, slot);
if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType) if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType)
{ {
@ -2274,7 +2267,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
continue; continue;
bestScoreForSlot = cur_score; bestScoreForSlot = cur_score;
bestItemForSlot = newItemId; bestItemForSlot = newItemId;
bestRandomPropForSlot = newItemProp;
} }
} }
@ -2312,16 +2304,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (oldItem) if (oldItem)
continue; continue;
if (Item* equipped = bot->EquipNewItem(dest, bestItemForSlot, true)) bot->EquipNewItem(dest, bestItemForSlot, true);
{
if (bestRandomPropForSlot != 0)
{
uint8 equipSlot = equipped->GetSlot();
bot->_ApplyItemMods(equipped, equipSlot, false);
equipped->SetItemRandomProperties(bestRandomPropForSlot);
bot->_ApplyItemMods(equipped, equipSlot, true);
}
}
bot->AutoUnequipOffhandIfNeed(); bot->AutoUnequipOffhandIfNeed();
// if (newItem) // if (newItem)
// { // {
@ -2362,21 +2345,19 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
std::vector<std::pair<uint32, int32>>& ids = items[slot]; std::vector<uint32>& ids = items[slot];
if (ids.empty()) if (ids.empty())
continue; continue;
float bestScoreForSlot = -1; float bestScoreForSlot = -1;
uint32 bestItemForSlot = 0; uint32 bestItemForSlot = 0;
int32 bestRandomPropForSlot = 0;
for (int index = 0; index < ids.size(); index++) for (int index = 0; index < ids.size(); index++)
{ {
uint32 newItemId = ids[index].first; uint32 newItemId = ids[index];
int32 newItemProp = ids[index].second;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
float cur_score = calculator.CalculateItem(newItemId, newItemProp, slot); float cur_score = calculator.CalculateItem(newItemId, 0, slot);
if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType) if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType)
{ {
@ -2395,7 +2376,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
continue; continue;
bestScoreForSlot = cur_score; bestScoreForSlot = cur_score;
bestItemForSlot = newItemId; bestItemForSlot = newItemId;
bestRandomPropForSlot = newItemProp;
} }
} }
@ -2406,16 +2386,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
continue; continue;
if (Item* equipped = bot->EquipNewItem(dest, bestItemForSlot, true)) bot->EquipNewItem(dest, bestItemForSlot, true);
{
if (bestRandomPropForSlot != 0)
{
uint8 equipSlot = equipped->GetSlot();
bot->_ApplyItemMods(equipped, equipSlot, false);
equipped->SetItemRandomProperties(bestRandomPropForSlot);
bot->_ApplyItemMods(equipped, equipSlot, true);
}
}
bot->AutoUnequipOffhandIfNeed(); bot->AutoUnequipOffhandIfNeed();
} }
} }

View File

@ -22,7 +22,6 @@
#include "GuildMgr.h" #include "GuildMgr.h"
#include "ObjectAccessor.h" #include "ObjectAccessor.h"
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "ObjectMgr.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "PlayerbotRepository.h" #include "PlayerbotRepository.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
@ -482,6 +481,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
} }
Player* master = botAI->GetMaster(); Player* master = botAI->GetMaster();
if (master)
{
ObjectGuid masterGuid = master->GetGUID();
if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid))
master->GetGroup()->ChangeLeader(masterGuid);
}
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (group) if (group)
@ -732,10 +737,7 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
bool addClassBot = sRandomPlayerbotMgr.IsAddclassBot(guid.GetCounter()); bool addClassBot = sRandomPlayerbotMgr.IsAddclassBot(guid.GetCounter());
if (!addClassBot) if (!addClassBot)
{ return "ERROR: You can not use this command on non-addclass bot.";
if (!(cmd == "refresh=raid" && sPlayerbotAIConfig.resetInstanceIdForAltBots))
return "ERROR: You can only use this command on addclass bots.";
}
if (!admin) if (!admin)
{ {
@ -1244,7 +1246,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
std::vector<std::string> chars = split(charnameStr, ','); std::vector<std::string> chars = split(charnameStr, ',');
for (std::vector<std::string>::iterator i = chars.begin(); i != chars.end(); i++) for (std::vector<std::string>::iterator i = chars.begin(); i != chars.end(); i++)
{ {
std::string s = *i; std::string const s = *i;
if (!strcmp(cmd, "addaccount")) if (!strcmp(cmd, "addaccount"))
{ {
@ -1253,13 +1255,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (!accountId) if (!accountId)
{ {
// If not found, try to get account ID from character name // If not found, try to get account ID from character name
std::string charName = s; ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
if (!normalizePlayerName(charName))
{
messages.push_back("Neither account nor character '" + s + "' found");
continue;
}
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(charName);
if (!charGuid) if (!charGuid)
{ {
messages.push_back("Neither account nor character '" + s + "' found"); messages.push_back("Neither account nor character '" + s + "' found");
@ -1287,11 +1283,6 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
else else
{ {
// For regular add command, only add the specific character // For regular add command, only add the specific character
if (!normalizePlayerName(s))
{
messages.push_back("Character '" + *i + "' not found");
continue;
}
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s); ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
if (!charGuid) if (!charGuid)
{ {

View File

@ -160,7 +160,6 @@ void RandomItemMgr::Init()
BuildPotionCache(); BuildPotionCache();
BuildFoodCache(); BuildFoodCache();
BuildTradeCache(); BuildTradeCache();
LoadEnchantmentPool();
} }
void RandomItemMgr::InitAfterAhBot() void RandomItemMgr::InitAfterAhBot()
@ -453,39 +452,6 @@ std::vector<uint32> RandomItemMgr::GetCachedEquipments(uint32 requiredLevel, uin
return equipCacheNew[requiredLevel][inventoryType]; return equipCacheNew[requiredLevel][inventoryType];
} }
void RandomItemMgr::LoadEnchantmentPool()
{
enchPoolCache.clear();
QueryResult result = WorldDatabase.Query("SELECT entry, ench FROM item_enchantment_template");
if (!result)
{
LOG_WARN("playerbots", "item_enchantment_template empty; bot autogear cannot evaluate random suffixes");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 entry = fields[0].Get<uint32>();
uint32 ench = fields[1].Get<uint32>();
enchPoolCache[entry].push_back(ench);
++count;
} while (result->NextRow());
LOG_INFO("playerbots", "Loaded {} item enchantment pool rows for bot autogear", count);
}
std::vector<uint32> const& RandomItemMgr::GetEnchantmentPool(uint32 entry) const
{
static std::vector<uint32> const empty;
auto it = enchPoolCache.find(entry);
if (it == enchPoolCache.end())
return empty;
return it->second;
}
bool RandomItemMgr::ShouldEquipArmorForSpec(uint8 playerclass, uint8 spec, ItemTemplate const* proto) bool RandomItemMgr::ShouldEquipArmorForSpec(uint8 playerclass, uint8 spec, ItemTemplate const* proto)
{ {
if (proto->InventoryType == INVTYPE_TABARD) if (proto->InventoryType == INVTYPE_TABARD)

View File

@ -8,7 +8,6 @@
#include <map> #include <map>
#include <set> #include <set>
#include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -173,11 +172,9 @@ public:
static bool IsUsedBySkill(ItemTemplate const* proto, uint32 skillId); static bool IsUsedBySkill(ItemTemplate const* proto, uint32 skillId);
bool IsTestItem(uint32 itemId) { return itemForTest.find(itemId) != itemForTest.end(); } bool IsTestItem(uint32 itemId) { return itemForTest.find(itemId) != itemForTest.end(); }
std::vector<uint32> GetCachedEquipments(uint32 requiredLevel, uint32 inventoryType); std::vector<uint32> GetCachedEquipments(uint32 requiredLevel, uint32 inventoryType);
std::vector<uint32> const& GetEnchantmentPool(uint32 entry) const;
private: private:
void BuildRandomItemCache(); void BuildRandomItemCache();
void LoadEnchantmentPool();
void BuildEquipCache(); void BuildEquipCache();
void BuildEquipCacheNew(); void BuildEquipCacheNew();
void BuildItemInfoCache(); void BuildItemInfoCache();
@ -220,8 +217,6 @@ private:
static std::set<uint32> itemCache; static std::set<uint32> itemCache;
// equipCacheNew[RequiredLevel][InventoryType] // equipCacheNew[RequiredLevel][InventoryType]
std::map<uint32, std::map<uint32, std::vector<uint32>>> equipCacheNew; std::map<uint32, std::map<uint32, std::vector<uint32>>> equipCacheNew;
// enchPoolCache[item_enchantment_template.entry] -> list of enchantment ids
std::unordered_map<uint32, std::vector<uint32>> enchPoolCache;
}; };
#define sRandomItemMgr RandomItemMgr::instance() #define sRandomItemMgr RandomItemMgr::instance()

View File

@ -14,7 +14,6 @@
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "PlayerbotAI.h" #include "PlayerbotAI.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
#include "RandomItemMgr.h"
#include "SharedDefines.h" #include "SharedDefines.h"
#include "SpellAuraDefines.h" #include "SpellAuraDefines.h"
#include "SpellMgr.h" #include "SpellMgr.h"
@ -191,53 +190,6 @@ void StatsWeightCalculator::CalculateRandomProperty(int32 randomPropertyId, uint
} }
} }
int32 StatsWeightCalculator::PickBestRandomPropertyId(uint32 itemId)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (!proto)
return 0;
bool isSuffix = false;
uint32 poolEntry = proto->RandomProperty;
if (!poolEntry)
{
poolEntry = proto->RandomSuffix;
isSuffix = true;
}
if (!poolEntry)
return 0;
std::vector<uint32> const& pool = sRandomItemMgr.GetEnchantmentPool(poolEntry);
if (pool.empty())
return 0;
Reset();
GenerateWeights(player_);
int32 bestId = 0;
float bestScore = 0.0f;
for (uint32 enchId : pool)
{
int32 candidate = isSuffix ? -static_cast<int32>(enchId) : static_cast<int32>(enchId);
collector_->Reset();
CalculateRandomProperty(candidate, itemId);
float score = 0.0f;
for (uint32 i = 0; i < STATS_TYPE_MAX; ++i)
score += stats_weights_[i] * collector_->stats[i];
if (bestId == 0 || score > bestScore)
{
bestId = candidate;
bestScore = score;
}
}
collector_->Reset();
return bestId;
}
void StatsWeightCalculator::GenerateWeights(Player* player) void StatsWeightCalculator::GenerateWeights(Player* player)
{ {
GenerateBasicWeights(player); GenerateBasicWeights(player);

View File

@ -30,7 +30,6 @@ public:
void Reset(); void Reset();
float CalculateItem(uint32 itemId, int32 randomPropertyId = 0, int32 slot = -1); float CalculateItem(uint32 itemId, int32 randomPropertyId = 0, int32 slot = -1);
float CalculateEnchant(uint32 enchantId); float CalculateEnchant(uint32 enchantId);
int32 PickBestRandomPropertyId(uint32 itemId);
void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; } void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; }
void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; } void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; }

View File

@ -147,6 +147,7 @@ bool PlayerbotAIConfig::Initialize()
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false); tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f); randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
incrementalGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.IncrementalGearInit", true);
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3); randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0); randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
preferClassArmorType = sConfigMgr->GetOption<bool>("AiPlayerbot.PreferClassArmorType", false); preferClassArmorType = sConfigMgr->GetOption<bool>("AiPlayerbot.PreferClassArmorType", false);
@ -214,7 +215,7 @@ bool PlayerbotAIConfig::Initialize()
attunementQuests); attunementQuests);
LoadSet<std::set<uint32>>( LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,44869,44870,46978"), sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,46978"),
unobtainableItems); unobtainableItems);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false); botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
@ -351,27 +352,32 @@ bool PlayerbotAIConfig::Initialize()
// does not depend on global chance // does not depend on global chance
broadcastChanceGuildManagement = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceGuildManagement", 30000); broadcastChanceGuildManagement = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceGuildManagement", 30000);
////////////////////////////
toxicLinksRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ToxicLinksRepliesChance", 30); // 0-100 toxicLinksRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ToxicLinksRepliesChance", 30); // 0-100
thunderfuryRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ThunderfuryRepliesChance", 40); // 0-100 thunderfuryRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ThunderfuryRepliesChance", 40); // 0-100
guildRepliesRate = sConfigMgr->GetOption<int32>("AiPlayerbot.GuildRepliesRate", 100); // 0-100 guildRepliesRate = sConfigMgr->GetOption<int32>("AiPlayerbot.GuildRepliesRate", 100); // 0-100
suggestDungeonsInLowerCaseRandomly =
sConfigMgr->GetOption<bool>("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false);
////////////////////////// !CHAT
randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true); randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true);
randomBotAutoJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutoJoinBG", false); randomBotAutoJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutoJoinBG", false);
randomBotAutoJoinArenaBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinArenaBracket", 14); randomBotAutoJoinArenaBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinArenaBracket", 7);
randomBotAutoJoinWSBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinWSBrackets", "7"); randomBotAutoJoinICBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinICBrackets", "0,1");
randomBotAutoJoinABBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinABBrackets", "6"); randomBotAutoJoinEYBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinEYBrackets", "0,1,2");
randomBotAutoJoinAVBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinAVBrackets", "3"); randomBotAutoJoinAVBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinAVBrackets", "0,1,2,3");
randomBotAutoJoinEYBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinEYBrackets", "2"); randomBotAutoJoinABBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinABBrackets", "0,1,2,3,4,5,6");
randomBotAutoJoinICBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinICBrackets", "1"); randomBotAutoJoinWSBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinWSBrackets", "0,1,2,3,4,5,6,7");
randomBotAutoJoinBGWSCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWSCount", 1);
randomBotAutoJoinBGABCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGABCount", 1);
randomBotAutoJoinBGAVCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGAVCount", 0);
randomBotAutoJoinBGEYCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGEYCount", 1);
randomBotAutoJoinBGICCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGICCount", 0); randomBotAutoJoinBGICCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGICCount", 0);
randomBotAutoJoinBGEYCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGEYCount", 0);
randomBotAutoJoinBGAVCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGAVCount", 0);
randomBotAutoJoinBGABCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGABCount", 0);
randomBotAutoJoinBGWSCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWSCount", 0);
randomBotAutoJoinBGRatedArena2v2Count = randomBotAutoJoinBGRatedArena2v2Count =
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count", 0); sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count", 0);
@ -387,6 +393,7 @@ bool PlayerbotAIConfig::Initialize()
randomBotMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLevel", 80); randomBotMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLevel", 80);
if (randomBotMaxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) if (randomBotMaxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
randomBotMaxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); randomBotMaxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
randomBotLoginAtStartup = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotLoginAtStartup", true);
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 1); randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 1);
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 3); randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 3);
openGoSpell = sConfigMgr->GetOption<int32>("AiPlayerbot.OpenGoSpell", 6477); openGoSpell = sConfigMgr->GetOption<int32>("AiPlayerbot.OpenGoSpell", 6477);
@ -552,8 +559,6 @@ bool PlayerbotAIConfig::Initialize()
randomBotGuildSizeMax = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildSizeMax", 15); randomBotGuildSizeMax = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildSizeMax", 15);
deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false); deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false);
botSendMailEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.BotSendMailEnabled", true);
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", false); guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", false);
minGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskChangeTime", 3 * 24 * 3600); minGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskChangeTime", 3 * 24 * 3600);
maxGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskChangeTime", 4 * 24 * 3600); maxGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskChangeTime", 4 * 24 * 3600);
@ -576,8 +581,8 @@ bool PlayerbotAIConfig::Initialize()
disableRandomLevels = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableRandomLevels", false); disableRandomLevels = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableRandomLevels", false);
randomBotRandomPassword = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotRandomPassword", true); randomBotRandomPassword = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotRandomPassword", true);
downgradeMaxLevelBot = sConfigMgr->GetOption<bool>("AiPlayerbot.DowngradeMaxLevelBot", true); downgradeMaxLevelBot = sConfigMgr->GetOption<bool>("AiPlayerbot.DowngradeMaxLevelBot", true);
equipAndSpecPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipAndSpecPersistence", true); equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
equipAndSpecPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipAndSpecPersistenceLevel", 1); equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1); groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1);
keepAltsInGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.KeepAltsInGroup", false); keepAltsInGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.KeepAltsInGroup", false);
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true); allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
@ -586,7 +591,6 @@ bool PlayerbotAIConfig::Initialize()
reviveBotWhenSummoned = sConfigMgr->GetOption<int32>("AiPlayerbot.ReviveBotWhenSummoned", 1); reviveBotWhenSummoned = sConfigMgr->GetOption<int32>("AiPlayerbot.ReviveBotWhenSummoned", 1);
botRepairWhenSummon = sConfigMgr->GetOption<bool>("AiPlayerbot.BotRepairWhenSummon", true); botRepairWhenSummon = sConfigMgr->GetOption<bool>("AiPlayerbot.BotRepairWhenSummon", true);
autoInitOnly = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoInitOnly", false); autoInitOnly = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoInitOnly", false);
resetInstanceIdForAltBots = sConfigMgr->GetOption<bool>("AiPlayerbot.ResetInstanceIdForAltBots", false);
autoInitEquipLevelLimitRatio = sConfigMgr->GetOption<float>("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0); autoInitEquipLevelLimitRatio = sConfigMgr->GetOption<float>("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0);
maxAddedBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBots", 40); maxAddedBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBots", 40);
@ -665,7 +669,7 @@ bool PlayerbotAIConfig::Initialize()
dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true); dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true);
allowLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowLearnTrainerSpells", true); allowLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowLearnTrainerSpells", true);
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true); autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", true); autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0); hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
defaultPetStance = sConfigMgr->GetOption<int32>("AiPlayerbot.DefaultPetStance", 1); defaultPetStance = sConfigMgr->GetOption<int32>("AiPlayerbot.DefaultPetStance", 1);
petChatCommandDebug = sConfigMgr->GetOption<bool>("AiPlayerbot.PetChatCommandDebug", 0); petChatCommandDebug = sConfigMgr->GetOption<bool>("AiPlayerbot.PetChatCommandDebug", 0);

View File

@ -3,8 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version. * and/or modify it under version 3 of the License, or (at your option), any later version.
*/ */
#ifndef _PLAYERBOT_PLAYERBOTAICONFIG_H #ifndef _PLAYERBOT_PLAYERbotAICONFIG_H
#define _PLAYERBOT_PLAYERBOTAICONFIG_H #define _PLAYERBOT_PLAYERbotAICONFIG_H
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
@ -134,6 +134,7 @@ public:
std::vector<uint32> randomBotQuestIds; std::vector<uint32> randomBotQuestIds;
uint32 randomBotTeleportDistance; uint32 randomBotTeleportDistance;
float randomGearLoweringChance; float randomGearLoweringChance;
bool incrementalGearInit;
int32 randomGearQualityLimit; int32 randomGearQualityLimit;
int32 randomGearScoreLimit; int32 randomGearScoreLimit;
bool preferClassArmorType; bool preferClassArmorType;
@ -226,6 +227,10 @@ public:
uint32 guildRepliesRate; uint32 guildRepliesRate;
bool suggestDungeonsInLowerCaseRandomly;
// --
bool randomBotJoinBG; bool randomBotJoinBG;
bool randomBotAutoJoinBG; bool randomBotAutoJoinBG;
@ -247,6 +252,7 @@ public:
uint32 randomBotAutoJoinBGRatedArena3v3Count; uint32 randomBotAutoJoinBGRatedArena3v3Count;
uint32 randomBotAutoJoinBGRatedArena5v5Count; uint32 randomBotAutoJoinBGRatedArena5v5Count;
bool randomBotLoginAtStartup;
uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel; uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel;
std::map<uint32, std::pair<uint32, uint32>> zoneBrackets; std::map<uint32, std::pair<uint32, uint32>> zoneBrackets;
bool logInGroupOnly, logValuesPerTick; bool logInGroupOnly, logValuesPerTick;
@ -292,7 +298,6 @@ public:
float periodicOnlineOfflineRatio; float periodicOnlineOfflineRatio;
bool gearscorecheck; bool gearscorecheck;
bool randomBotPreQuests; bool randomBotPreQuests;
bool botSendMailEnabled;
bool guildTaskEnabled; bool guildTaskEnabled;
uint32 minGuildTaskChangeTime, maxGuildTaskChangeTime; uint32 minGuildTaskChangeTime, maxGuildTaskChangeTime;
@ -389,8 +394,8 @@ public:
uint32 selfBotLevel; uint32 selfBotLevel;
bool downgradeMaxLevelBot; bool downgradeMaxLevelBot;
bool equipAndSpecPersistence; bool equipmentPersistence;
int32 equipAndSpecPersistenceLevel; int32 equipmentPersistenceLevel;
int32 groupInvitationPermission; int32 groupInvitationPermission;
bool keepAltsInGroup = false; bool keepAltsInGroup = false;
bool KeepAltsInGroup() const { return keepAltsInGroup; } bool KeepAltsInGroup() const { return keepAltsInGroup; }
@ -400,7 +405,6 @@ public:
int reviveBotWhenSummoned; int reviveBotWhenSummoned;
bool botRepairWhenSummon; bool botRepairWhenSummon;
bool autoInitOnly; bool autoInitOnly;
bool resetInstanceIdForAltBots;
float autoInitEquipLevelLimitRatio; float autoInitEquipLevelLimitRatio;
int32 maxAddedBots; int32 maxAddedBots;
int32 addClassCommand; int32 addClassCommand;

View File

@ -242,7 +242,6 @@ public:
} }
group->ChangeLeader(newLeader->GetGUID()); group->ChangeLeader(newLeader->GetGUID());
group->SendUpdate();
LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Changed leader to {}", newLeader->GetName()); LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Changed leader to {}", newLeader->GetName());
return true; return true;
} }