mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-02-20 18:10:02 +01:00
Compare commits
No commits in common. "86390f90fd3a805d7d58443b9924deac6f8acb8a" and "64df7439d804e273af3051296e5e9b90344abf3c" have entirely different histories.
86390f90fd
...
64df7439d8
@ -656,10 +656,6 @@ AiPlayerbot.RandomGearQualityLimit = 3
|
||||
# Default: 0 (no limit)
|
||||
AiPlayerbot.RandomGearScoreLimit = 0
|
||||
|
||||
# If disabled, random bots can only upgrade equipment through looting and quests
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.IncrementalGearInit = 1
|
||||
|
||||
# Set minimum level of bots that will enchant their equipment (Maxlevel + 1 to disable)
|
||||
# Default: 60
|
||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||
|
||||
@ -80,13 +80,13 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot)
|
||||
switch (bot->getClass())
|
||||
{
|
||||
case CLASS_MAGE:
|
||||
tab = MAGE_TAB_FROST;
|
||||
tab = 1;
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
tab = PALADIN_TAB_RETRIBUTION;
|
||||
tab = 2;
|
||||
break;
|
||||
case CLASS_PRIEST:
|
||||
tab = PRIEST_TAB_HOLY;
|
||||
tab = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -117,8 +117,6 @@ bool PlayerbotAIConfig::Initialize()
|
||||
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
|
||||
|
||||
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
|
||||
|
||||
incrementalGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.IncrementalGearInit", true);
|
||||
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
|
||||
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
|
||||
|
||||
|
||||
@ -87,7 +87,6 @@ public:
|
||||
std::vector<uint32> randomBotQuestIds;
|
||||
uint32 randomBotTeleportDistance;
|
||||
float randomGearLoweringChance;
|
||||
bool incrementalGearInit;
|
||||
int32 randomGearQualityLimit;
|
||||
int32 randomGearScoreLimit;
|
||||
float randomBotMinLevelChance, randomBotMaxLevelChance;
|
||||
|
||||
@ -486,13 +486,6 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')",
|
||||
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||
|
||||
// Wait for the characters to be deleted before proceeding to dependent deletes
|
||||
while (CharacterDatabase.QueueSize())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
|
||||
|
||||
// Clean up orphaned entries in playerbots_guild_tasks
|
||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)");
|
||||
|
||||
@ -507,13 +500,11 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_arena_stats WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_entry_point WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
|
||||
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
|
||||
// Clean up pet data
|
||||
CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)");
|
||||
@ -568,23 +559,11 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
|
||||
uint32 timer = getMSTime();
|
||||
|
||||
// After ALL deletions, make sure data is commited to DB
|
||||
LoginDatabase.Execute("COMMIT");
|
||||
CharacterDatabase.Execute("COMMIT");
|
||||
PlayerbotsDatabase.Execute("COMMIT");
|
||||
|
||||
// Wait for all pending database operations to complete
|
||||
while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
|
||||
|
||||
// Flush tables to ensure all data in memory are written to disk
|
||||
LoginDatabase.Execute("FLUSH TABLES");
|
||||
CharacterDatabase.Execute("FLUSH TABLES");
|
||||
PlayerbotsDatabase.Execute("FLUSH TABLES");
|
||||
|
||||
LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer));
|
||||
LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server...");
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
|
||||
@ -181,11 +181,6 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0)
|
||||
|
||||
BattlegroundData.clear(); // Clear here and here only.
|
||||
|
||||
// Cleanup on server start: orphaned pet data that's often left behind by bot pets that no longer exist in the DB
|
||||
CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)");
|
||||
|
||||
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
|
||||
{
|
||||
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
|
||||
@ -374,7 +369,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if (DelayLoginBotsTimer)
|
||||
{
|
||||
@ -520,7 +515,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
||||
{
|
||||
maxAllowedBotCount -= currentBots.size();
|
||||
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
|
||||
|
||||
|
||||
uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio;
|
||||
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio;
|
||||
|
||||
@ -541,7 +536,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
||||
{
|
||||
// minus addclass bots account
|
||||
int32 baseAccount = RandomPlayerbotFactory::CalculateTotalAccountCount() - sPlayerbotAIConfig->addClassAccountPoolSize;
|
||||
|
||||
|
||||
if (baseAccount <= 0 || baseAccount > sPlayerbotAIConfig->randomBotAccounts.size())
|
||||
{
|
||||
LOG_ERROR("playerbots", "Account calculation error with PeriodicOnlineOffline");
|
||||
@ -556,7 +551,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||
if (!result)
|
||||
continue;
|
||||
|
||||
|
||||
std::vector<GuidClassRaceInfo> allGuidInfos;
|
||||
|
||||
do {
|
||||
@ -724,7 +719,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
||||
LOG_DEBUG("playerbots", "Checking BG Queue...");
|
||||
|
||||
// Initialize Battleground Data (do not clear here)
|
||||
|
||||
|
||||
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
|
||||
{
|
||||
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
|
||||
@ -798,11 +793,11 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
||||
{
|
||||
std::vector<uint32>* instanceIds = nullptr;
|
||||
uint32 instanceId = player->GetBattleground()->GetInstanceID();
|
||||
|
||||
|
||||
instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;
|
||||
if (instanceIds && std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end())
|
||||
instanceIds->push_back(instanceId);
|
||||
|
||||
|
||||
BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size();
|
||||
}
|
||||
}
|
||||
@ -1483,7 +1478,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
||||
z = 0.05f + ground;
|
||||
PlayerInfo const* pInfo = sObjectMgr->GetPlayerInfo(bot->getRace(true), bot->getClass());
|
||||
float dis = loc.GetExactDist(pInfo->positionX, pInfo->positionY, pInfo->positionZ);
|
||||
|
||||
|
||||
// yunfan: distance check for low level
|
||||
if (bot->GetLevel() <= 4 && (loc.GetMapId() != pInfo->mapId || dis > 500.0f))
|
||||
{
|
||||
@ -1611,8 +1606,8 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
|
||||
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
|
||||
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
|
||||
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
|
||||
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
|
||||
|
||||
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
|
||||
|
||||
// Override with values from config
|
||||
for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) {
|
||||
zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second};
|
||||
@ -1704,8 +1699,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
"position_y, "
|
||||
"position_z, "
|
||||
"orientation, "
|
||||
"t.faction, "
|
||||
"t.entry "
|
||||
"t.faction "
|
||||
"FROM "
|
||||
"creature c "
|
||||
"INNER JOIN creature_template t on c.id1 = t.entry "
|
||||
@ -1727,11 +1721,6 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
float z = fields[3].Get<float>();
|
||||
float orient = fields[4].Get<float>();
|
||||
uint32 faction = fields[5].Get<uint32>();
|
||||
uint32 tEntry = fields[6].Get<uint32>();
|
||||
|
||||
if (tEntry == 3838 || tEntry == 29480)
|
||||
continue;
|
||||
|
||||
const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction);
|
||||
|
||||
WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI);
|
||||
@ -1782,7 +1771,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
}
|
||||
LOG_INFO("playerbots", ">> {} innkeepers locations for level collected.", collected_locs);
|
||||
}
|
||||
|
||||
|
||||
results = WorldDatabase.Query(
|
||||
"SELECT "
|
||||
"map, "
|
||||
@ -1799,8 +1788,13 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
"AND t.npcflag != 135298 "
|
||||
"AND t.minlevel != 55 "
|
||||
"AND t.minlevel != 65 "
|
||||
"AND t.faction not in (35, 474, 69, 57) "
|
||||
"AND t.entry not in (30606, 30608, 29282) "
|
||||
"AND t.faction != 35 "
|
||||
"AND t.faction != 474 "
|
||||
"AND t.faction != 69 "
|
||||
"AND t.entry != 30606 "
|
||||
"AND t.entry != 30608 "
|
||||
"AND t.entry != 29282 "
|
||||
"AND t.faction != 69 "
|
||||
"AND map IN ({}) "
|
||||
"ORDER BY "
|
||||
"t.minlevel;",
|
||||
@ -1883,12 +1877,12 @@ void RandomPlayerbotMgr::Init()
|
||||
{
|
||||
if (sPlayerbotAIConfig->addClassCommand)
|
||||
sRandomPlayerbotMgr->PrepareAddclassCache();
|
||||
|
||||
|
||||
if (sPlayerbotAIConfig->enabled)
|
||||
{
|
||||
sRandomPlayerbotMgr->PrepareTeleportCache();
|
||||
}
|
||||
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotJoinBG)
|
||||
sRandomPlayerbotMgr->LoadBattleMastersCache();
|
||||
|
||||
@ -2031,7 +2025,7 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
if (sPlayerbotAIConfig->syncLevelWithPlayers)
|
||||
maxLevel = std::max(sPlayerbotAIConfig->randomBotMinLevel,
|
||||
std::min(playersLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)));
|
||||
|
||||
|
||||
uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel;
|
||||
if (bot->getClass() == CLASS_DEATH_KNIGHT)
|
||||
{
|
||||
@ -2628,7 +2622,7 @@ void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot)
|
||||
{
|
||||
LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(),
|
||||
bot->GetName().c_str());
|
||||
|
||||
|
||||
if (playerBots.size() == sRandomPlayerbotMgr->GetMaxAllowedBotCount())
|
||||
{
|
||||
_isBotLogging = false;
|
||||
@ -2763,19 +2757,14 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
|
||||
std::map<uint8, uint32> perRace;
|
||||
std::map<uint8, uint32> perClass;
|
||||
|
||||
std::map<uint8, uint32> lvlPerRace;
|
||||
std::map<uint8, uint32> lvlPerClass;
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
|
||||
{
|
||||
perRace[race] = 0;
|
||||
lvlPerRace[race] = 0;
|
||||
}
|
||||
|
||||
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
perClass[cls] = 0;
|
||||
lvlPerClass[cls] = 0;
|
||||
}
|
||||
|
||||
uint32 dps = 0;
|
||||
@ -2813,9 +2802,6 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
++perRace[bot->getRace()];
|
||||
++perClass[bot->getClass()];
|
||||
|
||||
lvlPerClass[bot->getClass()] += bot->GetLevel();
|
||||
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (botAI->AllowActivity())
|
||||
++active;
|
||||
@ -2872,7 +2858,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
++tank;
|
||||
else
|
||||
++dps;
|
||||
|
||||
|
||||
zoneCount[bot->GetZoneId()]++;
|
||||
|
||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||
@ -2883,7 +2869,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
LOG_INFO("playerbots", "Bots level:");
|
||||
// uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
uint32_t currentAlliance = 0, currentHorde = 0;
|
||||
@ -2908,21 +2894,15 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
LOG_INFO("playerbots", "Bots race:");
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
|
||||
{
|
||||
if (perRace[race]) {
|
||||
uint32 lvl = lvlPerRace[race] * 10 / perRace[race];
|
||||
float flvl = lvl / 10.0f;
|
||||
LOG_INFO("playerbots", " {}: {}, avg lvl: {}", ChatHelper::FormatRace(race).c_str(), perRace[race], flvl);
|
||||
}
|
||||
if (perRace[race])
|
||||
LOG_INFO("playerbots", " {}: {}", ChatHelper::FormatRace(race).c_str(), perRace[race]);
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Bots class:");
|
||||
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
if (perClass[cls]) {
|
||||
uint32 lvl = lvlPerClass[cls] * 10 / perClass[cls];
|
||||
float flvl = lvl / 10.0f;
|
||||
LOG_INFO("playerbots", " {}: {}, avg lvl: {}", ChatHelper::FormatClass(cls).c_str(), perClass[cls], flvl);
|
||||
}
|
||||
if (perClass[cls])
|
||||
LOG_INFO("playerbots", " {}: {}", ChatHelper::FormatClass(cls).c_str(), perClass[cls]);
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Bots role:");
|
||||
@ -2945,7 +2925,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
LOG_INFO("playerbots", " In BG: {}", inBg);
|
||||
LOG_INFO("playerbots", " In Rest: {}", rest);
|
||||
LOG_INFO("playerbots", " Dead: {}", dead);
|
||||
|
||||
|
||||
// LOG_INFO("playerbots", "Bots zone:");
|
||||
// for (auto &[zond_id, counter] : zoneCount)
|
||||
// {
|
||||
@ -2953,7 +2933,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
// std::string name = PlayerbotAI::GetLocalizedAreaName(entry);
|
||||
// LOG_INFO("playerbots", " {}: {}", name, counter);
|
||||
// }
|
||||
|
||||
|
||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||
{
|
||||
LOG_INFO("playerbots", "Bots rpg status:");
|
||||
|
||||
@ -224,22 +224,24 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
{
|
||||
bot->resetTalents(true);
|
||||
}
|
||||
// bot->SaveToDB(false, false);
|
||||
ClearSkills();
|
||||
// bot->SaveToDB(false, false);
|
||||
ClearSpells();
|
||||
// bot->SaveToDB(false, false);
|
||||
if (!incremental)
|
||||
{
|
||||
ClearSkills();
|
||||
ClearSpells();
|
||||
ResetQuests();
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
ClearAllItems();
|
||||
}
|
||||
}
|
||||
ClearInventory();
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
ClearAllItems();
|
||||
}
|
||||
bot->RemoveAllSpellCooldown();
|
||||
UnbindInstance();
|
||||
|
||||
bot->GiveLevel(level);
|
||||
bot->InitStatsForLevel(true);
|
||||
bot->InitStatsForLevel();
|
||||
CancelAuras();
|
||||
// bot->SaveToDB(false, false);
|
||||
if (pmo)
|
||||
@ -278,6 +280,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
LOG_DEBUG("playerbots", "Initializing skills (step 1)...");
|
||||
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1");
|
||||
InitSkills();
|
||||
// InitTradeSkills();
|
||||
if (pmo)
|
||||
pmo->finish();
|
||||
|
||||
@ -334,8 +337,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
if (!incremental || !sPlayerbotAIConfig->equipmentPersistence ||
|
||||
bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
if (sPlayerbotAIConfig->incrementalGearInit || !incremental)
|
||||
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
|
||||
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
|
||||
}
|
||||
// bot->SaveToDB(false, false);
|
||||
if (pmo)
|
||||
@ -1022,21 +1024,9 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa
|
||||
/// @todo: match current talent with template
|
||||
specTab = AiFactory::GetPlayerSpecTab(bot);
|
||||
/// @todo: fix cat druid hardcode
|
||||
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20)
|
||||
{
|
||||
bool isCat = !bot->HasAura(16931);
|
||||
if (!isCat && bot->GetLevel() == 20)
|
||||
{
|
||||
uint32 bearP = sPlayerbotAIConfig->randomClassSpecProb[cls][1];
|
||||
uint32 catP = sPlayerbotAIConfig->randomClassSpecProb[cls][3];
|
||||
if (urand(1, bearP + catP) <= catP)
|
||||
isCat = true;
|
||||
}
|
||||
if (isCat)
|
||||
{
|
||||
specTab = 3;
|
||||
}
|
||||
}
|
||||
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 &&
|
||||
!bot->HasAura(16931))
|
||||
specTab = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1609,51 +1599,9 @@ void Shuffle(std::vector<uint32>& items)
|
||||
|
||||
void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
||||
{
|
||||
if (incremental && !sPlayerbotAIConfig->incrementalGearInit)
|
||||
return;
|
||||
|
||||
if (level < 5) {
|
||||
// original items
|
||||
if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(bot->getRace(), bot->getClass(), bot->getGender()))
|
||||
{
|
||||
for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
|
||||
{
|
||||
if (oEntry->ItemId[j] <= 0)
|
||||
continue;
|
||||
|
||||
uint32 itemId = oEntry->ItemId[j];
|
||||
|
||||
// skip hearthstone
|
||||
if (itemId == 6948)
|
||||
continue;
|
||||
|
||||
// just skip, reported in ObjectMgr::LoadItemTemplates
|
||||
ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!iProto)
|
||||
continue;
|
||||
|
||||
// BuyCount by default
|
||||
uint32 count = iProto->BuyCount;
|
||||
|
||||
// special amount for food/drink
|
||||
if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bot->HasItemCount(itemId, count)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bot->StoreNewItemInBestSlots(itemId, count);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<uint8, std::vector<uint32>> items;
|
||||
// int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
|
||||
|
||||
uint32 blevel = bot->GetLevel();
|
||||
int32 delta = std::min(blevel, 10u);
|
||||
|
||||
|
||||
@ -29,12 +29,12 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
|
||||
{
|
||||
if (proto->IsRangedWeapon())
|
||||
{
|
||||
float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
|
||||
uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
|
||||
stats[STATS_TYPE_RANGED_DPS] += val;
|
||||
}
|
||||
else if (proto->IsWeapon())
|
||||
{
|
||||
float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
|
||||
uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
|
||||
stats[STATS_TYPE_MELEE_DPS] += val;
|
||||
}
|
||||
stats[STATS_TYPE_ARMOR] += proto->Armor;
|
||||
@ -436,10 +436,10 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
|
||||
switch (itemStatType)
|
||||
{
|
||||
case ITEM_MOD_MANA:
|
||||
stats[STATS_TYPE_MANA_REGENERATION] += (float)val / 10;
|
||||
stats[STATS_TYPE_MANA_REGENERATION] += val / 10;
|
||||
break;
|
||||
case ITEM_MOD_HEALTH:
|
||||
stats[STATS_TYPE_STAMINA] += (float)val / 15;
|
||||
stats[STATS_TYPE_STAMINA] += val / 15;
|
||||
break;
|
||||
case ITEM_MOD_AGILITY:
|
||||
stats[STATS_TYPE_AGILITY] += val;
|
||||
@ -747,11 +747,11 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
|
||||
}
|
||||
}
|
||||
|
||||
float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
{
|
||||
// float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
|
||||
float basePoints = effectInfo.BasePoints;
|
||||
int32 randomPoints = effectInfo.DieSides;
|
||||
int32 basePoints = effectInfo.BasePoints;
|
||||
int32 randomPoints = int32(effectInfo.DieSides);
|
||||
|
||||
switch (randomPoints)
|
||||
{
|
||||
@ -761,7 +761,7 @@ float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
|
||||
basePoints += 1;
|
||||
break;
|
||||
default:
|
||||
float randvalue = (1 + randomPoints) / 2.0f;
|
||||
int32 randvalue = (1 + randomPoints) / 2;
|
||||
basePoints += randvalue;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ public:
|
||||
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
|
||||
|
||||
public:
|
||||
float stats[STATS_TYPE_MAX];
|
||||
int32 stats[STATS_TYPE_MAX];
|
||||
|
||||
private:
|
||||
void CollectByItemStatType(uint32 itemStatType, int32 val);
|
||||
@ -80,7 +80,7 @@ private:
|
||||
|
||||
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
|
||||
uint32 triggerCooldown);
|
||||
float AverageValue(const SpellEffectInfo& effectInfo);
|
||||
int32 AverageValue(const SpellEffectInfo& effectInfo);
|
||||
|
||||
private:
|
||||
CollectorType type_;
|
||||
|
||||
@ -33,7 +33,6 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
|
||||
else
|
||||
type_ = CollectorType::RANGED;
|
||||
cls = player->getClass();
|
||||
lvl = player->GetLevel();
|
||||
tab = AiFactory::GetPlayerSpecTab(player);
|
||||
collector_ = std::make_unique<StatsCollector>(type_, cls);
|
||||
|
||||
@ -71,7 +70,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId
|
||||
Reset();
|
||||
|
||||
collector_->CollectItemStats(proto);
|
||||
|
||||
|
||||
if (randomPropertyIds != 0)
|
||||
CalculateRandomProperty(randomPropertyIds, itemId);
|
||||
|
||||
@ -182,7 +181,6 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_ARMOR] += 0.001f;
|
||||
stats_weights_[STATS_TYPE_BONUS] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f;
|
||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 0.01f;
|
||||
|
||||
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
|
||||
{
|
||||
@ -531,13 +529,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
|
||||
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() &&
|
||||
player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
}
|
||||
// spec with double hand
|
||||
// fury without duel wield, arms, bear, retribution, blood dk
|
||||
@ -553,11 +551,15 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
// caster's main hand (cannot duel weapon but can equip two-hands stuff)
|
||||
if (cls == CLASS_MAGE || cls == CLASS_PRIEST || cls == CLASS_WARLOCK || cls == CLASS_DRUID ||
|
||||
if (cls == CLASS_MAGE ||
|
||||
cls == CLASS_PRIEST ||
|
||||
cls == CLASS_WARLOCK ||
|
||||
cls == CLASS_DRUID ||
|
||||
(cls == CLASS_SHAMAN && !player_->CanDualWield()))
|
||||
{
|
||||
weight_ *= 0.65;
|
||||
}
|
||||
|
||||
}
|
||||
// fury with titan's grip
|
||||
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
|
||||
@ -566,16 +568,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
|
||||
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
|
||||
if (lvl >= 10 && cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
|
||||
proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
|
||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||
{
|
||||
weight_ *= 1.5;
|
||||
weight_ *= 0.5;
|
||||
}
|
||||
|
||||
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
|
||||
@ -658,7 +660,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
else
|
||||
validPoints = 0;
|
||||
}
|
||||
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], validPoints);
|
||||
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints);
|
||||
}
|
||||
|
||||
{
|
||||
@ -675,7 +677,8 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], validPoints);
|
||||
collector_->stats[STATS_TYPE_EXPERTISE] =
|
||||
std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,7 +695,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], validPoints);
|
||||
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,7 +714,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] =
|
||||
std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], validPoints);
|
||||
std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +57,6 @@ private:
|
||||
CollectorType hitOverflowType_;
|
||||
std::unique_ptr<StatsCollector> collector_;
|
||||
uint8 cls;
|
||||
uint8 lvl;
|
||||
int tab;
|
||||
bool enable_overflow_penalty_;
|
||||
bool enable_item_set_bonus_;
|
||||
|
||||
@ -163,8 +163,7 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
if (sPlayerbotAIConfig->incrementalGearInit)
|
||||
factory.InitEquipment(true);
|
||||
factory.InitEquipment(true);
|
||||
}
|
||||
factory.InitAmmo();
|
||||
return;
|
||||
|
||||
@ -187,8 +187,7 @@ void EquipAction::EquipItem(Item* item)
|
||||
// Priority 1: Replace main hand if the new weapon is strictly better
|
||||
// and if conditions allow (e.g. no conflicting 2H logic)
|
||||
bool betterThanMH = (newItemScore > mainHandScore);
|
||||
// If a one-handed weapon is better, we can still use it instead of a two-handed weapon
|
||||
bool mhConditionOK = (invType != INVTYPE_2HWEAPON ||
|
||||
bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) ||
|
||||
(isTwoHander && !canTitanGrip) ||
|
||||
(canTitanGrip && isValidTGWeapon));
|
||||
|
||||
|
||||
@ -1801,6 +1801,7 @@ const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y,
|
||||
|
||||
bool FleeAction::Execute(Event event)
|
||||
{
|
||||
// return Flee(AI_VALUE(Unit*, "current target"));
|
||||
return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true);
|
||||
}
|
||||
|
||||
@ -1810,10 +1811,6 @@ bool FleeAction::isUseful()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
if (target && target->IsInWorld() && !bot->IsWithinMeleeRange(target))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ bool TaxiAction::Execute(Event event)
|
||||
{
|
||||
if (Creature* npcPtr = ObjectAccessor::GetCreature(*bot, npcGuid))
|
||||
if (!movement.taxiNodes.empty())
|
||||
bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr, 0);
|
||||
bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr);
|
||||
},
|
||||
delay);
|
||||
botAI->SetNextCheckDelay(delay + 50);
|
||||
@ -114,7 +114,7 @@ bool TaxiAction::Execute(Event event)
|
||||
return bot->ActivateTaxiPathTo({entry->from, entry->to}, npc, 0);
|
||||
}
|
||||
|
||||
if (!movement.taxiNodes.empty() && !bot->ActivateTaxiPathTo(movement.taxiNodes, npc, 0))
|
||||
if (!movement.taxiNodes.empty() && !bot->ActivateTaxiPathTo(movement.taxiNodes, npc))
|
||||
{
|
||||
movement.taxiNodes.clear();
|
||||
movement.Set(nullptr);
|
||||
|
||||
@ -60,7 +60,7 @@ ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy
|
||||
NextAction** ArcaneMageStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // arcane immune target
|
||||
// new NextAction("arcane barrage", ACTION_DEFAULT + 0.2f), // cast during movement
|
||||
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
|
||||
new NextAction("shoot", ACTION_DEFAULT), nullptr);
|
||||
}
|
||||
|
||||
@ -10,8 +10,7 @@
|
||||
|
||||
NextAction** FireMageStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // fire immune target
|
||||
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
|
||||
new NextAction("shoot", ACTION_DEFAULT), NULL);
|
||||
}
|
||||
|
||||
@ -37,6 +37,13 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||
botAI->rpgInfo.SetMoveFarTo(dest);
|
||||
}
|
||||
|
||||
float dis = bot->GetExactDist(dest);
|
||||
if (dis < pathFinderDis)
|
||||
{
|
||||
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
|
||||
false, true);
|
||||
}
|
||||
|
||||
// performance optimization
|
||||
if (IsWaitingForLastMove(MovementPriority::MOVEMENT_NORMAL))
|
||||
{
|
||||
@ -63,13 +70,6 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), zone_name);
|
||||
return bot->TeleportTo(dest);
|
||||
}
|
||||
|
||||
float dis = bot->GetExactDist(dest);
|
||||
if (dis < pathFinderDis)
|
||||
{
|
||||
return MoveTo(dest.getMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), false, false,
|
||||
false, true);
|
||||
}
|
||||
|
||||
float minDelta = M_PI;
|
||||
const float x = bot->GetPositionX();
|
||||
@ -852,18 +852,10 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot)
|
||||
float loRange = 2500.0f;
|
||||
if (bot->GetLevel() < 5)
|
||||
{
|
||||
hiRange /= 3;
|
||||
loRange /= 3;
|
||||
hiRange /= 10;
|
||||
loRange /= 10;
|
||||
}
|
||||
std::vector<WorldLocation> lo_prepared_locs, hi_prepared_locs;
|
||||
|
||||
bool inCity = false;
|
||||
if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetZoneId()))
|
||||
{
|
||||
if (zone->flags & AREA_FLAG_CAPITAL)
|
||||
inCity = true;
|
||||
}
|
||||
|
||||
for (auto& loc : locs)
|
||||
{
|
||||
if (bot->GetMapId() != loc.GetMapId())
|
||||
@ -871,17 +863,17 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot)
|
||||
|
||||
if (bot->GetExactDist(loc) > 2500.0f)
|
||||
continue;
|
||||
|
||||
if (!inCity && bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||
|
||||
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||
bot->GetZoneId())
|
||||
continue;
|
||||
|
||||
if (bot->GetExactDist(loc) < hiRange)
|
||||
if (bot->GetExactDist(loc) < 500.0f)
|
||||
{
|
||||
hi_prepared_locs.push_back(loc);
|
||||
}
|
||||
|
||||
if (bot->GetExactDist(loc) < loRange)
|
||||
if (bot->GetExactDist(loc) < 2500.0f)
|
||||
{
|
||||
lo_prepared_locs.push_back(loc);
|
||||
}
|
||||
@ -908,15 +900,6 @@ WorldPosition NewRpgBaseAction::SelectRandomInnKeeperPos(Player* bot)
|
||||
const std::vector<WorldLocation>& locs = IsAlliance(bot->getRace())
|
||||
? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()]
|
||||
: sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()];
|
||||
|
||||
bool inCity = false;
|
||||
|
||||
if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetZoneId()))
|
||||
{
|
||||
if (zone->flags & AREA_FLAG_CAPITAL)
|
||||
inCity = true;
|
||||
}
|
||||
|
||||
std::vector<WorldLocation> prepared_locs;
|
||||
for (auto& loc : locs)
|
||||
{
|
||||
@ -927,7 +910,7 @@ WorldPosition NewRpgBaseAction::SelectRandomInnKeeperPos(Player* bot)
|
||||
if (bot->GetExactDist(loc) > range)
|
||||
continue;
|
||||
|
||||
if (!inCity && bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||
if (bot->GetMap()->GetZoneId(bot->GetPhaseMask(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()) !=
|
||||
bot->GetZoneId())
|
||||
continue;
|
||||
|
||||
|
||||
@ -116,11 +116,12 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
|
||||
botAI->rpgInfo.status == RPG_GO_INNKEEPER ||
|
||||
botAI->rpgInfo.status == RPG_DO_QUEST;
|
||||
|
||||
bool notHostile = !bot->IsHostileTo(unit); /*|| (unit->ToCreature() && unit->ToCreature()->IsCivilian());*/
|
||||
float aggroRange = 30.0f;
|
||||
if (unit->ToCreature())
|
||||
aggroRange = std::min(30.0f, unit->ToCreature()->GetAggroRange(bot) + 10.0f);
|
||||
bool outOfAggro = unit->ToCreature() && bot->GetDistance(unit) > aggroRange;
|
||||
if (inactiveGrindStatus && outOfAggro)
|
||||
if (inactiveGrindStatus && (outOfAggro || notHostile))
|
||||
{
|
||||
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
|
||||
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
|
||||
|
||||
@ -84,8 +84,6 @@ void AfflictionWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers
|
||||
// Life Tap glyph buff, and Life Tap as filler
|
||||
triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", 39.0f), nullptr)));
|
||||
}
|
||||
|
||||
// ===== AoE Strategy, 3+ enemies =====
|
||||
|
||||
@ -30,6 +30,7 @@ public:
|
||||
creators["seed of corruption"] = &seed_of_corruption;
|
||||
creators["rain of fire"] = &rain_of_fire;
|
||||
creators["demon charge"] = &demon_charge;
|
||||
creators["shadow cleave"] = &shadow_cleave;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -51,6 +52,7 @@ private:
|
||||
static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* demon_charge(PlayerbotAI*) { return new ActionNode("demon charge", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* shadow_cleave(PlayerbotAI*) { return new ActionNode("shadow cleave", nullptr, nullptr, nullptr); }
|
||||
};
|
||||
|
||||
// ===== Single Target Strategy =====
|
||||
@ -95,8 +97,6 @@ void DemonologyWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers
|
||||
// Life Tap glyph buff, and Life Tap as filler
|
||||
triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("meta melee flee check", NextAction::array(0, new NextAction("flee", 39.0f), nullptr)));
|
||||
}
|
||||
|
||||
// ===== AoE Strategy, 3+ enemies =====
|
||||
@ -122,5 +122,6 @@ void MetaMeleeAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("immolation aura active", NextAction::array(0,
|
||||
new NextAction("reach melee", 25.5f),
|
||||
new NextAction("demon charge", 25.0f), nullptr)));
|
||||
new NextAction("demon charge", 25.0f),
|
||||
new NextAction("shadow cleave", 24.5f), nullptr)));
|
||||
}
|
||||
|
||||
@ -91,8 +91,6 @@ void DestructionWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
// Life Tap glyph buff, and Life Tap as filler
|
||||
triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", 39.0f), nullptr)));
|
||||
}
|
||||
|
||||
// ===== AoE Strategy, 3+ enemies =====
|
||||
|
||||
88
src/strategy/warlock/DpsWarlockStrategy.cpp
Normal file
88
src/strategy/warlock/DpsWarlockStrategy.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "DpsWarlockStrategy.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
// This strategy is designed for low-level Warlocks without talents.
|
||||
// All of the important spells/cooldowns have been migrated to
|
||||
// their respective specs.
|
||||
|
||||
// ===== Action Node Factory =====
|
||||
class DpsWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
DpsWarlockStrategyActionNodeFactory()
|
||||
{
|
||||
creators["corruption"] = &corruption;
|
||||
creators["curse of agony"] = &curse_of_agony;
|
||||
creators["immolate"] = &immolate;
|
||||
creators["shadow bolt"] = &shadow_bolt;
|
||||
creators["life tap"] = &life_tap;
|
||||
creators["shadowflame"] = &shadowflame;
|
||||
creators["seed of corruption"] = &seed_of_corruption;
|
||||
creators["rain of fire"] = &rain_of_fire;
|
||||
creators["drain soul"] = &drain_soul;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* curse_of_agony(PlayerbotAI*){return new ActionNode("curse of agony", nullptr, nullptr, nullptr);}
|
||||
static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* seed_of_corruption(PlayerbotAI*){return new ActionNode("seed of corruption", nullptr, nullptr, nullptr);}
|
||||
static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); }
|
||||
static ActionNode* drain_soul(PlayerbotAI*) { return new ActionNode("drain soul", nullptr, nullptr, nullptr); }
|
||||
};
|
||||
|
||||
// ===== Single Target Strategy =====
|
||||
DpsWarlockStrategy::DpsWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new DpsWarlockStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
// ===== Default Actions =====
|
||||
NextAction** DpsWarlockStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("immolate", 5.5f),
|
||||
new NextAction("corruption", 5.4f),
|
||||
new NextAction("curse of agony", 5.3f),
|
||||
new NextAction("shadow bolt", 5.2f),
|
||||
new NextAction("shoot", 5.0f), nullptr);
|
||||
}
|
||||
|
||||
// ===== Trigger Initialization ===
|
||||
void DpsWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericWarlockStrategy::InitTriggers(triggers);
|
||||
|
||||
// Main DoT triggers for high uptime
|
||||
triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 20.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 19.5f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("immolate on attacker", NextAction::array(0, new NextAction("immolate on attacker", 19.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 18.5f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 18.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 17.5f), nullptr)));
|
||||
|
||||
// Drain Soul as execute if target is low HP
|
||||
triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("drain soul", 17.0f), nullptr)));
|
||||
|
||||
// Cast during movement or to activate glyph buff
|
||||
triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", ACTION_DEFAULT + 0.1f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 28.0f), NULL)));
|
||||
}
|
||||
|
||||
// ===== AoE Strategy, 3+ enemies =====
|
||||
void DpsAoeWarlockStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
|
||||
new NextAction("shadowflame", 22.5f),
|
||||
new NextAction("seed of corruption on attacker", 22.0f),
|
||||
new NextAction("seed of corruption", 21.5f),
|
||||
new NextAction("rain of fire", 21.0f), nullptr)));
|
||||
}
|
||||
33
src/strategy/warlock/DpsWarlockStrategy.h
Normal file
33
src/strategy/warlock/DpsWarlockStrategy.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_DPSWARLOCKSTRATEGY_H
|
||||
#define _PLAYERBOT_DPSWARLOCKSTRATEGY_H
|
||||
|
||||
#include "GenericWarlockStrategy.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class DpsWarlockStrategy : public GenericWarlockStrategy
|
||||
{
|
||||
public:
|
||||
DpsWarlockStrategy(PlayerbotAI* botAI);
|
||||
|
||||
std::string const getName() override { return "dps"; }
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
NextAction** getDefaultActions() override;
|
||||
uint32 GetType() const override { return GenericWarlockStrategy::GetType() | STRATEGY_TYPE_DPS; }
|
||||
};
|
||||
|
||||
class DpsAoeWarlockStrategy : public CombatStrategy
|
||||
{
|
||||
public:
|
||||
DpsAoeWarlockStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "aoe"; }
|
||||
};
|
||||
#endif
|
||||
@ -89,18 +89,20 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
|
||||
Player* bot = botAI->GetBot();
|
||||
int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
|
||||
// Firestone/Spellstone triggers
|
||||
if (tab == 2) // Destruction uses Firestone
|
||||
{
|
||||
triggers.push_back(new TriggerNode("no firestone", NextAction::array(0, new NextAction("create firestone", 24.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("firestone", NextAction::array(0, new NextAction("firestone", 24.0f), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("no firestone", NextAction::array(0, new NextAction("create firestone", 24.0f), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("firestone", NextAction::array(0, new NextAction("firestone", 24.0f), nullptr)));
|
||||
}
|
||||
else // Affliction and Demonology use Spellstone
|
||||
{
|
||||
triggers.push_back(new TriggerNode("no spellstone", NextAction::array(0, new NextAction("create spellstone", 24.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("spellstone", NextAction::array(0, new NextAction("spellstone", 24.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("no spellstone",
|
||||
NextAction::array(0, new NextAction("create spellstone", 24.0f), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("spellstone", NextAction::array(0, new NextAction("spellstone", 24.0f), nullptr)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Non-combat strategy for summoning a Imp
|
||||
@ -122,7 +124,8 @@ SummonVoidwalkerStrategy::SummonVoidwalkerStrategy(PlayerbotAI* ai) : NonCombatS
|
||||
|
||||
void SummonVoidwalkerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon voidwalker", 29.0f), NULL)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("no pet", NextAction::array(0, new NextAction("summon voidwalker", 29.0f), NULL)));
|
||||
}
|
||||
|
||||
// Non-combat strategy for summoning a Succubus
|
||||
@ -144,7 +147,8 @@ SummonFelhunterStrategy::SummonFelhunterStrategy(PlayerbotAI* ai) : NonCombatStr
|
||||
|
||||
void SummonFelhunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), NULL)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), NULL)));
|
||||
}
|
||||
|
||||
// Non-combat strategy for summoning a Felguard
|
||||
|
||||
@ -18,7 +18,6 @@ public:
|
||||
std::string const getName() override { return "warlock"; }
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
NextAction** getDefaultActions() override;
|
||||
uint32 GetType() const override { return CombatStrategy::GetType() | STRATEGY_TYPE_RANGED | STRATEGY_TYPE_DPS; }
|
||||
};
|
||||
|
||||
class WarlockBoostStrategy : public Strategy
|
||||
|
||||
@ -15,9 +15,6 @@
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Unit.h"
|
||||
#include "Timer.h"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
// Checks if the bot has less than 32 soul shards, and if so, allows casting Drain Soul
|
||||
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 32; }
|
||||
@ -137,29 +134,9 @@ bool UseSoulstoneSelfAction::Execute(Event event)
|
||||
return UseItem(items[0], ObjectGuid::Empty, nullptr, bot);
|
||||
}
|
||||
|
||||
// Reservation map for soulstone targets (GUID -> reservation expiry in ms)
|
||||
static std::unordered_map<ObjectGuid, uint32> soulstoneReservations;
|
||||
static std::mutex soulstoneReservationsMutex;
|
||||
|
||||
// Helper to clean up expired reservations
|
||||
void CleanupSoulstoneReservations()
|
||||
{
|
||||
uint32 now = getMSTime();
|
||||
std::lock_guard<std::mutex> lock(soulstoneReservationsMutex);
|
||||
for (auto it = soulstoneReservations.begin(); it != soulstoneReservations.end();)
|
||||
{
|
||||
if (it->second <= now)
|
||||
it = soulstoneReservations.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the soulstone item on the bot's master with nc strategy "ss master"
|
||||
bool UseSoulstoneMasterAction::Execute(Event event)
|
||||
{
|
||||
CleanupSoulstoneReservations();
|
||||
|
||||
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
|
||||
if (items.empty())
|
||||
return false;
|
||||
@ -168,23 +145,6 @@ bool UseSoulstoneMasterAction::Execute(Event event)
|
||||
if (!master || HasSoulstoneAura(master))
|
||||
return false;
|
||||
|
||||
uint32 now = getMSTime();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(soulstoneReservationsMutex);
|
||||
if (soulstoneReservations.count(master->GetGUID()) && soulstoneReservations[master->GetGUID()] > now)
|
||||
return false; // Already being soulstoned
|
||||
|
||||
soulstoneReservations[master->GetGUID()] = now + 2500; // Reserve for 2.5 seconds
|
||||
}
|
||||
|
||||
float distance = sServerFacade->GetDistance2d(bot, master);
|
||||
if (distance >= 30.0f)
|
||||
return false;
|
||||
|
||||
if (!bot->IsWithinLOSInMap(master))
|
||||
return false;
|
||||
|
||||
bot->SetSelection(master->GetGUID());
|
||||
return UseItem(items[0], ObjectGuid::Empty, nullptr, master);
|
||||
}
|
||||
@ -192,83 +152,41 @@ bool UseSoulstoneMasterAction::Execute(Event event)
|
||||
// Use the soulstone item on a tank in the group with nc strategy "ss tank"
|
||||
bool UseSoulstoneTankAction::Execute(Event event)
|
||||
{
|
||||
CleanupSoulstoneReservations();
|
||||
|
||||
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
|
||||
if (items.empty())
|
||||
return false;
|
||||
|
||||
Player* chosenTank = nullptr;
|
||||
Player* tank = nullptr;
|
||||
Group* group = bot->GetGroup();
|
||||
uint32 now = getMSTime();
|
||||
|
||||
// First: Try to soulstone the main tank
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsTank(member) && botAI->IsMainTank(member) &&
|
||||
!HasSoulstoneAura(member))
|
||||
if (member && member->IsAlive() && botAI->IsTank(member) && !HasSoulstoneAura(member))
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(soulstoneReservationsMutex);
|
||||
if (soulstoneReservations.count(member->GetGUID()) && soulstoneReservations[member->GetGUID()] > now)
|
||||
continue; // Already being soulstoned
|
||||
|
||||
float distance = sServerFacade->GetDistance2d(bot, member);
|
||||
if (distance < 30.0f && bot->IsWithinLOSInMap(member))
|
||||
{
|
||||
chosenTank = member;
|
||||
soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5 seconds
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no main tank found, soulstone another tank
|
||||
if (!chosenTank)
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsTank(member) && !HasSoulstoneAura(member))
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(soulstoneReservationsMutex);
|
||||
if (soulstoneReservations.count(member->GetGUID()) &&
|
||||
soulstoneReservations[member->GetGUID()] > now)
|
||||
continue; // Already being soulstoned
|
||||
|
||||
float distance = sServerFacade->GetDistance2d(bot, member);
|
||||
if (distance < 30.0f && bot->IsWithinLOSInMap(member))
|
||||
{
|
||||
chosenTank = member;
|
||||
soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5 seconds
|
||||
break;
|
||||
}
|
||||
}
|
||||
tank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!chosenTank)
|
||||
if (!tank)
|
||||
return false;
|
||||
|
||||
bot->SetSelection(chosenTank->GetGUID());
|
||||
return UseItem(items[0], ObjectGuid::Empty, nullptr, chosenTank);
|
||||
bot->SetSelection(tank->GetGUID());
|
||||
return UseItem(items[0], ObjectGuid::Empty, nullptr, tank);
|
||||
}
|
||||
|
||||
// Use the soulstone item on a healer in the group with nc strategy "ss healer"
|
||||
bool UseSoulstoneHealerAction::Execute(Event event)
|
||||
{
|
||||
CleanupSoulstoneReservations();
|
||||
|
||||
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", "soulstone");
|
||||
if (items.empty())
|
||||
return false;
|
||||
|
||||
Player* healer = nullptr;
|
||||
Group* group = bot->GetGroup();
|
||||
uint32 now = getMSTime();
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
@ -276,20 +194,8 @@ bool UseSoulstoneHealerAction::Execute(Event event)
|
||||
Player* member = gref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsHeal(member) && !HasSoulstoneAura(member))
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(soulstoneReservationsMutex);
|
||||
if (soulstoneReservations.count(member->GetGUID()) &&
|
||||
soulstoneReservations[member->GetGUID()] > now)
|
||||
continue; // Already being soulstoned
|
||||
|
||||
float distance = sServerFacade->GetDistance2d(bot, member);
|
||||
if (distance < 30.0f && bot->IsWithinLOSInMap(member))
|
||||
{
|
||||
healer = member;
|
||||
soulstoneReservations[healer->GetGUID()] = now + 2500; // Reserve for 2.5 seconds
|
||||
break;
|
||||
}
|
||||
}
|
||||
healer = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "DemonologyWarlockStrategy.h"
|
||||
#include "DestructionWarlockStrategy.h"
|
||||
#include "TankWarlockStrategy.h"
|
||||
#include "DpsWarlockStrategy.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "GenericWarlockNonCombatStrategy.h"
|
||||
#include "NamedObjectContext.h"
|
||||
@ -28,16 +29,16 @@ public:
|
||||
creators["boost"] = &WarlockStrategyFactoryInternal::boost;
|
||||
creators["cc"] = &WarlockStrategyFactoryInternal::cc;
|
||||
creators["pet"] = &WarlockStrategyFactoryInternal::pet;
|
||||
creators["affli"] = &WarlockStrategyFactoryInternal::affliction;
|
||||
creators["affli aoe"] = &WarlockStrategyFactoryInternal::affliction_aoe;
|
||||
creators["demo"] = &WarlockStrategyFactoryInternal::demonology;
|
||||
creators["demo aoe"] = &WarlockStrategyFactoryInternal::demonology_aoe;
|
||||
creators["destro"] = &WarlockStrategyFactoryInternal::destruction;
|
||||
creators["destro aoe"] = &WarlockStrategyFactoryInternal::destruction_aoe;
|
||||
creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe;
|
||||
creators["dps"] = &WarlockStrategyFactoryInternal::dps;
|
||||
creators["aoe"] = &WarlockStrategyFactoryInternal::aoe;
|
||||
creators["curse of elements"] = &WarlockStrategyFactoryInternal::curse_of_elements;
|
||||
creators["imp"] = &WarlockStrategyFactoryInternal::imp;
|
||||
creators["voidwalker"] = &WarlockStrategyFactoryInternal::voidwalker;
|
||||
creators["succubus"] = &WarlockStrategyFactoryInternal::succubus;
|
||||
creators["felhunter"] = &WarlockStrategyFactoryInternal::felhunter;
|
||||
creators["felguard"] = &WarlockStrategyFactoryInternal::felguard;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -46,16 +47,16 @@ private:
|
||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
|
||||
static Strategy* boost(PlayerbotAI* botAI) { return new WarlockBoostStrategy(botAI); }
|
||||
static Strategy* cc(PlayerbotAI* botAI) { return new WarlockCcStrategy(botAI); }
|
||||
static Strategy* affliction(PlayerbotAI* botAI) { return new AfflictionWarlockStrategy(botAI); }
|
||||
static Strategy* affliction_aoe(PlayerbotAI* botAI) { return new AfflictionWarlockAoeStrategy(botAI); }
|
||||
static Strategy* demonology(PlayerbotAI* botAI) { return new DemonologyWarlockStrategy(botAI); }
|
||||
static Strategy* demonology_aoe(PlayerbotAI* botAI) { return new DemonologyWarlockAoeStrategy(botAI); }
|
||||
static Strategy* destruction(PlayerbotAI* botAI) { return new DestructionWarlockStrategy(botAI); }
|
||||
static Strategy* destruction_aoe(PlayerbotAI* botAI) { return new DestructionWarlockAoeStrategy(botAI); }
|
||||
static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); }
|
||||
static Strategy* dps(PlayerbotAI* botAI) { return new DpsWarlockStrategy(botAI); }
|
||||
static Strategy* aoe(PlayerbotAI* botAI) { return new DpsAoeWarlockStrategy(botAI); }
|
||||
static Strategy* curse_of_elements(PlayerbotAI* botAI) { return new WarlockCurseOfTheElementsStrategy(botAI); }
|
||||
static Strategy* imp(PlayerbotAI* ai) { return new SummonImpStrategy(ai); }
|
||||
static Strategy* voidwalker(PlayerbotAI* ai) { return new SummonVoidwalkerStrategy(ai); }
|
||||
static Strategy* succubus(PlayerbotAI* ai) { return new SummonSuccubusStrategy(ai); }
|
||||
static Strategy* felhunter(PlayerbotAI* ai) { return new SummonFelhunterStrategy(ai); }
|
||||
static Strategy* felguard(PlayerbotAI* ai) { return new SummonFelguardStrategy(ai); }
|
||||
};
|
||||
|
||||
class WarlockCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
@ -64,16 +65,10 @@ public:
|
||||
WarlockCombatStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
|
||||
{
|
||||
creators["tank"] = &WarlockCombatStrategyFactoryInternal::tank;
|
||||
creators["affli"] = &WarlockCombatStrategyFactoryInternal::affliction;
|
||||
creators["demo"] = &WarlockCombatStrategyFactoryInternal::demonology;
|
||||
creators["destro"] = &WarlockCombatStrategyFactoryInternal::destruction;
|
||||
}
|
||||
|
||||
private:
|
||||
static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); }
|
||||
static Strategy* affliction(PlayerbotAI* botAI) { return new AfflictionWarlockStrategy(botAI); }
|
||||
static Strategy* demonology(PlayerbotAI* botAI) { return new DemonologyWarlockStrategy(botAI); }
|
||||
static Strategy* destruction(PlayerbotAI* botAI) { return new DestructionWarlockStrategy(botAI); }
|
||||
};
|
||||
|
||||
class NonCombatBuffStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
@ -81,6 +76,11 @@ class NonCombatBuffStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||
public:
|
||||
NonCombatBuffStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
|
||||
{
|
||||
creators["imp"] = &NonCombatBuffStrategyFactoryInternal::imp;
|
||||
creators["voidwalker"] = &NonCombatBuffStrategyFactoryInternal::voidwalker;
|
||||
creators["succubus"] = &NonCombatBuffStrategyFactoryInternal::succubus;
|
||||
creators["felhunter"] = &NonCombatBuffStrategyFactoryInternal::felhunter;
|
||||
creators["felguard"] = &NonCombatBuffStrategyFactoryInternal::felguard;
|
||||
creators["ss self"] = &NonCombatBuffStrategyFactoryInternal::soulstone_self;
|
||||
creators["ss master"] = &NonCombatBuffStrategyFactoryInternal::soulstone_master;
|
||||
creators["ss tank"] = &NonCombatBuffStrategyFactoryInternal::soulstone_tank;
|
||||
@ -88,6 +88,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static Strategy* imp(PlayerbotAI* ai) { return new SummonImpStrategy(ai); }
|
||||
static Strategy* voidwalker(PlayerbotAI* ai) { return new SummonVoidwalkerStrategy(ai); }
|
||||
static Strategy* succubus(PlayerbotAI* ai) { return new SummonSuccubusStrategy(ai); }
|
||||
static Strategy* felhunter(PlayerbotAI* ai) { return new SummonFelhunterStrategy(ai); }
|
||||
static Strategy* felguard(PlayerbotAI* ai) { return new SummonFelguardStrategy(ai); }
|
||||
static Strategy* soulstone_self(PlayerbotAI* ai) { return new SoulstoneSelfStrategy(ai); }
|
||||
static Strategy* soulstone_master(PlayerbotAI* ai) { return new SoulstoneMasterStrategy(ai); }
|
||||
static Strategy* soulstone_tank(PlayerbotAI* ai) { return new SoulstoneTankStrategy(ai); }
|
||||
@ -132,8 +137,6 @@ public:
|
||||
creators["metamorphosis"] = &WarlockTriggerFactoryInternal::metamorphosis;
|
||||
creators["demonic empowerment"] = &WarlockTriggerFactoryInternal::demonic_empowerment;
|
||||
creators["immolation aura active"] = &WarlockTriggerFactoryInternal::immolation_aura_active;
|
||||
creators["metamorphosis not active"] = &WarlockTriggerFactoryInternal::metamorphosis_not_active;
|
||||
creators["meta melee flee check"] = &WarlockTriggerFactoryInternal::meta_melee_flee_check;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -170,8 +173,6 @@ private:
|
||||
static Trigger* metamorphosis(PlayerbotAI* ai) { return new MetamorphosisTrigger(ai); }
|
||||
static Trigger* demonic_empowerment(PlayerbotAI* ai) { return new DemonicEmpowermentTrigger(ai); }
|
||||
static Trigger* immolation_aura_active(PlayerbotAI* ai) { return new ImmolationAuraActiveTrigger(ai); }
|
||||
static Trigger* metamorphosis_not_active(PlayerbotAI* ai) { return new MetamorphosisNotActiveTrigger(ai); }
|
||||
static Trigger* meta_melee_flee_check(PlayerbotAI* ai) { return new MetaMeleeEnemyTooCloseForSpellTrigger(ai); }
|
||||
};
|
||||
|
||||
class WarlockAiObjectContextInternal : public NamedObjectContext<Action>
|
||||
|
||||
@ -271,18 +271,4 @@ class MoltenCoreTrigger : public HasAuraTrigger
|
||||
public:
|
||||
MoltenCoreTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "molten core") {}
|
||||
};
|
||||
|
||||
class MetamorphosisNotActiveTrigger : public HasNoAuraTrigger
|
||||
{
|
||||
public:
|
||||
MetamorphosisNotActiveTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "metamorphosis") {}
|
||||
};
|
||||
|
||||
class MetaMeleeEnemyTooCloseForSpellTrigger : public TwoTriggers
|
||||
{
|
||||
public:
|
||||
MetaMeleeEnemyTooCloseForSpellTrigger(PlayerbotAI* ai)
|
||||
: TwoTriggers(ai, "enemy too close for spell", "metamorphosis not active") {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user