mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
9 Commits
4a63ee37e2
...
92fa97c3aa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92fa97c3aa | ||
|
|
ff001afd46 | ||
|
|
32d10080a4 | ||
|
|
1bbed177c8 | ||
|
|
a3ca438bef | ||
|
|
28ec9b34b8 | ||
|
|
a1f9ff4542 | ||
|
|
0afaf753c6 | ||
|
|
240bb2dfca |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
@ -12,8 +12,8 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/)
|
|||||||
{
|
{
|
||||||
AutoPickTalents();
|
AutoPickTalents();
|
||||||
AutoLearnSpell();
|
AutoLearnSpell();
|
||||||
AutoUpgradeEquip();
|
|
||||||
AutoTeleportForLevel();
|
AutoTeleportForLevel();
|
||||||
|
AutoUpgradeEquip();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -21,13 +21,11 @@ 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;
|
||||||
}
|
}
|
||||||
@ -89,21 +87,17 @@ void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
|||||||
{
|
{
|
||||||
Quest const* quest = i->second;
|
Quest const* quest = i->second;
|
||||||
|
|
||||||
// only process class-specific quests to learn class-related spells, cuz
|
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10 ||
|
||||||
// we don't want all these bunch of entries to be handled!
|
quest->GetMinLevel() > bot->GetLevel())
|
||||||
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)
|
||||||
@ -113,31 +107,26 @@ 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; // pussywizard: break and not cast the spell (found is false)
|
break;
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@ -167,12 +156,11 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s
|
|||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig.autoUpgradeEquip || !sRandomPlayerbotMgr.IsRandomBot(bot))
|
if (!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();
|
||||||
@ -181,9 +169,6 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
|||||||
factory.InitConsumables();
|
factory.InitConsumables();
|
||||||
factory.InitPotions();
|
factory.InitPotions();
|
||||||
|
|
||||||
if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
if (sPlayerbotAIConfig.autoUpgradeEquip)
|
||||||
{
|
factory.InitEquipment(true);
|
||||||
if (sPlayerbotAIConfig.incrementalGearInit)
|
|
||||||
factory.InitEquipment(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,9 +154,11 @@ 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);
|
float newItemScore = calculator.CalculateItem(itemId, item->GetItemRandomPropertyId());
|
||||||
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
|
float mainHandScore = mainHandItem
|
||||||
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
|
? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId, mainHandItem->GetItemRandomPropertyId()) : 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 ||
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#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"
|
||||||
@ -23,6 +24,116 @@
|
|||||||
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) {}
|
||||||
|
|
||||||
@ -429,52 +540,109 @@ 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;
|
||||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(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);
|
||||||
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;
|
applyAura = true;
|
||||||
break;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!applyAura)
|
if (defensiveTankEffect)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
uint32 spellProcFlag = spellInfo->ProcFlags;
|
if (!botAI->CanCastSpell(spellId, bot, false, nullptr, item))
|
||||||
|
|
||||||
// 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;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spellId)
|
if (!spellId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -483,7 +651,25 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -334,6 +334,10 @@ 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
|
||||||
|
|||||||
@ -44,6 +44,21 @@ 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");
|
||||||
|
|||||||
@ -34,24 +34,23 @@ 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(
|
||||||
|
|||||||
@ -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("leader", relevance),*/ }));
|
new TriggerNode("group set leader", { NextAction("reset botAI", 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(
|
||||||
|
|||||||
@ -38,3 +38,16 @@ 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);
|
||||||
|
}
|
||||||
|
|||||||
@ -29,4 +29,11 @@ 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
|
||||||
|
|||||||
@ -13,12 +13,14 @@ 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
|
||||||
|
|||||||
@ -11,11 +11,15 @@ 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
|
||||||
|
|||||||
@ -10,4 +10,10 @@ 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) }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,3 +27,24 @@ 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);
|
||||||
|
}
|
||||||
|
|||||||
@ -21,4 +21,20 @@ 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
|
||||||
|
|||||||
@ -12,7 +12,10 @@ 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
|
||||||
|
|||||||
@ -552,9 +552,8 @@ 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"));
|
||||||
@ -562,16 +561,22 @@ 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 (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel)
|
if (!sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
|
level < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
|
||||||
|
{
|
||||||
bot->resetTalents(true);
|
bot->resetTalents(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (!incremental)
|
if (!incremental)
|
||||||
{
|
{
|
||||||
ClearSkills();
|
ClearSkills();
|
||||||
ClearSpells();
|
ClearSpells();
|
||||||
ResetQuests();
|
ResetQuests();
|
||||||
if (!PlayerbotAIConfig::instance().equipmentPersistence || level < PlayerbotAIConfig::instance().equipmentPersistenceLevel)
|
if (!sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
|
level < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
|
||||||
|
{
|
||||||
ClearAllItems();
|
ClearAllItems();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ClearInventory();
|
ClearInventory();
|
||||||
bot->RemoveAllSpellCooldown();
|
bot->RemoveAllSpellCooldown();
|
||||||
@ -622,8 +627,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.equipmentPersistence ||
|
if (!incremental || !sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
|
||||||
{
|
{
|
||||||
uint32 specIndex = InitTalentsTree();
|
uint32 specIndex = InitTalentsTree();
|
||||||
sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specIndex + 1);
|
sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specIndex + 1);
|
||||||
@ -670,11 +675,10 @@ 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.equipmentPersistence ||
|
if (!incremental || !sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
|
||||||
{
|
{
|
||||||
if (sPlayerbotAIConfig.incrementalGearInit || !incremental)
|
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig.twoRoundsGearInit);
|
||||||
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig.twoRoundsGearInit);
|
|
||||||
}
|
}
|
||||||
// bot->SaveToDB(false, false);
|
// bot->SaveToDB(false, false);
|
||||||
if (pmo)
|
if (pmo)
|
||||||
@ -811,7 +815,8 @@ void PlayerbotFactory::Randomize(bool incremental)
|
|||||||
void PlayerbotFactory::Refresh()
|
void PlayerbotFactory::Refresh()
|
||||||
{
|
{
|
||||||
// Prepare();
|
// Prepare();
|
||||||
// if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
// if (!sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
|
// bot->GetLevel() < sPlayerbotAIConfig.equipAndSpecPersistenceLevel)
|
||||||
// {
|
// {
|
||||||
// InitEquipment(true);
|
// InitEquipment(true);
|
||||||
// }
|
// }
|
||||||
@ -831,14 +836,13 @@ void PlayerbotFactory::Refresh()
|
|||||||
InitSpecialSpells();
|
InitSpecialSpells();
|
||||||
InitMounts();
|
InitMounts();
|
||||||
InitKeyring();
|
InitKeyring();
|
||||||
if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel)
|
if (!sPlayerbotAIConfig.equipAndSpecPersistence ||
|
||||||
|
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);
|
||||||
@ -2037,9 +2041,6 @@ 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
|
||||||
@ -2081,7 +2082,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<uint8, std::vector<uint32>> items;
|
std::unordered_map<uint8, std::vector<std::pair<uint32, int32>>> items;
|
||||||
// int tab = AiFactory::GetPlayerSpecTab(bot);
|
// int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
|
|
||||||
uint32 blevel = bot->GetLevel();
|
uint32 blevel = bot->GetLevel();
|
||||||
@ -2228,13 +2229,17 @@ 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<uint32>& ids = items[slot];
|
std::vector<std::pair<uint32, int32>>& ids = items[slot];
|
||||||
if (ids.empty())
|
if (ids.empty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -2242,13 +2247,15 @@ 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];
|
uint32 newItemId = ids[index].first;
|
||||||
|
int32 newItemProp = ids[index].second;
|
||||||
|
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||||
|
|
||||||
float cur_score = calculator.CalculateItem(newItemId, 0, slot);
|
float cur_score = calculator.CalculateItem(newItemId, newItemProp, 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)
|
||||||
{
|
{
|
||||||
@ -2267,6 +2274,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
continue;
|
continue;
|
||||||
bestScoreForSlot = cur_score;
|
bestScoreForSlot = cur_score;
|
||||||
bestItemForSlot = newItemId;
|
bestItemForSlot = newItemId;
|
||||||
|
bestRandomPropForSlot = newItemProp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2304,7 +2312,16 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
if (oldItem)
|
if (oldItem)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bot->EquipNewItem(dest, bestItemForSlot, true);
|
if (Item* equipped = 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)
|
||||||
// {
|
// {
|
||||||
@ -2345,19 +2362,21 @@ 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<uint32>& ids = items[slot];
|
std::vector<std::pair<uint32, int32>>& 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];
|
uint32 newItemId = ids[index].first;
|
||||||
|
int32 newItemProp = ids[index].second;
|
||||||
|
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||||
|
|
||||||
float cur_score = calculator.CalculateItem(newItemId, 0, slot);
|
float cur_score = calculator.CalculateItem(newItemId, newItemProp, 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)
|
||||||
{
|
{
|
||||||
@ -2376,6 +2395,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
continue;
|
continue;
|
||||||
bestScoreForSlot = cur_score;
|
bestScoreForSlot = cur_score;
|
||||||
bestItemForSlot = newItemId;
|
bestItemForSlot = newItemId;
|
||||||
|
bestRandomPropForSlot = newItemProp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2386,7 +2406,16 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bot->EquipNewItem(dest, bestItemForSlot, true);
|
if (Item* equipped = 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#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"
|
||||||
@ -481,12 +482,6 @@ 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)
|
||||||
@ -737,7 +732,10 @@ 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)
|
||||||
{
|
{
|
||||||
@ -1246,7 +1244,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 const s = *i;
|
std::string s = *i;
|
||||||
|
|
||||||
if (!strcmp(cmd, "addaccount"))
|
if (!strcmp(cmd, "addaccount"))
|
||||||
{
|
{
|
||||||
@ -1255,7 +1253,13 @@ 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
|
||||||
ObjectGuid charGuid = sCharacterCache->GetCharacterGuidByName(s);
|
std::string charName = 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");
|
||||||
@ -1283,6 +1287,11 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -160,6 +160,7 @@ void RandomItemMgr::Init()
|
|||||||
BuildPotionCache();
|
BuildPotionCache();
|
||||||
BuildFoodCache();
|
BuildFoodCache();
|
||||||
BuildTradeCache();
|
BuildTradeCache();
|
||||||
|
LoadEnchantmentPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RandomItemMgr::InitAfterAhBot()
|
void RandomItemMgr::InitAfterAhBot()
|
||||||
@ -452,6 +453,39 @@ 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)
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -172,9 +173,11 @@ 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();
|
||||||
@ -217,6 +220,8 @@ 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()
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#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"
|
||||||
@ -190,6 +191,53 @@ 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);
|
||||||
|
|||||||
@ -30,6 +30,7 @@ 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; }
|
||||||
|
|||||||
@ -147,7 +147,6 @@ 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);
|
||||||
@ -215,7 +214,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
attunementQuests);
|
attunementQuests);
|
||||||
|
|
||||||
LoadSet<std::set<uint32>>(
|
LoadSet<std::set<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,46978"),
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,44869,44870,46978"),
|
||||||
unobtainableItems);
|
unobtainableItems);
|
||||||
|
|
||||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||||
@ -352,32 +351,27 @@ 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", 7);
|
randomBotAutoJoinArenaBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinArenaBracket", 14);
|
||||||
|
|
||||||
randomBotAutoJoinICBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinICBrackets", "0,1");
|
randomBotAutoJoinWSBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinWSBrackets", "7");
|
||||||
randomBotAutoJoinEYBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinEYBrackets", "0,1,2");
|
randomBotAutoJoinABBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinABBrackets", "6");
|
||||||
randomBotAutoJoinAVBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinAVBrackets", "0,1,2,3");
|
randomBotAutoJoinAVBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinAVBrackets", "3");
|
||||||
randomBotAutoJoinABBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinABBrackets", "0,1,2,3,4,5,6");
|
randomBotAutoJoinEYBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinEYBrackets", "2");
|
||||||
randomBotAutoJoinWSBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinWSBrackets", "0,1,2,3,4,5,6,7");
|
randomBotAutoJoinICBrackets = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAutoJoinICBrackets", "1");
|
||||||
|
|
||||||
randomBotAutoJoinBGICCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGICCount", 0);
|
randomBotAutoJoinBGWSCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWSCount", 1);
|
||||||
randomBotAutoJoinBGEYCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGEYCount", 0);
|
randomBotAutoJoinBGABCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGABCount", 1);
|
||||||
randomBotAutoJoinBGAVCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGAVCount", 0);
|
randomBotAutoJoinBGAVCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGAVCount", 0);
|
||||||
randomBotAutoJoinBGABCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGABCount", 0);
|
randomBotAutoJoinBGEYCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGEYCount", 1);
|
||||||
randomBotAutoJoinBGWSCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGWSCount", 0);
|
randomBotAutoJoinBGICCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGICCount", 0);
|
||||||
|
|
||||||
randomBotAutoJoinBGRatedArena2v2Count =
|
randomBotAutoJoinBGRatedArena2v2Count =
|
||||||
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count", 0);
|
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinBGRatedArena2v2Count", 0);
|
||||||
@ -393,7 +387,6 @@ 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);
|
||||||
@ -559,6 +552,8 @@ 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);
|
||||||
@ -581,8 +576,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);
|
||||||
equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
|
equipAndSpecPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipAndSpecPersistence", true);
|
||||||
equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
|
equipAndSpecPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipAndSpecPersistenceLevel", 1);
|
||||||
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);
|
||||||
@ -591,6 +586,7 @@ 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);
|
||||||
@ -669,7 +665,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", false);
|
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", true);
|
||||||
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);
|
||||||
|
|||||||
@ -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,7 +134,6 @@ 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;
|
||||||
@ -227,10 +226,6 @@ public:
|
|||||||
|
|
||||||
uint32 guildRepliesRate;
|
uint32 guildRepliesRate;
|
||||||
|
|
||||||
bool suggestDungeonsInLowerCaseRandomly;
|
|
||||||
|
|
||||||
// --
|
|
||||||
|
|
||||||
bool randomBotJoinBG;
|
bool randomBotJoinBG;
|
||||||
bool randomBotAutoJoinBG;
|
bool randomBotAutoJoinBG;
|
||||||
|
|
||||||
@ -252,7 +247,6 @@ 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;
|
||||||
@ -298,6 +292,7 @@ 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;
|
||||||
@ -394,8 +389,8 @@ public:
|
|||||||
|
|
||||||
uint32 selfBotLevel;
|
uint32 selfBotLevel;
|
||||||
bool downgradeMaxLevelBot;
|
bool downgradeMaxLevelBot;
|
||||||
bool equipmentPersistence;
|
bool equipAndSpecPersistence;
|
||||||
int32 equipmentPersistenceLevel;
|
int32 equipAndSpecPersistenceLevel;
|
||||||
int32 groupInvitationPermission;
|
int32 groupInvitationPermission;
|
||||||
bool keepAltsInGroup = false;
|
bool keepAltsInGroup = false;
|
||||||
bool KeepAltsInGroup() const { return keepAltsInGroup; }
|
bool KeepAltsInGroup() const { return keepAltsInGroup; }
|
||||||
@ -405,6 +400,7 @@ 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;
|
||||||
|
|||||||
@ -242,6 +242,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user