mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge branch 'master' into master
This commit is contained in:
@@ -501,9 +501,10 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
|||||||
# "mana" (bots have infinite mana)
|
# "mana" (bots have infinite mana)
|
||||||
# "power" (bots have infinite energy, rage, and runic power)
|
# "power" (bots have infinite energy, rage, and runic power)
|
||||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||||
|
# "raid" (bots use cheats implemented into raid strategies)
|
||||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,taxi")
|
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,taxi")
|
||||||
# Default: taxi is enabled
|
# Default: taxi is enabled
|
||||||
AiPlayerbot.BotCheats = "taxi"
|
AiPlayerbot.BotCheats = "taxi,raid"
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -1435,8 +1436,8 @@ AiPlayerbot.PremadeSpecLink.7.5.80 = -023222301004-05032331331013501120331251
|
|||||||
|
|
||||||
AiPlayerbot.PremadeSpecName.8.0 = arcane pve
|
AiPlayerbot.PremadeSpecName.8.0 = arcane pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751
|
AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751
|
||||||
AiPlayerbot.PremadeSpecLink.8.0.60 = 23000503110033014032310150532
|
AiPlayerbot.PremadeSpecLink.8.0.60 = 230005231100330150323102500321
|
||||||
AiPlayerbot.PremadeSpecLink.8.0.80 = 23000523310033015032310250532-03-203203001
|
AiPlayerbot.PremadeSpecLink.8.0.80 = 230005231100330150323102505321-03-203303001
|
||||||
AiPlayerbot.PremadeSpecName.8.1 = fire pve
|
AiPlayerbot.PremadeSpecName.8.1 = fire pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
|
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
|
||||||
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
|
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
|
||||||
@@ -1448,7 +1449,7 @@ AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--053303031320310003015223135
|
|||||||
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
|
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
|
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
|
||||||
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
|
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
|
||||||
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120311351-023303031
|
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120321351-023302031
|
||||||
AiPlayerbot.PremadeSpecName.8.4 = arcane pvp
|
AiPlayerbot.PremadeSpecName.8.4 = arcane pvp
|
||||||
AiPlayerbot.PremadeSpecGlyph.8.4 = 42735,43364,42738,43360,43357,42752
|
AiPlayerbot.PremadeSpecGlyph.8.4 = 42735,43364,42738,43360,43357,42752
|
||||||
AiPlayerbot.PremadeSpecLink.8.4.60 = 205323200122032103303102015221
|
AiPlayerbot.PremadeSpecLink.8.4.60 = 205323200122032103303102015221
|
||||||
|
|||||||
8
data/sql/playerbots/base/playerbots_account_type.sql
Normal file
8
data/sql/playerbots/base/playerbots_account_type.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
DROP TABLE IF EXISTS `playerbots_account_type`;
|
||||||
|
CREATE TABLE `playerbots_account_type` (
|
||||||
|
`account_id` int unsigned NOT NULL,
|
||||||
|
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
|
||||||
|
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`account_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- Create playerbots_account_type table for tracking accounts assignments
|
||||||
|
DROP TABLE IF EXISTS `playerbots_account_type`;
|
||||||
|
CREATE TABLE `playerbots_account_type` (
|
||||||
|
`account_id` int unsigned NOT NULL,
|
||||||
|
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
|
||||||
|
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`account_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';
|
||||||
|
|
||||||
@@ -305,22 +305,22 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
break;
|
break;
|
||||||
case CLASS_MAGE:
|
case CLASS_MAGE:
|
||||||
if (tab == 0)
|
if (tab == 0)
|
||||||
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
|
engine->addStrategiesNoInit("arcane", nullptr);
|
||||||
else if (tab == 1)
|
else if (tab == 1)
|
||||||
{
|
{
|
||||||
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
|
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
|
||||||
{
|
{
|
||||||
engine->addStrategiesNoInit("frostfire", "frostfire aoe", nullptr);
|
engine->addStrategiesNoInit("frostfire", nullptr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
|
engine->addStrategiesNoInit("fire", nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
|
engine->addStrategiesNoInit("frost", nullptr);
|
||||||
|
|
||||||
engine->addStrategiesNoInit("dps", "dps assist", "cure", nullptr);
|
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
|
||||||
break;
|
break;
|
||||||
case CLASS_WARRIOR:
|
case CLASS_WARRIOR:
|
||||||
if (tab == 2)
|
if (tab == 2)
|
||||||
|
|||||||
@@ -2950,14 +2950,19 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
|||||||
|
|
||||||
if (!itemTarget)
|
if (!itemTarget)
|
||||||
{
|
{
|
||||||
|
// Exception for Deep Freeze (44572) - allow cast for damage on immune targets (e.g., bosses)
|
||||||
if (target->IsImmunedToSpell(spellInfo))
|
if (target->IsImmunedToSpell(spellInfo))
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
|
if (spellid != 44572) // Deep Freeze
|
||||||
{
|
{
|
||||||
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
|
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
|
||||||
target->GetName(), spellid, bot->GetName());
|
{
|
||||||
|
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
|
||||||
|
target->GetName(), spellid, bot->GetName());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
// Otherwise, allow Deep Freeze even if immune
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bot != target && sServerFacade->GetDistance2d(bot, target) > sPlayerbotAIConfig->sightDistance)
|
if (bot != target && sServerFacade->GetDistance2d(bot, target) > sPlayerbotAIConfig->sightDistance)
|
||||||
|
|||||||
@@ -118,7 +118,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);
|
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);
|
||||||
@@ -157,7 +156,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
|
||||||
randomBotQuestIds);
|
randomBotQuestIds);
|
||||||
|
|
||||||
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
|
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
|
||||||
disallowedGameObjects);
|
disallowedGameObjects);
|
||||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||||
@@ -355,7 +354,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
{
|
{
|
||||||
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
|
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
|
||||||
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
|
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
|
||||||
|
|
||||||
if (!value.empty())
|
if (!value.empty())
|
||||||
{
|
{
|
||||||
size_t commaPos = value.find(',');
|
size_t commaPos = value.find(',');
|
||||||
@@ -457,7 +456,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
botCheats.clear();
|
botCheats.clear();
|
||||||
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi"),
|
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi,raid"),
|
||||||
botCheats);
|
botCheats);
|
||||||
|
|
||||||
botCheatMask = 0;
|
botCheatMask = 0;
|
||||||
@@ -472,6 +471,8 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
botCheatMask |= (uint32)BotCheatMask::mana;
|
botCheatMask |= (uint32)BotCheatMask::mana;
|
||||||
if (std::find(botCheats.begin(), botCheats.end(), "power") != botCheats.end())
|
if (std::find(botCheats.begin(), botCheats.end(), "power") != botCheats.end())
|
||||||
botCheatMask |= (uint32)BotCheatMask::power;
|
botCheatMask |= (uint32)BotCheatMask::power;
|
||||||
|
if (std::find(botCheats.begin(), botCheats.end(), "raid") != botCheats.end())
|
||||||
|
botCheatMask |= (uint32)BotCheatMask::raid;
|
||||||
|
|
||||||
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AllowedLogFiles", ""),
|
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AllowedLogFiles", ""),
|
||||||
allowedLogFiles);
|
allowedLogFiles);
|
||||||
@@ -625,6 +626,9 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign account types after accounts are created
|
||||||
|
sRandomPlayerbotMgr->AssignAccountTypes();
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->enabled)
|
if (sPlayerbotAIConfig->enabled)
|
||||||
{
|
{
|
||||||
sRandomPlayerbotMgr->Init();
|
sRandomPlayerbotMgr->Init();
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ enum class BotCheatMask : uint32
|
|||||||
health = 4,
|
health = 4,
|
||||||
mana = 8,
|
mana = 8,
|
||||||
power = 16,
|
power = 16,
|
||||||
maxMask = 32
|
raid = 32,
|
||||||
|
maxMask = 64
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HealingManaEfficiency : uint8
|
enum class HealingManaEfficiency : uint8
|
||||||
|
|||||||
@@ -584,8 +584,8 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bot->SaveToDB(false, false);
|
bot->SaveToDB(false, false);
|
||||||
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(bot->GetGUID().GetCounter());
|
bool addClassBot = sRandomPlayerbotMgr->IsAccountType(accountId, 2);
|
||||||
if (addClassBot && master && isRandomAccount && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
|
if (addClassBot && master && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
|
||||||
{
|
{
|
||||||
// PlayerbotFactory factory(bot, master->GetLevel());
|
// PlayerbotFactory factory(bot, master->GetLevel());
|
||||||
// factory.Randomize(false);
|
// factory.Randomize(false);
|
||||||
|
|||||||
@@ -393,37 +393,118 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
|
|||||||
return std::move(botName);
|
return std::move(botName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
|
||||||
|
// or determining it dynamically based on MaxRandomBots, EnablePeriodicOnlineOffline and its ratio,
|
||||||
|
// and AddClassAccountPoolSize. The system also factors in the types of existing account, as assigned by
|
||||||
|
// AssignAccountTypes()
|
||||||
uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
|
uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
|
||||||
{
|
{
|
||||||
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
|
// Reset account types if features are disabled
|
||||||
// or determining it dynamically based on the WOTLK condition, max random bots, rotation pool size,
|
// Reset is done here to precede needed accounts calculations
|
||||||
// and additional class account pool size.
|
if (sPlayerbotAIConfig->maxRandomBots == 0 || sPlayerbotAIConfig->addClassAccountPoolSize == 0)
|
||||||
|
{
|
||||||
|
if (sPlayerbotAIConfig->maxRandomBots == 0)
|
||||||
|
{
|
||||||
|
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 1");
|
||||||
|
LOG_INFO("playerbots", "MaxRandomBots set to 0, any RNDbot accounts (type 1) will be unassigned (type 0)");
|
||||||
|
}
|
||||||
|
if (sPlayerbotAIConfig->addClassAccountPoolSize == 0)
|
||||||
|
{
|
||||||
|
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 2");
|
||||||
|
LOG_INFO("playerbots", "AddClassAccountPoolSize set to 0, any AddClass accounts (type 2) will be unassigned (type 0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for DB to reflect the change, up to 1 second max. This is needed to make sure other logs don't show wrong info
|
||||||
|
for (int waited = 0; waited < 1000; waited += 50)
|
||||||
|
{
|
||||||
|
QueryResult res = PlayerbotsDatabase.Query("SELECT COUNT(*) FROM playerbots_account_type WHERE account_type IN ({}, {})",
|
||||||
|
sPlayerbotAIConfig->maxRandomBots == 0 ? 1 : -1,
|
||||||
|
sPlayerbotAIConfig->addClassAccountPoolSize == 0 ? 2 : -1);
|
||||||
|
|
||||||
|
if (!res || res->Fetch()[0].Get<uint64>() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Extra 50ms fixed delay for safety.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
|
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
|
||||||
if (sPlayerbotAIConfig->randomBotAccountCount > 0)
|
if (sPlayerbotAIConfig->randomBotAccountCount > 0)
|
||||||
return sPlayerbotAIConfig->randomBotAccountCount;
|
return sPlayerbotAIConfig->randomBotAccountCount;
|
||||||
|
|
||||||
// Avoid creating accounts if both maxRandom & ClassBots are set to zero.
|
// Check existing account types
|
||||||
if (sPlayerbotAIConfig->maxRandomBots == 0 &&
|
uint32 existingRndBotAccounts = 0;
|
||||||
sPlayerbotAIConfig->addClassAccountPoolSize == 0)
|
uint32 existingAddClassAccounts = 0;
|
||||||
return 0;
|
uint32 existingUnassignedAccounts = 0;
|
||||||
|
|
||||||
//bool isWOTLK = sWorld->getIntConfig(CONFIG_EXPANSION) == EXPANSION_WRATH_OF_THE_LICH_KING; //not used, line marked for removal.
|
QueryResult typeCheck = PlayerbotsDatabase.Query("SELECT account_type, COUNT(*) FROM playerbots_account_type GROUP BY account_type");
|
||||||
|
if (typeCheck)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = typeCheck->Fetch();
|
||||||
|
uint8 accountType = fields[0].Get<uint8>();
|
||||||
|
uint32 count = fields[1].Get<uint32>();
|
||||||
|
|
||||||
// Determine divisor based on WOTLK condition
|
if (accountType == 0) existingUnassignedAccounts = count;
|
||||||
|
else if (accountType == 1) existingRndBotAccounts = count;
|
||||||
|
else if (accountType == 2) existingAddClassAccounts = count;
|
||||||
|
} while (typeCheck->NextRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine divisor based on Death Knight login eligibility and requested A&H faction ratio
|
||||||
int divisor = CalculateAvailableCharsPerAccount();
|
int divisor = CalculateAvailableCharsPerAccount();
|
||||||
|
|
||||||
// Calculate max bots
|
// Calculate max bots
|
||||||
int maxBots = sPlayerbotAIConfig->maxRandomBots;
|
int maxBots = sPlayerbotAIConfig->maxRandomBots;
|
||||||
// Take perodic online - offline into account
|
// Take periodic online - offline into account
|
||||||
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
||||||
{
|
{
|
||||||
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
|
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate base accounts, add class account pool size, and add 1 as a fixed offset
|
// Calculate number of accounts needed for RNDbots
|
||||||
uint32 baseAccounts = maxBots / divisor;
|
// Result is rounded up for maxBots not cleanly divisible by the divisor
|
||||||
return baseAccounts + sPlayerbotAIConfig->addClassAccountPoolSize + 1;
|
uint32 neededRndBotAccounts = (maxBots + divisor - 1) / divisor;
|
||||||
|
uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize;
|
||||||
|
|
||||||
|
// Start with existing total
|
||||||
|
uint32 existingTotal = existingRndBotAccounts + existingAddClassAccounts + existingUnassignedAccounts;
|
||||||
|
|
||||||
|
// Calculate shortfalls after using unassigned accounts
|
||||||
|
uint32 availableUnassigned = existingUnassignedAccounts;
|
||||||
|
uint32 additionalAccountsNeeded = 0;
|
||||||
|
|
||||||
|
// Check RNDbot needs
|
||||||
|
if (neededRndBotAccounts > existingRndBotAccounts)
|
||||||
|
{
|
||||||
|
uint32 rndBotShortfall = neededRndBotAccounts - existingRndBotAccounts;
|
||||||
|
if (rndBotShortfall <= availableUnassigned)
|
||||||
|
availableUnassigned -= rndBotShortfall;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
additionalAccountsNeeded += (rndBotShortfall - availableUnassigned);
|
||||||
|
availableUnassigned = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check AddClass needs
|
||||||
|
if (neededAddClassAccounts > existingAddClassAccounts)
|
||||||
|
{
|
||||||
|
uint32 addClassShortfall = neededAddClassAccounts - existingAddClassAccounts;
|
||||||
|
if (addClassShortfall <= availableUnassigned)
|
||||||
|
availableUnassigned -= addClassShortfall;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
additionalAccountsNeeded += (addClassShortfall - availableUnassigned);
|
||||||
|
availableUnassigned = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return existing total plus any additional accounts needed
|
||||||
|
return existingTotal + additionalAccountsNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
|
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
|
||||||
@@ -475,8 +556,9 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
|||||||
LOG_INFO("playerbots", "Deleting all random bot characters and accounts...");
|
LOG_INFO("playerbots", "Deleting all random bot characters and accounts...");
|
||||||
|
|
||||||
// First execute all the cleanup SQL commands
|
// First execute all the cleanup SQL commands
|
||||||
// Clear playerbots_random_bots table
|
// Clear playerbots_random_bots and playerbots_account_type
|
||||||
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots");
|
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots");
|
||||||
|
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_type");
|
||||||
|
|
||||||
// Get the database names dynamically
|
// Get the database names dynamically
|
||||||
std::string loginDBName = LoginDatabase.GetConnectionInfo()->database;
|
std::string loginDBName = LoginDatabase.GetConnectionInfo()->database;
|
||||||
|
|||||||
@@ -515,15 +515,174 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
|||||||
// setActivityPercentage(activityPercentage);
|
// setActivityPercentage(activityPercentage);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Assigns accounts as RNDbot accounts (type 1) based on MaxRandomBots and EnablePeriodicOnlineOffline and its ratio,
|
||||||
|
// and assigns accounts as AddClass accounts (type 2) based AddClassAccountPoolSize. Type 1 and 2 assignments are
|
||||||
|
// permenant, unless MaxRandomBots or AddClassAccountPoolSize are set to 0. If so, their associated accounts will
|
||||||
|
// be unassigned (type 0)
|
||||||
|
void RandomPlayerbotMgr::AssignAccountTypes()
|
||||||
|
{
|
||||||
|
LOG_INFO("playerbots", "Assigning account types for random bot accounts...");
|
||||||
|
|
||||||
|
// Clear existing filtered lists
|
||||||
|
rndBotTypeAccounts.clear();
|
||||||
|
addClassTypeAccounts.clear();
|
||||||
|
|
||||||
|
// First, get ALL randombot accounts from the database
|
||||||
|
std::vector<uint32> allRandomBotAccounts;
|
||||||
|
QueryResult allAccounts = LoginDatabase.Query(
|
||||||
|
"SELECT id FROM account WHERE username LIKE '{}%%' ORDER BY id",
|
||||||
|
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
|
||||||
|
|
||||||
|
if (allAccounts)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = allAccounts->Fetch();
|
||||||
|
uint32 accountId = fields[0].Get<uint32>();
|
||||||
|
allRandomBotAccounts.push_back(accountId);
|
||||||
|
} while (allAccounts->NextRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("playerbots", "Found {} total randombot accounts in database", allRandomBotAccounts.size());
|
||||||
|
|
||||||
|
// Check existing assignments
|
||||||
|
QueryResult existingAssignments = PlayerbotsDatabase.Query("SELECT account_id, account_type FROM playerbots_account_type");
|
||||||
|
std::map<uint32, uint8> currentAssignments;
|
||||||
|
|
||||||
|
if (existingAssignments)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = existingAssignments->Fetch();
|
||||||
|
uint32 accountId = fields[0].Get<uint32>();
|
||||||
|
uint8 accountType = fields[1].Get<uint8>();
|
||||||
|
currentAssignments[accountId] = accountType;
|
||||||
|
} while (existingAssignments->NextRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark ALL randombot accounts as unassigned if not already assigned
|
||||||
|
for (uint32 accountId : allRandomBotAccounts)
|
||||||
|
{
|
||||||
|
if (currentAssignments.find(accountId) == currentAssignments.end())
|
||||||
|
{
|
||||||
|
PlayerbotsDatabase.Execute("INSERT INTO playerbots_account_type (account_id, account_type) VALUES ({}, 0) ON DUPLICATE KEY UPDATE account_type = account_type", accountId);
|
||||||
|
currentAssignments[accountId] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate needed RNDbot accounts
|
||||||
|
uint32 neededRndBotAccounts = 0;
|
||||||
|
if (sPlayerbotAIConfig->maxRandomBots > 0)
|
||||||
|
{
|
||||||
|
int divisor = RandomPlayerbotFactory::CalculateAvailableCharsPerAccount();
|
||||||
|
int maxBots = sPlayerbotAIConfig->maxRandomBots;
|
||||||
|
|
||||||
|
// Take periodic online-offline into account
|
||||||
|
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
||||||
|
{
|
||||||
|
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate base accounts needed for RNDbots, ensuring round up for maxBots not cleanly divisible by the divisor
|
||||||
|
neededRndBotAccounts = (maxBots + divisor - 1) / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count existing assigned accounts
|
||||||
|
uint32 existingRndBotAccounts = 0;
|
||||||
|
uint32 existingAddClassAccounts = 0;
|
||||||
|
|
||||||
|
for (const auto& [accountId, accountType] : currentAssignments)
|
||||||
|
{
|
||||||
|
if (accountType == 1) existingRndBotAccounts++;
|
||||||
|
else if (accountType == 2) existingAddClassAccounts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign RNDbot accounts from lowest position if needed
|
||||||
|
if (existingRndBotAccounts < neededRndBotAccounts)
|
||||||
|
{
|
||||||
|
uint32 toAssign = neededRndBotAccounts - existingRndBotAccounts;
|
||||||
|
uint32 assigned = 0;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < allRandomBotAccounts.size() && assigned < toAssign; i++)
|
||||||
|
{
|
||||||
|
uint32 accountId = allRandomBotAccounts[i];
|
||||||
|
if (currentAssignments[accountId] == 0) // Unassigned
|
||||||
|
{
|
||||||
|
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 1, assignment_date = NOW() WHERE account_id = {}", accountId);
|
||||||
|
currentAssignments[accountId] = 1;
|
||||||
|
assigned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assigned < toAssign)
|
||||||
|
{
|
||||||
|
LOG_ERROR("playerbots", "Not enough unassigned accounts to fulfill RNDbot requirements. Need {} more accounts.", toAssign - assigned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign AddClass accounts from highest position if needed
|
||||||
|
uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize;
|
||||||
|
|
||||||
|
if (existingAddClassAccounts < neededAddClassAccounts)
|
||||||
|
{
|
||||||
|
uint32 toAssign = neededAddClassAccounts - existingAddClassAccounts;
|
||||||
|
uint32 assigned = 0;
|
||||||
|
|
||||||
|
for (int i = allRandomBotAccounts.size() - 1; i >= 0 && assigned < toAssign; i--)
|
||||||
|
{
|
||||||
|
uint32 accountId = allRandomBotAccounts[i];
|
||||||
|
if (currentAssignments[accountId] == 0) // Unassigned
|
||||||
|
{
|
||||||
|
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 2, assignment_date = NOW() WHERE account_id = {}", accountId);
|
||||||
|
currentAssignments[accountId] = 2;
|
||||||
|
assigned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assigned < toAssign)
|
||||||
|
{
|
||||||
|
LOG_ERROR("playerbots", "Not enough unassigned accounts to fulfill AddClass requirements. Need {} more accounts.", toAssign - assigned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate filtered account lists with ALL accounts of each type
|
||||||
|
for (const auto& [accountId, accountType] : currentAssignments)
|
||||||
|
{
|
||||||
|
if (accountType == 1) rndBotTypeAccounts.push_back(accountId);
|
||||||
|
else if (accountType == 2) addClassTypeAccounts.push_back(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("playerbots", "Account type assignment complete: {} RNDbot accounts, {} AddClass accounts, {} unassigned",
|
||||||
|
rndBotTypeAccounts.size(), addClassTypeAccounts.size(),
|
||||||
|
currentAssignments.size() - rndBotTypeAccounts.size() - addClassTypeAccounts.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomPlayerbotMgr::IsAccountType(uint32 accountId, uint8 accountType)
|
||||||
|
{
|
||||||
|
QueryResult result = PlayerbotsDatabase.Query("SELECT 1 FROM playerbots_account_type WHERE account_id = {} AND account_type = {}", accountId, accountType);
|
||||||
|
return result != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs-in bots in 4 phases. Phase 1 logs Alliance bots up to how much is expected according to the faction ratio,
|
||||||
|
// and Phase 2 logs-in the remainder Horde bots to reach the total maxAllowedBotCount. If maxAllowedBotCount is not
|
||||||
|
// reached after Phase 2, the function goes back to log-in Alliance bots and reach maxAllowedBotCount. This is done
|
||||||
|
// because not every account is guaranteed 5A/5H bots, so the true ratio might be skewed by few percentages. Finally,
|
||||||
|
// Phase 4 is reached if and only if the value of RandomBotAccountCount is lower than it should.
|
||||||
uint32 RandomPlayerbotMgr::AddRandomBots()
|
uint32 RandomPlayerbotMgr::AddRandomBots()
|
||||||
{
|
{
|
||||||
uint32 maxAllowedBotCount = GetEventValue(0, "bot_count");
|
uint32 maxAllowedBotCount = GetEventValue(0, "bot_count");
|
||||||
|
static time_t missingBotsTimer = 0;
|
||||||
|
|
||||||
if (currentBots.size() < maxAllowedBotCount)
|
if (currentBots.size() < maxAllowedBotCount)
|
||||||
{
|
{
|
||||||
|
// Calculate how many bots to add
|
||||||
maxAllowedBotCount -= currentBots.size();
|
maxAllowedBotCount -= currentBots.size();
|
||||||
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
|
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
|
||||||
|
|
||||||
|
// Single RNG instance for all shuffling
|
||||||
|
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
|
||||||
|
|
||||||
|
// Only need to track the Alliance count, as it's in Phase 1
|
||||||
uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio;
|
uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio;
|
||||||
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio;
|
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio;
|
||||||
|
|
||||||
@@ -535,26 +694,42 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
|||||||
allowedAllianceCount++;
|
allowedAllianceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 allowedHordeCount = maxAllowedBotCount - allowedAllianceCount;
|
// Determine which accounts to use based on EnablePeriodicOnlineOffline
|
||||||
|
std::vector<uint32> accountsToUse;
|
||||||
for (std::vector<uint32>::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin();
|
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
||||||
i != sPlayerbotAIConfig->randomBotAccounts.end(); i++)
|
|
||||||
{
|
{
|
||||||
uint32 accountId = *i;
|
|
||||||
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
|
|
||||||
{
|
|
||||||
// minus addclass bots account
|
|
||||||
int32 baseAccount =
|
|
||||||
RandomPlayerbotFactory::CalculateTotalAccountCount() - sPlayerbotAIConfig->addClassAccountPoolSize;
|
|
||||||
|
|
||||||
if (baseAccount <= 0 || baseAccount > sPlayerbotAIConfig->randomBotAccounts.size())
|
// Calculate how many accounts can be used
|
||||||
{
|
// With enablePeriodicOnlineOffline, don't use all of rndBotTypeAccounts right away. Fraction results are rounded up
|
||||||
LOG_ERROR("playerbots", "Account calculation error with PeriodicOnlineOffline");
|
uint32 accountsToUseCount = (rndBotTypeAccounts.size() + sPlayerbotAIConfig->periodicOnlineOfflineRatio - 1)
|
||||||
return 0;
|
/ sPlayerbotAIConfig->periodicOnlineOfflineRatio;
|
||||||
}
|
|
||||||
uint32 index = urand(0, baseAccount - 1);
|
// Randomly select accounts
|
||||||
accountId = sPlayerbotAIConfig->randomBotAccounts[index];
|
std::vector<uint32> shuffledAccounts = rndBotTypeAccounts;
|
||||||
|
std::shuffle(shuffledAccounts.begin(), shuffledAccounts.end(), rng);
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < accountsToUseCount && i < shuffledAccounts.size(); i++)
|
||||||
|
{
|
||||||
|
accountsToUse.push_back(shuffledAccounts[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
accountsToUse = rndBotTypeAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-map all characters from selected accounts
|
||||||
|
struct CharacterInfo
|
||||||
|
{
|
||||||
|
uint32 guid;
|
||||||
|
uint8 rClass;
|
||||||
|
uint8 rRace;
|
||||||
|
uint32 accountId;
|
||||||
|
};
|
||||||
|
std::vector<CharacterInfo> allCharacters;
|
||||||
|
|
||||||
|
for (uint32 accountId : accountsToUse)
|
||||||
|
{
|
||||||
CharacterDatabasePreparedStatement* stmt =
|
CharacterDatabasePreparedStatement* stmt =
|
||||||
CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
|
CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
|
||||||
stmt->SetData(0, accountId);
|
stmt->SetData(0, accountId);
|
||||||
@@ -562,87 +737,115 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
|
|||||||
if (!result)
|
if (!result)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::vector<GuidClassRaceInfo> allGuidInfos;
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
GuidClassRaceInfo info;
|
CharacterInfo info;
|
||||||
info.guid = fields[0].Get<uint32>();
|
info.guid = fields[0].Get<uint32>();
|
||||||
info.rClass = fields[1].Get<uint8>();
|
info.rClass = fields[1].Get<uint8>();
|
||||||
info.rRace = fields[2].Get<uint8>();
|
info.rRace = fields[2].Get<uint8>();
|
||||||
allGuidInfos.push_back(info);
|
info.accountId = accountId;
|
||||||
|
allCharacters.push_back(info);
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
|
|
||||||
// random shuffle for class balance
|
|
||||||
std::mt19937 rnd(time(0));
|
|
||||||
std::shuffle(allGuidInfos.begin(), allGuidInfos.end(), rnd);
|
|
||||||
|
|
||||||
std::vector<uint32> guids;
|
|
||||||
for (const auto& info : allGuidInfos)
|
|
||||||
{
|
|
||||||
ObjectGuid::LowType guid = info.guid;
|
|
||||||
uint32 rClass = info.rClass;
|
|
||||||
uint32 rRace = info.rRace;
|
|
||||||
|
|
||||||
if (GetEventValue(guid, "add"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (GetEventValue(guid, "logout"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (GetPlayerBot(guid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (std::find(currentBots.begin(), currentBots.end(), guid) != currentBots.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->disableDeathKnightLogin)
|
|
||||||
{
|
|
||||||
if (rClass == CLASS_DEATH_KNIGHT)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 isAlliance = IsAlliance(rRace);
|
|
||||||
bool factionNotAllowed = (!allowedAllianceCount && isAlliance) || (!allowedHordeCount && !isAlliance);
|
|
||||||
|
|
||||||
if (factionNotAllowed)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isAlliance)
|
|
||||||
{
|
|
||||||
allowedAllianceCount--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
allowedHordeCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline
|
|
||||||
? urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
|
|
||||||
sPlayerbotAIConfig->maxRandomBotInWorldTime)
|
|
||||||
: sPlayerbotAIConfig->permanantlyInWorldTime;
|
|
||||||
|
|
||||||
SetEventValue(guid, "add", 1, add_time);
|
|
||||||
SetEventValue(guid, "logout", 0, 0);
|
|
||||||
currentBots.push_back(guid);
|
|
||||||
|
|
||||||
maxAllowedBotCount--;
|
|
||||||
if (!maxAllowedBotCount)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!maxAllowedBotCount)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shuffle for class balance
|
||||||
|
std::shuffle(allCharacters.begin(), allCharacters.end(), rng);
|
||||||
|
|
||||||
|
// Separate characters by faction for phased login
|
||||||
|
std::vector<CharacterInfo> allianceChars;
|
||||||
|
std::vector<CharacterInfo> hordeChars;
|
||||||
|
|
||||||
|
for (const auto& charInfo : allCharacters)
|
||||||
|
{
|
||||||
|
if (IsAlliance(charInfo.rRace))
|
||||||
|
allianceChars.push_back(charInfo);
|
||||||
|
|
||||||
|
else
|
||||||
|
hordeChars.push_back(charInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lambda to handle bot login logic
|
||||||
|
auto tryLoginBot = [&](const CharacterInfo& charInfo) -> bool
|
||||||
|
{
|
||||||
|
if (GetEventValue(charInfo.guid, "add") ||
|
||||||
|
GetEventValue(charInfo.guid, "logout") ||
|
||||||
|
GetPlayerBot(charInfo.guid) ||
|
||||||
|
std::find(currentBots.begin(), currentBots.end(), charInfo.guid) != currentBots.end() ||
|
||||||
|
(sPlayerbotAIConfig->disableDeathKnightLogin && charInfo.rClass == CLASS_DEATH_KNIGHT))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline
|
||||||
|
? urand(sPlayerbotAIConfig->minRandomBotInWorldTime,
|
||||||
|
sPlayerbotAIConfig->maxRandomBotInWorldTime)
|
||||||
|
: sPlayerbotAIConfig->permanantlyInWorldTime;
|
||||||
|
|
||||||
|
SetEventValue(charInfo.guid, "add", 1, add_time);
|
||||||
|
SetEventValue(charInfo.guid, "logout", 0, 0);
|
||||||
|
currentBots.push_back(charInfo.guid);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// PHASE 1: Log-in Alliance bots up to allowedAllianceCount
|
||||||
|
for (const auto& charInfo : allianceChars)
|
||||||
|
{
|
||||||
|
if (!allowedAllianceCount)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (tryLoginBot(charInfo))
|
||||||
|
{
|
||||||
|
maxAllowedBotCount--;
|
||||||
|
allowedAllianceCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHASE 2: Log-in Horde bots up to maxAllowedBotCount
|
||||||
|
for (const auto& charInfo : hordeChars)
|
||||||
|
{
|
||||||
|
if (!maxAllowedBotCount)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (tryLoginBot(charInfo))
|
||||||
|
maxAllowedBotCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHASE 3: If maxAllowedBotCount wasn't reached, log-in more Alliance bots
|
||||||
|
for (const auto& charInfo : allianceChars)
|
||||||
|
{
|
||||||
|
if (!maxAllowedBotCount)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (tryLoginBot(charInfo))
|
||||||
|
maxAllowedBotCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHASE 4: An error is given if maxAllowedBotCount is still not reached
|
||||||
if (maxAllowedBotCount)
|
if (maxAllowedBotCount)
|
||||||
LOG_ERROR("playerbots",
|
{
|
||||||
"Not enough random bot accounts available. Try to increase RandomBotAccountCount "
|
if (missingBotsTimer == 0)
|
||||||
"in your conf file",
|
missingBotsTimer = time(nullptr);
|
||||||
ceil(maxAllowedBotCount / 10));
|
|
||||||
|
if (time(nullptr) - missingBotsTimer >= 10)
|
||||||
|
{
|
||||||
|
int divisor = RandomPlayerbotFactory::CalculateAvailableCharsPerAccount();
|
||||||
|
uint32 moreAccountsNeeded = (maxAllowedBotCount + divisor - 1) / divisor;
|
||||||
|
LOG_ERROR("playerbots",
|
||||||
|
"Can't log-in all the requested bots. Try increasing RandomBotAccountCount in your conf file.\n"
|
||||||
|
"{} more accounts needed.", moreAccountsNeeded);
|
||||||
|
missingBotsTimer = 0; // Reset timer so error is not spammed every tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
missingBotsTimer = 0; // Reset timer if logins for this interval were successful
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
missingBotsTimer = 0; // Reset timer if there's enough bots
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentBots.size();
|
return currentBots.size();
|
||||||
@@ -1165,7 +1368,6 @@ void RandomPlayerbotMgr::ScheduleChangeStrategy(uint32 bot, uint32 time)
|
|||||||
bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
||||||
{
|
{
|
||||||
ObjectGuid botGUID = ObjectGuid::Create<HighGuid::Player>(bot);
|
ObjectGuid botGUID = ObjectGuid::Create<HighGuid::Player>(bot);
|
||||||
|
|
||||||
Player* player = GetPlayerBot(botGUID);
|
Player* player = GetPlayerBot(botGUID);
|
||||||
PlayerbotAI* botAI = player ? GET_PLAYERBOT_AI(player) : nullptr;
|
PlayerbotAI* botAI = player ? GET_PLAYERBOT_AI(player) : nullptr;
|
||||||
|
|
||||||
@@ -1875,24 +2077,21 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
|
|
||||||
void RandomPlayerbotMgr::PrepareAddclassCache()
|
void RandomPlayerbotMgr::PrepareAddclassCache()
|
||||||
{
|
{
|
||||||
/// @FIXME: Modifying RandomBotAccountCount may cause the original addclass bots to be converted into rndbots,
|
// Using accounts marked as type 2 (AddClass)
|
||||||
// which needs to be fixed by separating the two accounts in implementation
|
|
||||||
size_t poolSize = sPlayerbotAIConfig->addClassAccountPoolSize;
|
|
||||||
size_t start = sPlayerbotAIConfig->randomBotAccounts.size() > poolSize
|
|
||||||
? sPlayerbotAIConfig->randomBotAccounts.size() - poolSize
|
|
||||||
: 0;
|
|
||||||
int32 collected = 0;
|
int32 collected = 0;
|
||||||
for (size_t i = start; i < sPlayerbotAIConfig->randomBotAccounts.size(); i++)
|
|
||||||
|
for (uint32 accountId : addClassTypeAccounts)
|
||||||
{
|
{
|
||||||
for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++)
|
for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++)
|
||||||
{
|
{
|
||||||
if (claz == 10)
|
if (claz == 10)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QueryResult results = CharacterDatabase.Query(
|
QueryResult results = CharacterDatabase.Query(
|
||||||
"SELECT guid, race FROM characters "
|
"SELECT guid, race FROM characters "
|
||||||
"WHERE account = {} AND class = '{}' AND online = 0 "
|
"WHERE account = {} AND class = '{}' AND online = 0",
|
||||||
"ORDER BY account DESC",
|
accountId, claz);
|
||||||
sPlayerbotAIConfig->randomBotAccounts[i], claz);
|
|
||||||
if (results)
|
if (results)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
@@ -1907,7 +2106,8 @@ void RandomPlayerbotMgr::PrepareAddclassCache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_INFO("playerbots", ">> {} characters collected for addclass command.", collected);
|
|
||||||
|
LOG_INFO("playerbots", ">> {} characters collected for addclass command from {} AddClass accounts.", collected, addClassTypeAccounts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RandomPlayerbotMgr::Init()
|
void RandomPlayerbotMgr::Init()
|
||||||
@@ -2286,10 +2486,13 @@ bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)
|
|||||||
ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(bot);
|
ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(bot);
|
||||||
if (!sPlayerbotAIConfig->IsInRandomAccountList(sCharacterCache->GetCharacterAccountIdByGuid(guid)))
|
if (!sPlayerbotAIConfig->IsInRandomAccountList(sCharacterCache->GetCharacterAccountIdByGuid(guid)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (std::find(currentBots.begin(), currentBots.end(), bot) != currentBots.end())
|
if (std::find(currentBots.begin(), currentBots.end(), bot) != currentBots.end())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
||||||
{
|
{
|
||||||
if (bot && GET_PLAYERBOT_AI(bot))
|
if (bot && GET_PLAYERBOT_AI(bot))
|
||||||
@@ -2301,23 +2504,37 @@ bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
|||||||
{
|
{
|
||||||
return IsAddclassBot(bot->GetGUID().GetCounter());
|
return IsAddclassBot(bot->GetGUID().GetCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RandomPlayerbotMgr::IsAddclassBot(ObjectGuid::LowType bot)
|
bool RandomPlayerbotMgr::IsAddclassBot(ObjectGuid::LowType bot)
|
||||||
{
|
{
|
||||||
ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(bot);
|
ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(bot);
|
||||||
|
|
||||||
|
// Check the cache with faction considerations
|
||||||
for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++)
|
for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++)
|
||||||
{
|
{
|
||||||
if (claz == 10)
|
if (claz == 10)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (uint8 isAlliance = 0; isAlliance <= 1; isAlliance++)
|
for (uint8 isAlliance = 0; isAlliance <= 1; isAlliance++)
|
||||||
{
|
{
|
||||||
if (addclassCache[GetTeamClassIdx(isAlliance, claz)].find(guid) !=
|
if (addclassCache[GetTeamClassIdx(isAlliance, claz)].find(guid) !=
|
||||||
addclassCache[GetTeamClassIdx(isAlliance, claz)].end())
|
addclassCache[GetTeamClassIdx(isAlliance, claz)].end())
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If not in cache, check the account type
|
||||||
|
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid);
|
||||||
|
if (accountId && IsAccountType(accountId, 2)) // Type 2 = AddClass
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ public:
|
|||||||
|
|
||||||
bool IsEmpty() { return !lastChangeTime; }
|
bool IsEmpty() { return !lastChangeTime; }
|
||||||
|
|
||||||
public:
|
|
||||||
uint32 value;
|
uint32 value;
|
||||||
uint32 lastChangeTime;
|
uint32 lastChangeTime;
|
||||||
uint32 validIn;
|
uint32 validIn;
|
||||||
@@ -104,10 +103,6 @@ public:
|
|||||||
void LogPlayerLocation();
|
void LogPlayerLocation();
|
||||||
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
|
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
|
||||||
|
|
||||||
private:
|
|
||||||
//void ScaleBotActivity();
|
|
||||||
|
|
||||||
public:
|
|
||||||
uint32 activeBots = 0;
|
uint32 activeBots = 0;
|
||||||
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
|
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
|
||||||
bool IsRandomBot(Player* bot);
|
bool IsRandomBot(Player* bot);
|
||||||
@@ -189,6 +184,11 @@ public:
|
|||||||
};
|
};
|
||||||
std::map<uint32, LevelBracket> zone2LevelBracket;
|
std::map<uint32, LevelBracket> zone2LevelBracket;
|
||||||
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
||||||
|
|
||||||
|
// Account type management
|
||||||
|
void AssignAccountTypes();
|
||||||
|
bool IsAccountType(uint32 accountId, uint8 accountType);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnBotLoginInternal(Player* const bot) override;
|
void OnBotLoginInternal(Player* const bot) override;
|
||||||
|
|
||||||
@@ -218,10 +218,8 @@ private:
|
|||||||
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
|
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
|
||||||
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
|
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
|
||||||
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
|
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
|
||||||
|
|
||||||
std::vector<Player*> players;
|
std::vector<Player*> players;
|
||||||
uint32 processTicks;
|
uint32 processTicks;
|
||||||
|
|
||||||
|
|
||||||
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
|
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
|
||||||
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
|
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
|
||||||
@@ -230,6 +228,12 @@ private:
|
|||||||
std::list<uint32> currentBots;
|
std::list<uint32> currentBots;
|
||||||
uint32 bgBotsCount;
|
uint32 bgBotsCount;
|
||||||
uint32 playersLevel;
|
uint32 playersLevel;
|
||||||
|
|
||||||
|
// Account lists
|
||||||
|
std::vector<uint32> rndBotTypeAccounts; // Accounts marked as RNDbot (type 1)
|
||||||
|
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
|
||||||
|
|
||||||
|
//void ScaleBotActivity(); // Deprecated function
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()
|
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ BotCheatMask CheatAction::GetCheatMask(std::string const cheat)
|
|||||||
if (cheat == "power")
|
if (cheat == "power")
|
||||||
return BotCheatMask::power;
|
return BotCheatMask::power;
|
||||||
|
|
||||||
|
if (cheat == "raid")
|
||||||
|
return BotCheatMask::raid;
|
||||||
|
|
||||||
return BotCheatMask::none;
|
return BotCheatMask::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +73,8 @@ std::string const CheatAction::GetCheatName(BotCheatMask cheatMask)
|
|||||||
return "mana";
|
return "mana";
|
||||||
case BotCheatMask::power:
|
case BotCheatMask::power:
|
||||||
return "power";
|
return "power";
|
||||||
|
case BotCheatMask::raid:
|
||||||
|
return "raid";
|
||||||
default:
|
default:
|
||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ bool DrinkAction::Execute(Event event)
|
|||||||
float delay;
|
float delay;
|
||||||
|
|
||||||
if (!bot->InBattleground())
|
if (!bot->InBattleground())
|
||||||
delay = 27000.0f * (100 - p) / 100.0f;
|
delay = 18000.0f * (100 - p) / 100.0f;
|
||||||
else
|
else
|
||||||
delay = 20000.0f * (100 - p) / 100.0f;
|
delay = 12000.0f * (100 - p) / 100.0f;
|
||||||
|
|
||||||
botAI->SetNextCheckDelay(delay);
|
botAI->SetNextCheckDelay(delay);
|
||||||
|
|
||||||
bot->AddAura(24707, bot);
|
bot->AddAura(25990, bot);
|
||||||
return true;
|
return true;
|
||||||
// return botAI->CastSpell(24707, bot);
|
// return botAI->CastSpell(24707, bot);
|
||||||
}
|
}
|
||||||
@@ -90,13 +90,13 @@ bool EatAction::Execute(Event event)
|
|||||||
float delay;
|
float delay;
|
||||||
|
|
||||||
if (!bot->InBattleground())
|
if (!bot->InBattleground())
|
||||||
delay = 27000.0f * (100 - p) / 100.0f;
|
delay = 18000.0f * (100 - p) / 100.0f;
|
||||||
else
|
else
|
||||||
delay = 20000.0f * (100 - p) / 100.0f;
|
delay = 12000.0f * (100 - p) / 100.0f;
|
||||||
|
|
||||||
botAI->SetNextCheckDelay(delay);
|
botAI->SetNextCheckDelay(delay);
|
||||||
|
|
||||||
bot->AddAura(24707, bot);
|
bot->AddAura(25990, bot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ArcaneMageStrategy.h"
|
#include "ArcaneMageStrategy.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
// ===== Action Node Factory =====
|
||||||
class ArcaneMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
class ArcaneMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -15,69 +15,44 @@ public:
|
|||||||
creators["arcane blast"] = &arcane_blast;
|
creators["arcane blast"] = &arcane_blast;
|
||||||
creators["arcane barrage"] = &arcane_barrage;
|
creators["arcane barrage"] = &arcane_barrage;
|
||||||
creators["arcane missiles"] = &arcane_missiles;
|
creators["arcane missiles"] = &arcane_missiles;
|
||||||
// creators["firebolt"] = &firebolt;
|
creators["fire blast"] = &fire_blast;
|
||||||
|
creators["frostbolt"] = &frostbolt;
|
||||||
|
creators["arcane power"] = &arcane_power;
|
||||||
|
creators["icy veins"] = &icy_veins;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ActionNode* arcane_blast([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* arcane_blast(PlayerbotAI*) { return new ActionNode("arcane blast", nullptr, nullptr, nullptr); }
|
||||||
{
|
static ActionNode* arcane_barrage(PlayerbotAI*) { return new ActionNode("arcane barrage", nullptr, nullptr, nullptr); }
|
||||||
return new ActionNode("arcane blast",
|
static ActionNode* arcane_missiles(PlayerbotAI*) { return new ActionNode("arcane missiles", nullptr, nullptr, nullptr); }
|
||||||
/*P*/ nullptr,
|
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
|
||||||
/*A*/ NextAction::array(0, new NextAction("arcane missiles"), nullptr),
|
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
|
||||||
/*C*/ nullptr);
|
static ActionNode* arcane_power(PlayerbotAI*) { return new ActionNode("arcane power", nullptr, nullptr, nullptr); }
|
||||||
}
|
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
|
||||||
|
|
||||||
static ActionNode* arcane_barrage([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("arcane barrage",
|
|
||||||
/*P*/ nullptr,
|
|
||||||
/*A*/ nullptr,
|
|
||||||
/*C*/ nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ActionNode* arcane_missiles([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("arcane missiles",
|
|
||||||
/*P*/ nullptr,
|
|
||||||
/*A*/ NextAction::array(0, new NextAction("fireball"), nullptr),
|
|
||||||
/*C*/ nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static ActionNode* firebolt([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
// {
|
|
||||||
// return new ActionNode ("firebolt",
|
|
||||||
// /*P*/ nullptr,
|
|
||||||
// /*A*/ NextAction::array(0, new NextAction("shoot"), nullptr),
|
|
||||||
// /*C*/ nullptr);
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ===== Single Target Strategy =====
|
||||||
ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
ArcaneMageStrategy::ArcaneMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
||||||
{
|
{
|
||||||
actionNodeFactories.Add(new ArcaneMageStrategyActionNodeFactory());
|
actionNodeFactories.Add(new ArcaneMageStrategyActionNodeFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Default Actions =====
|
||||||
NextAction** ArcaneMageStrategy::getDefaultActions()
|
NextAction** ArcaneMageStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("arcane blast", ACTION_DEFAULT + 0.3f),
|
return NextAction::array(0, new NextAction("arcane blast", 5.6f),
|
||||||
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // arcane immune target
|
new NextAction("arcane missiles", 5.5f),
|
||||||
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
|
new NextAction("arcane barrage", 5.4f), // cast while moving
|
||||||
new NextAction("shoot", ACTION_DEFAULT), nullptr);
|
new NextAction("fire blast", 5.3f), // cast while moving if arcane barrage isn't available/learned
|
||||||
|
new NextAction("frostbolt", 5.2f), // for arcane immune targets
|
||||||
|
new NextAction("shoot", 5.1f), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Trigger Initialization ===
|
||||||
void ArcaneMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void ArcaneMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericMageStrategy::InitTriggers(triggers);
|
GenericMageStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(
|
// Proc Trigger
|
||||||
new TriggerNode("arcane blast stack", NextAction::array(0, new NextAction("arcane missiles", 15.0f), NULL)));
|
triggers.push_back(new TriggerNode("arcane blast 4 stacks and missile barrage", NextAction::array(0, new NextAction("arcane missiles", 15.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArcaneMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|
||||||
{
|
|
||||||
// triggers.push_back(new TriggerNode(
|
|
||||||
// "high aoe",
|
|
||||||
// NextAction::array(0, new NextAction("arcane explosion", 39.0f), NULL)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("blizzard", 40.0f), NULL)));
|
|
||||||
}
|
|
||||||
@@ -20,13 +20,4 @@ public:
|
|||||||
NextAction** getDefaultActions() override;
|
NextAction** getDefaultActions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArcaneMageAoeStrategy : public CombatStrategy
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArcaneMageAoeStrategy(PlayerbotAI* ai) : CombatStrategy(ai) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
|
||||||
std::string const getName() override { return "arcane aoe"; }
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,42 +4,71 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FireMageStrategy.h"
|
#include "FireMageStrategy.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
|
|
||||||
NextAction** FireMageStrategy::getDefaultActions()
|
// ===== Action Node Factory =====
|
||||||
|
class FireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("fireball", ACTION_DEFAULT + 0.3f),
|
public:
|
||||||
new NextAction("frostbolt", ACTION_DEFAULT + 0.2f), // fire immune target
|
FireMageStrategyActionNodeFactory()
|
||||||
new NextAction("fire blast", ACTION_DEFAULT + 0.1f), // cast during movement
|
{
|
||||||
new NextAction("shoot", ACTION_DEFAULT), NULL);
|
creators["fireball"] = &fireball;
|
||||||
|
creators["frostbolt"] = &frostbolt;
|
||||||
|
creators["fire blast"] = &fire_blast;
|
||||||
|
creators["pyroblast"] = &pyroblast;
|
||||||
|
creators["scorch"] = &scorch;
|
||||||
|
creators["living bomb"] = &living_bomb;
|
||||||
|
creators["combustion"] = &combustion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", nullptr, nullptr, nullptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Single Target Strategy =====
|
||||||
|
FireMageStrategy::FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
||||||
|
{
|
||||||
|
actionNodeFactories.Add(new FireMageStrategyActionNodeFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Default Actions =====
|
||||||
|
NextAction** FireMageStrategy::getDefaultActions()
|
||||||
|
{
|
||||||
|
return NextAction::array(0, new NextAction("fireball", 5.3f),
|
||||||
|
new NextAction("frostbolt", 5.2f), // fire immune target
|
||||||
|
new NextAction("fire blast", 5.1f), // cast during movement
|
||||||
|
new NextAction("shoot", 5.0f), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Trigger Initialization =====
|
||||||
void FireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void FireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericMageStrategy::InitTriggers(triggers);
|
GenericMageStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
// triggers.push_back(new TriggerNode("pyroblast", NextAction::array(0, new NextAction("pyroblast", 10.0f),
|
// Debuff Triggers
|
||||||
// nullptr)));
|
triggers.push_back(new TriggerNode("improved scorch", NextAction::array(0, new NextAction("scorch", 19.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 18.5f), nullptr)));
|
||||||
new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
// Proc Trigger
|
||||||
new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 50.0f), nullptr)));
|
triggers.push_back(new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 19.0f), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("dragon's
|
|
||||||
// breath", 70.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FireMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
// Combat strategy to run to melee for Dragon's Breath and Blast Wave
|
||||||
|
// Disabled by default for the Fire/Frostfire spec
|
||||||
|
// To enable, type "co +firestarter"
|
||||||
|
// To disable, type "co -firestarter"
|
||||||
|
FirestarterStrategy::FirestarterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
||||||
|
|
||||||
|
void FirestarterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
// higher priority to cast before move away
|
triggers.push_back(new TriggerNode(
|
||||||
triggers.push_back(
|
"blast wave off cd and medium aoe",
|
||||||
new TriggerNode("medium aoe", NextAction::array(0,
|
NextAction::array(0, new NextAction("reach melee", 25.5f), nullptr)));
|
||||||
new NextAction("dragon's breath", ACTION_MOVE + 9),
|
|
||||||
new NextAction("flamestrike", ACTION_MOVE + 8),
|
|
||||||
new NextAction("blast wave", ACTION_MOVE + 7),
|
|
||||||
new NextAction("living bomb on attackers", 21.0f),
|
|
||||||
new NextAction("blizzard", 20.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,19 @@ class PlayerbotAI;
|
|||||||
class FireMageStrategy : public GenericMageStrategy
|
class FireMageStrategy : public GenericMageStrategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) {}
|
FireMageStrategy(PlayerbotAI* botAI);
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "fire"; }
|
std::string const getName() override { return "fire"; }
|
||||||
NextAction** getDefaultActions() override;
|
NextAction** getDefaultActions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FireMageAoeStrategy : public CombatStrategy
|
class FirestarterStrategy : public CombatStrategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FireMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
FirestarterStrategy(PlayerbotAI* botAI);
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "fire aoe"; }
|
std::string const getName() override { return "firestarter"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,31 +4,56 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FrostFireMageStrategy.h"
|
#include "FrostFireMageStrategy.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
NextAction** FrostFireMageStrategy::getDefaultActions()
|
// ===== Action Node Factory =====
|
||||||
|
class FrostFireMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("frostfire bolt", ACTION_DEFAULT + 0.1f),
|
public:
|
||||||
new NextAction("shoot", ACTION_DEFAULT), NULL);
|
FrostFireMageStrategyActionNodeFactory()
|
||||||
|
{
|
||||||
|
creators["frostfire bolt"] = &frostfire_bolt;
|
||||||
|
creators["fire blast"] = &fire_blast;
|
||||||
|
creators["pyroblast"] = &pyroblast;
|
||||||
|
creators["combustion"] = &combustion;
|
||||||
|
creators["icy veins"] = &icy_veins;
|
||||||
|
creators["scorch"] = &scorch;
|
||||||
|
creators["living bomb"] = &living_bomb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", nullptr, nullptr, nullptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== Single Target Strategy =====
|
||||||
|
FrostFireMageStrategy::FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
||||||
|
{
|
||||||
|
actionNodeFactories.Add(new FrostFireMageStrategyActionNodeFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Default Actions =====
|
||||||
|
NextAction** FrostFireMageStrategy::getDefaultActions()
|
||||||
|
{
|
||||||
|
return NextAction::array(0, new NextAction("frostfire bolt", 5.2f),
|
||||||
|
new NextAction("fire blast", 5.1f), // cast during movement
|
||||||
|
new NextAction("shoot", 5.0f), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Trigger Initialization =====
|
||||||
void FrostFireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void FrostFireMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericMageStrategy::InitTriggers(triggers);
|
GenericMageStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(
|
// Debuff Triggers
|
||||||
new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
|
triggers.push_back(new TriggerNode("improved scorch", NextAction::array(0, new NextAction("scorch", 19.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 18.5f), nullptr)));
|
||||||
new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 50.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 60.0f), nullptr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrostFireMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
// Proc Trigger
|
||||||
{
|
triggers.push_back(new TriggerNode("hot streak", NextAction::array(0, new NextAction("pyroblast", 25.0f), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("flamestrike", 20.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("living bomb", NextAction::array(0, new NextAction("living bomb", 25.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,11 @@ class PlayerbotAI;
|
|||||||
class FrostFireMageStrategy : public GenericMageStrategy
|
class FrostFireMageStrategy : public GenericMageStrategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) {}
|
FrostFireMageStrategy(PlayerbotAI* botAI);
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
std::string const getName() override { return "frostfire"; }
|
std::string const getName() override { return "frostfire"; }
|
||||||
NextAction** getDefaultActions() override;
|
NextAction** getDefaultActions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrostFireMageAoeStrategy : public CombatStrategy
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FrostFireMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
|
||||||
std::string const getName() override { return "frostfire aoe"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
// ===== Action Node Factory =====
|
||||||
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -16,96 +17,66 @@ public:
|
|||||||
creators["ice barrier"] = &ice_barrier;
|
creators["ice barrier"] = &ice_barrier;
|
||||||
creators["summon water elemental"] = &summon_water_elemental;
|
creators["summon water elemental"] = &summon_water_elemental;
|
||||||
creators["deep freeze"] = &deep_freeze;
|
creators["deep freeze"] = &deep_freeze;
|
||||||
|
creators["icy veins"] = &icy_veins;
|
||||||
|
creators["frostbolt"] = &frostbolt;
|
||||||
|
creators["ice lance"] = &ice_lance;
|
||||||
|
creators["fire blast"] = &fire_blast;
|
||||||
|
creators["fireball"] = &fireball;
|
||||||
|
creators["frostfire bolt"] = &frostfire_bolt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ActionNode* cold_snap([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", nullptr, nullptr, nullptr); }
|
||||||
{
|
static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", nullptr, nullptr, nullptr); }
|
||||||
return new ActionNode("cold snap",
|
static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", nullptr, nullptr, nullptr); }
|
||||||
/*P*/ nullptr,
|
static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", nullptr, nullptr, nullptr); }
|
||||||
/*A*/ nullptr,
|
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", nullptr, nullptr, nullptr); }
|
||||||
/*C*/ nullptr);
|
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", nullptr, nullptr, nullptr); }
|
||||||
}
|
static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", nullptr, nullptr, nullptr); }
|
||||||
|
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", nullptr, nullptr, nullptr); }
|
||||||
static ActionNode* ice_barrier([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", nullptr, nullptr, nullptr); }
|
||||||
{
|
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", nullptr, nullptr, nullptr); }
|
||||||
return new ActionNode("ice barrier",
|
|
||||||
/*P*/ nullptr,
|
|
||||||
/*A*/ nullptr,
|
|
||||||
/*C*/ nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ActionNode* summon_water_elemental([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("summon water elemental",
|
|
||||||
/*P*/ nullptr,
|
|
||||||
/*A*/ nullptr,
|
|
||||||
/*C*/ nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ActionNode* deep_freeze([[maybe_unused]] PlayerbotAI* botAI)
|
|
||||||
{
|
|
||||||
return new ActionNode("deep freeze",
|
|
||||||
/*P*/ nullptr,
|
|
||||||
/*A*/ NextAction::array(0, new NextAction("ice lance"), nullptr),
|
|
||||||
/*C*/ nullptr);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ===== Single Target Strategy =====
|
||||||
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
||||||
{
|
{
|
||||||
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
|
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Default Actions =====
|
||||||
NextAction** FrostMageStrategy::getDefaultActions()
|
NextAction** FrostMageStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("frostbolt", ACTION_DEFAULT + 0.3f),
|
return NextAction::array(0, new NextAction("frostbolt", 5.4f),
|
||||||
new NextAction("fire blast", ACTION_DEFAULT + 0.2f), // cast during movement
|
new NextAction("ice lance", 5.3f), // cast during movement
|
||||||
new NextAction("shoot", ACTION_DEFAULT + 0.1f),
|
new NextAction("fire blast", 5.2f), // cast during movement if ice lance is not learned
|
||||||
new NextAction("fireball", ACTION_DEFAULT), nullptr);
|
new NextAction("shoot", 5.1f),
|
||||||
|
new NextAction("fireball", 5.0f), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Trigger Initialization ===
|
||||||
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericMageStrategy::InitTriggers(triggers);
|
GenericMageStrategy::InitTriggers(triggers);
|
||||||
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr)));
|
|
||||||
// No logic currently for cold snap usage.. possibly use right after icy veins drops off?
|
|
||||||
// triggers.push_back(new TriggerNode("cold snap", NextAction::array(0, new NextAction("cold snap", 50.0f),
|
|
||||||
// nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Pet/Defensive triggers
|
||||||
"no pet", NextAction::array(0, new NextAction("summon water elemental", ACTION_HIGH), nullptr)));
|
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon water elemental", 30.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 29.5f), nullptr)));
|
||||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", ACTION_HIGH + 1), nullptr)));
|
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
||||||
new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", ACTION_NORMAL), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", ACTION_HIGH + 1), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"brain freeze", NextAction::array(0, new NextAction("frostfire bolt", ACTION_NORMAL + 3), nullptr)));
|
|
||||||
// Combo cast the last charge of fingers of frost for double crits.
|
|
||||||
// Should only do this on the final charge of FoF.
|
|
||||||
triggers.push_back(new TriggerNode("fingers of frost single",
|
|
||||||
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
|
|
||||||
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
|
|
||||||
// May not need this, frostbolt is the default action so probably don't need to specify.
|
|
||||||
// Maybe uncomment if you find the mage is prioritising auxillary spells while this buff is up, and wasting the
|
|
||||||
// proc. triggers.push_back(new TriggerNode("fingers of frost double", NextAction::array(0, new
|
|
||||||
// NextAction("frostbolt", ACTION_NORMAL), nullptr)));
|
|
||||||
|
|
||||||
// Same 2-spell combo for various freeze procs
|
// Proc/Freeze triggers
|
||||||
triggers.push_back(new TriggerNode("frost nova on target",
|
triggers.push_back(new TriggerNode("brain freeze", NextAction::array(0, new NextAction("frostfire bolt", 19.5f), nullptr)));
|
||||||
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
|
triggers.push_back(new TriggerNode("fingers of frost", NextAction::array(0,
|
||||||
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
|
new NextAction("deep freeze", 19.0f),
|
||||||
triggers.push_back(new TriggerNode("frostbite on target",
|
new NextAction("frostbolt", 18.0f), nullptr)));
|
||||||
NextAction::array(0, new NextAction("frostbolt", ACTION_NORMAL + 2),
|
|
||||||
new NextAction("deep freeze", ACTION_NORMAL + 1), nullptr)));
|
triggers.push_back(new TriggerNode("frostbite on target", NextAction::array(0,
|
||||||
}
|
new NextAction("deep freeze", 19.0f),
|
||||||
|
new NextAction("frostbolt", 18.0f), nullptr)));
|
||||||
void FrostMageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|
||||||
{
|
triggers.push_back(new TriggerNode("frost nova on target", NextAction::array(0,
|
||||||
triggers.push_back(
|
new NextAction("deep freeze", 19.0f),
|
||||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("blizzard", ACTION_HIGH), nullptr)));
|
new NextAction("frostbolt", 18.0f), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("light aoe", NextAction::array(0, new NextAction("cone of cold", ACTION_HIGH + 1), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,4 @@ public:
|
|||||||
NextAction** getDefaultActions() override;
|
NextAction** getDefaultActions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrostMageAoeStrategy : public CombatStrategy
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FrostMageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
|
||||||
std::string const getName() override { return "frost aoe"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GenericMageNonCombatStrategy.h"
|
#include "GenericMageNonCombatStrategy.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
class GenericMageNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
class GenericMageNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
@@ -52,35 +52,23 @@ void GenericMageNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigg
|
|||||||
{
|
{
|
||||||
NonCombatStrategy::InitTriggers(triggers);
|
NonCombatStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("arcane intellect", NextAction::array(0, new NextAction("arcane intellect", 21.0f), nullptr)));
|
||||||
new TriggerNode("arcane intellect", NextAction::array(0, new NextAction("arcane intellect", 21.0f), nullptr)));
|
triggers.push_back(new TriggerNode("no focus magic", NextAction::array(0, new NextAction("focus magic on party", 19.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("no focus magic", NextAction::array(0, new NextAction("focus magic on party", 19.0f), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("no drink", NextAction::array(0, new NextAction("conjure water", 16.0f),
|
|
||||||
// nullptr))); triggers.push_back(new TriggerNode("no food", NextAction::array(0, new NextAction("conjure
|
|
||||||
// food", 15.0f), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("no mana gem", NextAction::array(0, new NextAction("conjure mana gem", 20.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("mage armor", NextAction::array(0, new NextAction("mage armor", 19.0f), nullptr)));
|
||||||
new TriggerNode("mage armor", NextAction::array(0, new NextAction("mage armor", 19.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("mage armor", NextAction::array(0, new NextAction("molten armor", 19.0f), nullptr)));
|
||||||
new TriggerNode("mage armor", NextAction::array(0, new NextAction("molten armor", 19.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("arcane intellect on party", NextAction::array(0, new NextAction("arcane intellect on party", 20.0f), nullptr)));
|
||||||
new TriggerNode("arcane intellect on party",
|
|
||||||
NextAction::array(0, new NextAction("arcane intellect on party", 20.0f), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("give water", NextAction::array(0, new NextAction("give water", 14.0f),
|
|
||||||
// nullptr))); triggers.push_back(new TriggerNode("give food", NextAction::array(0, new NextAction("give
|
|
||||||
// food", 13.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GenericMageStrategy.h"
|
#include "GenericMageStrategy.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RangedCombatStrategy.h"
|
#include "RangedCombatStrategy.h"
|
||||||
|
|
||||||
@@ -160,49 +160,119 @@ void GenericMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
RangedCombatStrategy::InitTriggers(triggers);
|
RangedCombatStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
|
// Threat Triggers
|
||||||
// ACTION_MOVE + 9), nullptr)));
|
triggers.push_back(new TriggerNode("high threat", NextAction::array(0, new NextAction("mirror image", 60.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("medium threat", NextAction::array(0, new NextAction("invisibility", 30.0f), nullptr)));
|
||||||
new TriggerNode("enemy is close", NextAction::array(0, new NextAction("frost nova", 50.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
// Defensive Triggers
|
||||||
new TriggerNode("counterspell on enemy healer",
|
triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("ice block", 90.0f), nullptr)));
|
||||||
NextAction::array(0, new NextAction("counterspell on enemy healer", 40.0f), nullptr)));
|
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("mana shield", 85.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("fire ward", NextAction::array(0, new NextAction("fire ward", 90.0f), nullptr)));
|
||||||
new TriggerNode("critical health", NextAction::array(0, new NextAction("ice block", 80.0f), nullptr)));
|
triggers.push_back(new TriggerNode("frost ward", NextAction::array(0, new NextAction("frost ward", 90.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("enemy is close and no firestarter strategy", NextAction::array(0, new NextAction("frost nova", 50.0f), nullptr)));
|
||||||
new TriggerNode("spellsteal", NextAction::array(0, new NextAction("spellsteal", 40.0f), nullptr)));
|
triggers.push_back(new TriggerNode("enemy too close for spell and no firestarter strategy", NextAction::array(0, new NextAction("blink back", 35.0f), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("medium threat", NextAction::array(0, new NextAction("invisibility", 60.0f), nullptr)));
|
// Mana Threshold Triggers
|
||||||
triggers.push_back(
|
Player* bot = botAI->GetBot();
|
||||||
new TriggerNode("low mana", NextAction::array(0, new NextAction("evocation", ACTION_EMERGENCY + 5), nullptr)));
|
if (bot->HasSpell(42985)) // Mana Sapphire
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana sapphire", 90.0f), nullptr)));
|
||||||
new TriggerNode("fire ward", NextAction::array(0, new NextAction("fire ward", ACTION_EMERGENCY), nullptr)));
|
else if (bot->HasSpell(27101)) // Mana Emerald
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana emerald", 90.0f), nullptr)));
|
||||||
new TriggerNode("frost ward", NextAction::array(0, new NextAction("frost ward", ACTION_EMERGENCY), nullptr)));
|
else if (bot->HasSpell(10054)) // Mana Ruby
|
||||||
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana ruby", 90.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("enemy too close for spell",
|
else if (bot->HasSpell(10053)) // Mana Citrine
|
||||||
NextAction::array(0, new NextAction("blink back", ACTION_MOVE + 5), nullptr)));
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana citrine", 90.0f), nullptr)));
|
||||||
|
else if (bot->HasSpell(3552)) // Mana Jade
|
||||||
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana jade", 90.0f), nullptr)));
|
||||||
|
else if (bot->HasSpell(759)) // Mana Agate
|
||||||
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("use mana agate", 90.0f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana potion", 90.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("evocation", 90.0f), nullptr)));
|
||||||
|
|
||||||
|
// Counterspell / Spellsteal Triggers
|
||||||
|
triggers.push_back(new TriggerNode("spellsteal", NextAction::array(0, new NextAction("spellsteal", 40.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("counterspell on enemy healer", NextAction::array(0, new NextAction("counterspell on enemy healer", 40.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("remove curse", NextAction::array(0, new NextAction("remove curse", 41.0f), nullptr)));
|
||||||
new TriggerNode("remove curse", NextAction::array(0, new NextAction("remove curse", 41.0f), nullptr)));
|
triggers.push_back(new TriggerNode("remove curse on party", NextAction::array(0, new NextAction("remove curse on party", 40.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("remove curse on party",
|
|
||||||
NextAction::array(0, new NextAction("remove curse on party", 40.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr)));
|
Player* bot = botAI->GetBot();
|
||||||
triggers.push_back(
|
int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
new TriggerNode("presence of mind", NextAction::array(0, new NextAction("presence of mind", 42.0f), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), nullptr)));
|
if (tab == 0) // Arcane
|
||||||
triggers.push_back(
|
{
|
||||||
new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr)));
|
triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 29.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 28.5f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 28.0f), nullptr)));
|
||||||
|
}
|
||||||
|
else if (tab == 1)
|
||||||
|
{
|
||||||
|
if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/)
|
||||||
|
{ // Frostfire
|
||||||
|
triggers.push_back(new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 18.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 17.5f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 17.0f), nullptr)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Fire
|
||||||
|
triggers.push_back(new TriggerNode("combustion", NextAction::array(0, new NextAction("combustion", 18.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 17.5f), nullptr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tab == 2) // Frost
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode("cold snap", NextAction::array(0, new NextAction("cold snap", 28.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 27.5f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 26.0f), nullptr)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MageCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void MageCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("polymorph", NextAction::array(0, new NextAction("polymorph", 30.0f), nullptr)));
|
triggers.push_back(new TriggerNode("polymorph", NextAction::array(0, new NextAction("polymorph", 30.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode("blizzard channel check", NextAction::array(0, new NextAction("cancel channel", 26.0f), nullptr)));
|
||||||
|
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
|
|
||||||
|
if (tab == 0) // Arcane
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
|
||||||
|
new NextAction("flamestrike", 23.0f),
|
||||||
|
new NextAction("blizzard", 22.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("arcane explosion", 21.0f), nullptr)));
|
||||||
|
}
|
||||||
|
else if (tab == 1) // Fire and Frostfire
|
||||||
|
{
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("medium aoe", NextAction::array(0,
|
||||||
|
new NextAction("dragon's breath", 39.0f),
|
||||||
|
new NextAction("blast wave", 38.0f),
|
||||||
|
new NextAction("flamestrike", 23.0f),
|
||||||
|
new NextAction("blizzard", 22.0f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("firestarter", NextAction::array(0, new NextAction("flamestrike", 40.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("living bomb on attackers", NextAction::array(0, new NextAction("living bomb on attackers", 21.0f), nullptr)));
|
||||||
|
}
|
||||||
|
else if (tab == 2) // Frost
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", NextAction::array(0, new NextAction("blizzard", 24.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0,
|
||||||
|
new NextAction("flamestrike", 23.0f),
|
||||||
|
new NextAction("blizzard", 22.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("cone of cold", 21.0f), nullptr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,4 +51,13 @@ public:
|
|||||||
std::string const getName() override { return "cc"; }
|
std::string const getName() override { return "cc"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MageAoeStrategy : public CombatStrategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MageAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
std::string const getName() override { return "aoe"; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include "UseItemAction.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
@@ -13,6 +13,42 @@
|
|||||||
|
|
||||||
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
|
Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", getName()); }
|
||||||
|
|
||||||
|
bool UseManaSapphireAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(33312, false) > 0; // Mana Sapphire
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseManaEmeraldAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(22044, false) > 0; // Mana Emerald
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseManaRubyAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(8008, false) > 0; // Mana Ruby
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseManaCitrineAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(8007, false) > 0; // Mana Citrine
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseManaJadeAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(5513, false) > 0; // Mana Jade
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseManaAgateAction::isUseful()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
return AI_VALUE2(bool, "combat", "self target") && bot->GetItemCount(5514, false) > 0; // Mana Agate
|
||||||
|
}
|
||||||
|
|
||||||
bool CastFrostNovaAction::isUseful()
|
bool CastFrostNovaAction::isUseful()
|
||||||
{
|
{
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
@@ -105,4 +141,14 @@ bool CastBlinkBackAction::Execute(Event event)
|
|||||||
// can cast spell check passed in isUseful()
|
// can cast spell check passed in isUseful()
|
||||||
bot->SetOrientation(bot->GetAngle(target) + M_PI);
|
bot->SetOrientation(bot->GetAngle(target) + M_PI);
|
||||||
return CastSpellAction::Execute(event);
|
return CastSpellAction::Execute(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CancelChannelAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||||
|
{
|
||||||
|
bot->InterruptSpell(CURRENT_CHANNELED_SPELL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,11 +8,280 @@
|
|||||||
|
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
#include "UseItemAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
BUFF_ACTION(CastFireWardAction, "fire ward");
|
// Buff and Out of Combat Actions
|
||||||
BUFF_ACTION(CastFrostWardAction, "frost ward");
|
|
||||||
|
class CastMoltenArmorAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastMageArmorAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastIceArmorAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastIceArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice armor") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastFrostArmorAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastArcaneIntellectAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastArcaneIntellectAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane intellect") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastArcaneIntellectOnPartyAction : public BuffOnPartyAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "arcane intellect") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastFocusMagicOnPartyAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {}
|
||||||
|
Unit* GetTarget() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastSummonWaterElementalAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Boost Actions
|
||||||
|
|
||||||
|
class CastCombustionAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastCombustionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "combustion") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastArcanePowerAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastArcanePowerAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane power") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastPresenceOfMindAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastPresenceOfMindAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "presence of mind") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastIcyVeinsAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastIcyVeinsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "icy veins") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastColdSnapAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastColdSnapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cold snap") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defensive Actions
|
||||||
|
|
||||||
|
class CastFireWardAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFireWardAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "fire ward") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastFrostWardAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFrostWardAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost ward") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastIceBarrierAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastIceBarrierAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice barrier") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastInvisibilityAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastInvisibilityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "invisibility") {}
|
||||||
|
};
|
||||||
|
class CastIceBlockAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastIceBlockAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice block") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastMirrorImageAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastBlinkBackAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastBlinkBackAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blink") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastManaShieldAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastManaShieldAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mana shield") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility Actions
|
||||||
|
|
||||||
|
class CastEvocationAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastEvocationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "evocation") {}
|
||||||
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastConjureManaGemAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastConjureManaGemAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure mana gem") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastConjureFoodAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastConjureFoodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure food") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastConjureWaterAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastConjureWaterAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure water") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseManaSapphireAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaSapphireAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana sapphire") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
class UseManaEmeraldAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaEmeraldAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana emerald") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseManaRubyAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaRubyAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana ruby") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseManaCitrineAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaCitrineAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana citrine") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseManaJadeAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaJadeAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana jade") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseManaAgateAction : public UseItemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseManaAgateAction(PlayerbotAI* botAI) : UseItemAction(botAI, "mana agate") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CC, Interrupt, and Dispel Actions
|
||||||
|
|
||||||
|
class CastPolymorphAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
|
||||||
|
Value<Unit*>* GetTargetValue() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastSpellstealAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastSpellstealAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "spellsteal") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastCounterspellAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastCounterspellAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "counterspell") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastFrostNovaAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFrostNovaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "frost nova") {}
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastDeepFreezeAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastDeepFreezeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "deep freeze") {}
|
||||||
|
bool isPossible() override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRemoveCurseAction : public CastCureSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastRemoveCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove curse") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRemoveLesserCurseAction : public CastCureSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastRemoveLesserCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove lesser curse") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRemoveCurseOnPartyAction : public CurePartyMemberAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastRemoveCurseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "remove curse", DISPEL_CURSE) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI)
|
||||||
|
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Damage and Debuff Actions
|
||||||
|
|
||||||
class CastFireballAction : public CastSpellAction
|
class CastFireballAction : public CastSpellAction
|
||||||
{
|
{
|
||||||
@@ -57,18 +326,26 @@ public:
|
|||||||
CastPyroblastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "pyroblast") {}
|
CastPyroblastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "pyroblast") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastFlamestrikeAction : public CastDebuffSpellAction
|
class CastLivingBombAction : public CastDebuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {}
|
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
|
||||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
bool isUseful() override
|
||||||
|
{
|
||||||
|
// Bypass TTL check
|
||||||
|
return CastAuraSpellAction::isUseful();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastFrostNovaAction : public CastSpellAction
|
class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastFrostNovaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "frost nova") {}
|
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
|
||||||
bool isUseful() override;
|
bool isUseful() override
|
||||||
|
{
|
||||||
|
// Bypass TTL check
|
||||||
|
return CastAuraSpellAction::isUseful();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastFrostboltAction : public CastSpellAction
|
class CastFrostboltAction : public CastSpellAction
|
||||||
@@ -89,12 +366,6 @@ public:
|
|||||||
CastIceLanceAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "ice lance") {}
|
CastIceLanceAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "ice lance") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastDeepFreezeAction : public CastSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastDeepFreezeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "deep freeze") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastBlizzardAction : public CastSpellAction
|
class CastBlizzardAction : public CastSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -110,143 +381,11 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastArcaneIntellectAction : public CastBuffSpellAction
|
class CastFlamestrikeAction : public CastDebuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastArcaneIntellectAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane intellect") {}
|
CastFlamestrikeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "flamestrike", true, 0.0f) {}
|
||||||
};
|
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||||
|
|
||||||
class CastArcaneIntellectOnPartyAction : public BuffOnPartyAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastArcaneIntellectOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "arcane intellect") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastRemoveCurseAction : public CastCureSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastRemoveCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove curse") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastRemoveLesserCurseAction : public CastCureSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastRemoveLesserCurseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "remove lesser curse") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastIcyVeinsAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastIcyVeinsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "icy veins") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastColdSnapAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastColdSnapAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "cold snap") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastIceBarrierAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastIceBarrierAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice barrier") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastSummonWaterElementalAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastCombustionAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastCombustionAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "combustion") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEGIN_SPELL_ACTION(CastCounterspellAction, "counterspell")
|
|
||||||
END_SPELL_ACTION()
|
|
||||||
|
|
||||||
class CastRemoveCurseOnPartyAction : public CurePartyMemberAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastRemoveCurseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "remove curse", DISPEL_CURSE) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI)
|
|
||||||
: CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastConjureFoodAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastConjureFoodAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure food") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastConjureWaterAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastConjureWaterAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "conjure water") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastIceBlockAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastIceBlockAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice block") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastMoltenArmorAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastMageArmorAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastIceArmorAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastIceArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ice armor") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastFrostArmorAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastFrostArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "frost armor") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastPolymorphAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastPolymorphAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "polymorph") {}
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastSpellstealAction : public CastSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastSpellstealAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "spellsteal") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastLivingBombAction : public CastDebuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastDragonsBreathAction : public CastSpellAction
|
class CastDragonsBreathAction : public CastSpellAction
|
||||||
@@ -265,55 +404,12 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastInvisibilityAction : public CastBuffSpellAction
|
class CancelChannelAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastInvisibilityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "invisibility") {}
|
CancelChannelAction(PlayerbotAI* botAI) : Action(botAI, "cancel channel") {}
|
||||||
};
|
|
||||||
|
|
||||||
class CastEvocationAction : public CastSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastEvocationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "evocation") {}
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastArcanePowerAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastArcanePowerAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "arcane power") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastPresenceOfMindAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastPresenceOfMindAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "presence of mind") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastMirrorImageAction : public CastBuffSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastMirrorImageAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mirror image") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastFocusMagicOnPartyAction : public CastSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastFocusMagicOnPartyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "focus magic") {}
|
|
||||||
Unit* GetTarget() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CastBlinkBackAction : public CastSpellAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CastBlinkBackAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blink") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MageAiObjectContext.h"
|
#include "MageAiObjectContext.h"
|
||||||
|
|
||||||
#include "ArcaneMageStrategy.h"
|
#include "ArcaneMageStrategy.h"
|
||||||
#include "FireMageStrategy.h"
|
#include "FireMageStrategy.h"
|
||||||
#include "FrostFireMageStrategy.h"
|
#include "FrostFireMageStrategy.h"
|
||||||
@@ -23,27 +22,23 @@ public:
|
|||||||
{
|
{
|
||||||
creators["nc"] = &MageStrategyFactoryInternal::nc;
|
creators["nc"] = &MageStrategyFactoryInternal::nc;
|
||||||
creators["pull"] = &MageStrategyFactoryInternal::pull;
|
creators["pull"] = &MageStrategyFactoryInternal::pull;
|
||||||
creators["fire aoe"] = &MageStrategyFactoryInternal::fire_aoe;
|
creators["aoe"] = &MageStrategyFactoryInternal::aoe;
|
||||||
creators["frostfire aoe"] = &MageStrategyFactoryInternal::frostfire_aoe;
|
|
||||||
creators["frost aoe"] = &MageStrategyFactoryInternal::frost_aoe;
|
|
||||||
creators["arcane aoe"] = &MageStrategyFactoryInternal::arcane_aoe;
|
|
||||||
creators["cure"] = &MageStrategyFactoryInternal::cure;
|
creators["cure"] = &MageStrategyFactoryInternal::cure;
|
||||||
creators["buff"] = &MageStrategyFactoryInternal::buff;
|
creators["buff"] = &MageStrategyFactoryInternal::buff;
|
||||||
creators["boost"] = &MageStrategyFactoryInternal::boost;
|
creators["boost"] = &MageStrategyFactoryInternal::boost;
|
||||||
creators["cc"] = &MageStrategyFactoryInternal::cc;
|
creators["cc"] = &MageStrategyFactoryInternal::cc;
|
||||||
|
creators["firestarter"] = &MageStrategyFactoryInternal::firestarter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericMageNonCombatStrategy(botAI); }
|
static Strategy* nc(PlayerbotAI* botAI) { return new GenericMageNonCombatStrategy(botAI); }
|
||||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
|
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
|
||||||
static Strategy* fire_aoe(PlayerbotAI* botAI) { return new FireMageAoeStrategy(botAI); }
|
static Strategy* aoe(PlayerbotAI* botAI) { return new MageAoeStrategy(botAI); }
|
||||||
static Strategy* frostfire_aoe(PlayerbotAI* botAI) { return new FrostFireMageAoeStrategy(botAI); }
|
|
||||||
static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostMageAoeStrategy(botAI); }
|
|
||||||
static Strategy* arcane_aoe(PlayerbotAI* botAI) { return new ArcaneMageAoeStrategy(botAI); }
|
|
||||||
static Strategy* cure(PlayerbotAI* botAI) { return new MageCureStrategy(botAI); }
|
static Strategy* cure(PlayerbotAI* botAI) { return new MageCureStrategy(botAI); }
|
||||||
static Strategy* buff(PlayerbotAI* botAI) { return new MageBuffStrategy(botAI); }
|
static Strategy* buff(PlayerbotAI* botAI) { return new MageBuffStrategy(botAI); }
|
||||||
static Strategy* boost(PlayerbotAI* botAI) { return new MageBoostStrategy(botAI); }
|
static Strategy* boost(PlayerbotAI* botAI) { return new MageBoostStrategy(botAI); }
|
||||||
static Strategy* cc(PlayerbotAI* botAI) { return new MageCcStrategy(botAI); }
|
static Strategy* cc(PlayerbotAI* botAI) { return new MageCcStrategy(botAI); }
|
||||||
|
static Strategy* firestarter(PlayerbotAI* botAI) { return new FirestarterStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MageCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
class MageCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||||
@@ -86,8 +81,7 @@ public:
|
|||||||
creators["fireball"] = &MageTriggerFactoryInternal::fireball;
|
creators["fireball"] = &MageTriggerFactoryInternal::fireball;
|
||||||
creators["pyroblast"] = &MageTriggerFactoryInternal::pyroblast;
|
creators["pyroblast"] = &MageTriggerFactoryInternal::pyroblast;
|
||||||
creators["combustion"] = &MageTriggerFactoryInternal::combustion;
|
creators["combustion"] = &MageTriggerFactoryInternal::combustion;
|
||||||
creators["fingers of frost single"] = &MageTriggerFactoryInternal::fingers_of_frost_single;
|
creators["fingers of frost"] = &MageTriggerFactoryInternal::fingers_of_frost;
|
||||||
creators["fingers of frost double"] = &MageTriggerFactoryInternal::fingers_of_frost_double;
|
|
||||||
creators["brain freeze"] = &MageTriggerFactoryInternal::brain_freeze;
|
creators["brain freeze"] = &MageTriggerFactoryInternal::brain_freeze;
|
||||||
creators["icy veins"] = &MageTriggerFactoryInternal::icy_veins;
|
creators["icy veins"] = &MageTriggerFactoryInternal::icy_veins;
|
||||||
creators["cold snap"] = &MageTriggerFactoryInternal::cold_snap;
|
creators["cold snap"] = &MageTriggerFactoryInternal::cold_snap;
|
||||||
@@ -102,6 +96,7 @@ public:
|
|||||||
creators["spellsteal"] = &MageTriggerFactoryInternal::spellsteal;
|
creators["spellsteal"] = &MageTriggerFactoryInternal::spellsteal;
|
||||||
creators["hot streak"] = &MageTriggerFactoryInternal::hot_streak;
|
creators["hot streak"] = &MageTriggerFactoryInternal::hot_streak;
|
||||||
creators["living bomb"] = &MageTriggerFactoryInternal::living_bomb;
|
creators["living bomb"] = &MageTriggerFactoryInternal::living_bomb;
|
||||||
|
creators["living bomb on attackers"] = &MageTriggerFactoryInternal::living_bomb_on_attackers;
|
||||||
creators["missile barrage"] = &MageTriggerFactoryInternal::missile_barrage;
|
creators["missile barrage"] = &MageTriggerFactoryInternal::missile_barrage;
|
||||||
creators["arcane blast"] = &MageTriggerFactoryInternal::arcane_blast;
|
creators["arcane blast"] = &MageTriggerFactoryInternal::arcane_blast;
|
||||||
creators["counterspell on enemy healer"] = &MageTriggerFactoryInternal::counterspell_enemy_healer;
|
creators["counterspell on enemy healer"] = &MageTriggerFactoryInternal::counterspell_enemy_healer;
|
||||||
@@ -115,6 +110,20 @@ public:
|
|||||||
creators["frostbite on target"] = &MageTriggerFactoryInternal::frostbite_on_target;
|
creators["frostbite on target"] = &MageTriggerFactoryInternal::frostbite_on_target;
|
||||||
creators["no focus magic"] = &MageTriggerFactoryInternal::no_focus_magic;
|
creators["no focus magic"] = &MageTriggerFactoryInternal::no_focus_magic;
|
||||||
creators["frostfire bolt"] = &MageTriggerFactoryInternal::frostfire_bolt;
|
creators["frostfire bolt"] = &MageTriggerFactoryInternal::frostfire_bolt;
|
||||||
|
creators["firestarter"] = &MageTriggerFactoryInternal::firestarter;
|
||||||
|
creators["improved scorch"] = &MageTriggerFactoryInternal::improved_scorch;
|
||||||
|
creators["flamestrike nearby"] = &MageTriggerFactoryInternal::flamestrike_nearby;
|
||||||
|
creators["flamestrike active and medium aoe"] = &MageTriggerFactoryInternal::flamestrike_blizzard;
|
||||||
|
creators["arcane blast 4 stacks and missile barrage"] = &MageTriggerFactoryInternal::arcane_blast_4_stacks_and_missile_barrage;
|
||||||
|
creators["icy veins on cd"] = &MageTriggerFactoryInternal::icy_veins_on_cd;
|
||||||
|
creators["deep freeze on cd"] = &MageTriggerFactoryInternal::deep_freeze_on_cd;
|
||||||
|
creators["no mana gem"] = &MageTriggerFactoryInternal::NoManaGem;
|
||||||
|
creators["blizzard channel check"] = &MageTriggerFactoryInternal::blizzard_channel_check;
|
||||||
|
creators["blast wave off cd"] = &MageTriggerFactoryInternal::blast_wave_off_cd;
|
||||||
|
creators["blast wave off cd and medium aoe"] = &MageTriggerFactoryInternal::blast_wave_off_cd_and_medium_aoe;
|
||||||
|
creators["no firestarter strategy"] = &MageTriggerFactoryInternal::no_firestarter_strategy;
|
||||||
|
creators["enemy is close and no firestarter strategy"] = &MageTriggerFactoryInternal::enemy_is_close_and_no_firestarter_strategy;
|
||||||
|
creators["enemy too close for spell and no firestarter strategy"] = &MageTriggerFactoryInternal::enemy_too_close_for_spell_and_no_firestarter_strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -126,8 +135,7 @@ private:
|
|||||||
static Trigger* fireball(PlayerbotAI* botAI) { return new FireballTrigger(botAI); }
|
static Trigger* fireball(PlayerbotAI* botAI) { return new FireballTrigger(botAI); }
|
||||||
static Trigger* pyroblast(PlayerbotAI* botAI) { return new PyroblastTrigger(botAI); }
|
static Trigger* pyroblast(PlayerbotAI* botAI) { return new PyroblastTrigger(botAI); }
|
||||||
static Trigger* combustion(PlayerbotAI* botAI) { return new CombustionTrigger(botAI); }
|
static Trigger* combustion(PlayerbotAI* botAI) { return new CombustionTrigger(botAI); }
|
||||||
static Trigger* fingers_of_frost_single(PlayerbotAI* botAI) { return new FingersOfFrostSingleTrigger(botAI); }
|
static Trigger* fingers_of_frost(PlayerbotAI* botAI) { return new FingersOfFrostTrigger(botAI); }
|
||||||
static Trigger* fingers_of_frost_double(PlayerbotAI* botAI) { return new FingersOfFrostDoubleTrigger(botAI); }
|
|
||||||
static Trigger* brain_freeze(PlayerbotAI* botAI) { return new BrainFreezeTrigger(botAI); }
|
static Trigger* brain_freeze(PlayerbotAI* botAI) { return new BrainFreezeTrigger(botAI); }
|
||||||
static Trigger* icy_veins(PlayerbotAI* botAI) { return new IcyVeinsTrigger(botAI); }
|
static Trigger* icy_veins(PlayerbotAI* botAI) { return new IcyVeinsTrigger(botAI); }
|
||||||
static Trigger* cold_snap(PlayerbotAI* botAI) { return new ColdSnapTrigger(botAI); }
|
static Trigger* cold_snap(PlayerbotAI* botAI) { return new ColdSnapTrigger(botAI); }
|
||||||
@@ -141,6 +149,7 @@ private:
|
|||||||
static Trigger* polymorph(PlayerbotAI* botAI) { return new PolymorphTrigger(botAI); }
|
static Trigger* polymorph(PlayerbotAI* botAI) { return new PolymorphTrigger(botAI); }
|
||||||
static Trigger* spellsteal(PlayerbotAI* botAI) { return new SpellstealTrigger(botAI); }
|
static Trigger* spellsteal(PlayerbotAI* botAI) { return new SpellstealTrigger(botAI); }
|
||||||
static Trigger* living_bomb(PlayerbotAI* botAI) { return new LivingBombTrigger(botAI); }
|
static Trigger* living_bomb(PlayerbotAI* botAI) { return new LivingBombTrigger(botAI); }
|
||||||
|
static Trigger* living_bomb_on_attackers(PlayerbotAI* botAI) { return new LivingBombOnAttackersTrigger(botAI); }
|
||||||
static Trigger* missile_barrage(PlayerbotAI* botAI) { return new MissileBarrageTrigger(botAI); }
|
static Trigger* missile_barrage(PlayerbotAI* botAI) { return new MissileBarrageTrigger(botAI); }
|
||||||
static Trigger* arcane_blast(PlayerbotAI* botAI) { return new ArcaneBlastTrigger(botAI); }
|
static Trigger* arcane_blast(PlayerbotAI* botAI) { return new ArcaneBlastTrigger(botAI); }
|
||||||
static Trigger* counterspell_enemy_healer(PlayerbotAI* botAI) { return new CounterspellEnemyHealerTrigger(botAI); }
|
static Trigger* counterspell_enemy_healer(PlayerbotAI* botAI) { return new CounterspellEnemyHealerTrigger(botAI); }
|
||||||
@@ -150,6 +159,20 @@ private:
|
|||||||
static Trigger* frostbite_on_target(PlayerbotAI* botAI) { return new FrostbiteOnTargetTrigger(botAI); }
|
static Trigger* frostbite_on_target(PlayerbotAI* botAI) { return new FrostbiteOnTargetTrigger(botAI); }
|
||||||
static Trigger* no_focus_magic(PlayerbotAI* botAI) { return new NoFocusMagicTrigger(botAI); }
|
static Trigger* no_focus_magic(PlayerbotAI* botAI) { return new NoFocusMagicTrigger(botAI); }
|
||||||
static Trigger* frostfire_bolt(PlayerbotAI* botAI) { return new FrostfireBoltTrigger(botAI); }
|
static Trigger* frostfire_bolt(PlayerbotAI* botAI) { return new FrostfireBoltTrigger(botAI); }
|
||||||
|
static Trigger* improved_scorch(PlayerbotAI* botAI) { return new ImprovedScorchTrigger(botAI); }
|
||||||
|
static Trigger* firestarter(PlayerbotAI* botAI) { return new FirestarterTrigger(botAI); }
|
||||||
|
static Trigger* flamestrike_nearby(PlayerbotAI* botAI) { return new FlamestrikeNearbyTrigger(botAI); }
|
||||||
|
static Trigger* flamestrike_blizzard(PlayerbotAI* botAI) { return new FlamestrikeBlizzardTrigger(botAI); }
|
||||||
|
static Trigger* arcane_blast_4_stacks_and_missile_barrage(PlayerbotAI* botAI) { return new ArcaneBlast4StacksAndMissileBarrageTrigger(botAI); }
|
||||||
|
static Trigger* icy_veins_on_cd(PlayerbotAI* botAI) { return new IcyVeinsCooldownTrigger(botAI); }
|
||||||
|
static Trigger* deep_freeze_on_cd(PlayerbotAI* botAI) { return new DeepFreezeCooldownTrigger(botAI); }
|
||||||
|
static Trigger* NoManaGem(PlayerbotAI* botAI) { return new NoManaGemTrigger(botAI); }
|
||||||
|
static Trigger* blizzard_channel_check(PlayerbotAI* botAI) { return new BlizzardChannelCheckTrigger(botAI); }
|
||||||
|
static Trigger* blast_wave_off_cd(PlayerbotAI* botAI) { return new BlastWaveOffCdTrigger(botAI); }
|
||||||
|
static Trigger* blast_wave_off_cd_and_medium_aoe(PlayerbotAI* botAI) { return new BlastWaveOffCdTriggerAndMediumAoeTrigger(botAI); }
|
||||||
|
static Trigger* no_firestarter_strategy(PlayerbotAI* botAI) { return new NoFirestarterStrategyTrigger(botAI); }
|
||||||
|
static Trigger* enemy_is_close_and_no_firestarter_strategy(PlayerbotAI* botAI) { return new EnemyIsCloseAndNoFirestarterStrategyTrigger(botAI); }
|
||||||
|
static Trigger* enemy_too_close_for_spell_and_no_firestarter_strategy(PlayerbotAI* botAI) { return new EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MageAiObjectContextInternal : public NamedObjectContext<Action>
|
class MageAiObjectContextInternal : public NamedObjectContext<Action>
|
||||||
@@ -170,6 +193,7 @@ public:
|
|||||||
creators["arcane intellect on party"] = &MageAiObjectContextInternal::arcane_intellect_on_party;
|
creators["arcane intellect on party"] = &MageAiObjectContextInternal::arcane_intellect_on_party;
|
||||||
creators["conjure water"] = &MageAiObjectContextInternal::conjure_water;
|
creators["conjure water"] = &MageAiObjectContextInternal::conjure_water;
|
||||||
creators["conjure food"] = &MageAiObjectContextInternal::conjure_food;
|
creators["conjure food"] = &MageAiObjectContextInternal::conjure_food;
|
||||||
|
creators["conjure mana gem"] = &MageAiObjectContextInternal::conjure_mana_gem;
|
||||||
creators["molten armor"] = &MageAiObjectContextInternal::molten_armor;
|
creators["molten armor"] = &MageAiObjectContextInternal::molten_armor;
|
||||||
creators["mage armor"] = &MageAiObjectContextInternal::mage_armor;
|
creators["mage armor"] = &MageAiObjectContextInternal::mage_armor;
|
||||||
creators["ice armor"] = &MageAiObjectContextInternal::ice_armor;
|
creators["ice armor"] = &MageAiObjectContextInternal::ice_armor;
|
||||||
@@ -207,6 +231,14 @@ public:
|
|||||||
creators["mirror image"] = &MageAiObjectContextInternal::mirror_image;
|
creators["mirror image"] = &MageAiObjectContextInternal::mirror_image;
|
||||||
creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party;
|
creators["focus magic on party"] = &MageAiObjectContextInternal::focus_magic_on_party;
|
||||||
creators["blink back"] = &MageAiObjectContextInternal::blink_back;
|
creators["blink back"] = &MageAiObjectContextInternal::blink_back;
|
||||||
|
creators["use mana sapphire"] = &MageAiObjectContextInternal::use_mana_sapphire;
|
||||||
|
creators["use mana emerald"] = &MageAiObjectContextInternal::use_mana_emerald;
|
||||||
|
creators["use mana ruby"] = &MageAiObjectContextInternal::use_mana_ruby;
|
||||||
|
creators["use mana citrine"] = &MageAiObjectContextInternal::use_mana_citrine;
|
||||||
|
creators["use mana jade"] = &MageAiObjectContextInternal::use_mana_jade;
|
||||||
|
creators["use mana agate"] = &MageAiObjectContextInternal::use_mana_agate;
|
||||||
|
creators["cancel channel"] = &MageAiObjectContextInternal::cancel_channel;
|
||||||
|
creators["mana shield"] = &MageAiObjectContextInternal::mana_shield;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -228,6 +260,7 @@ private:
|
|||||||
static Action* arcane_intellect_on_party(PlayerbotAI* botAI) { return new CastArcaneIntellectOnPartyAction(botAI); }
|
static Action* arcane_intellect_on_party(PlayerbotAI* botAI) { return new CastArcaneIntellectOnPartyAction(botAI); }
|
||||||
static Action* conjure_water(PlayerbotAI* botAI) { return new CastConjureWaterAction(botAI); }
|
static Action* conjure_water(PlayerbotAI* botAI) { return new CastConjureWaterAction(botAI); }
|
||||||
static Action* conjure_food(PlayerbotAI* botAI) { return new CastConjureFoodAction(botAI); }
|
static Action* conjure_food(PlayerbotAI* botAI) { return new CastConjureFoodAction(botAI); }
|
||||||
|
static Action* conjure_mana_gem(PlayerbotAI* botAI) { return new CastConjureManaGemAction(botAI); }
|
||||||
static Action* molten_armor(PlayerbotAI* botAI) { return new CastMoltenArmorAction(botAI); }
|
static Action* molten_armor(PlayerbotAI* botAI) { return new CastMoltenArmorAction(botAI); }
|
||||||
static Action* mage_armor(PlayerbotAI* botAI) { return new CastMageArmorAction(botAI); }
|
static Action* mage_armor(PlayerbotAI* botAI) { return new CastMageArmorAction(botAI); }
|
||||||
static Action* ice_armor(PlayerbotAI* botAI) { return new CastIceArmorAction(botAI); }
|
static Action* ice_armor(PlayerbotAI* botAI) { return new CastIceArmorAction(botAI); }
|
||||||
@@ -241,10 +274,7 @@ private:
|
|||||||
static Action* remove_curse(PlayerbotAI* botAI) { return new CastRemoveCurseAction(botAI); }
|
static Action* remove_curse(PlayerbotAI* botAI) { return new CastRemoveCurseAction(botAI); }
|
||||||
static Action* remove_curse_on_party(PlayerbotAI* botAI) { return new CastRemoveCurseOnPartyAction(botAI); }
|
static Action* remove_curse_on_party(PlayerbotAI* botAI) { return new CastRemoveCurseOnPartyAction(botAI); }
|
||||||
static Action* remove_lesser_curse(PlayerbotAI* botAI) { return new CastRemoveLesserCurseAction(botAI); }
|
static Action* remove_lesser_curse(PlayerbotAI* botAI) { return new CastRemoveLesserCurseAction(botAI); }
|
||||||
static Action* remove_lesser_curse_on_party(PlayerbotAI* botAI)
|
static Action* remove_lesser_curse_on_party(PlayerbotAI* botAI) { return new CastRemoveLesserCurseOnPartyAction(botAI); }
|
||||||
{
|
|
||||||
return new CastRemoveLesserCurseOnPartyAction(botAI);
|
|
||||||
}
|
|
||||||
static Action* icy_veins(PlayerbotAI* botAI) { return new CastIcyVeinsAction(botAI); }
|
static Action* icy_veins(PlayerbotAI* botAI) { return new CastIcyVeinsAction(botAI); }
|
||||||
static Action* cold_snap(PlayerbotAI* botAI) { return new CastColdSnapAction(botAI); }
|
static Action* cold_snap(PlayerbotAI* botAI) { return new CastColdSnapAction(botAI); }
|
||||||
static Action* ice_barrier(PlayerbotAI* botAI) { return new CastIceBarrierAction(botAI); }
|
static Action* ice_barrier(PlayerbotAI* botAI) { return new CastIceBarrierAction(botAI); }
|
||||||
@@ -259,13 +289,18 @@ private:
|
|||||||
static Action* blast_wave(PlayerbotAI* botAI) { return new CastBlastWaveAction(botAI); }
|
static Action* blast_wave(PlayerbotAI* botAI) { return new CastBlastWaveAction(botAI); }
|
||||||
static Action* invisibility(PlayerbotAI* botAI) { return new CastInvisibilityAction(botAI); }
|
static Action* invisibility(PlayerbotAI* botAI) { return new CastInvisibilityAction(botAI); }
|
||||||
static Action* evocation(PlayerbotAI* botAI) { return new CastEvocationAction(botAI); }
|
static Action* evocation(PlayerbotAI* botAI) { return new CastEvocationAction(botAI); }
|
||||||
static Action* counterspell_on_enemy_healer(PlayerbotAI* botAI)
|
static Action* counterspell_on_enemy_healer(PlayerbotAI* botAI) { return new CastCounterspellOnEnemyHealerAction(botAI); }
|
||||||
{
|
|
||||||
return new CastCounterspellOnEnemyHealerAction(botAI);
|
|
||||||
}
|
|
||||||
static Action* mirror_image(PlayerbotAI* botAI) { return new CastMirrorImageAction(botAI); }
|
static Action* mirror_image(PlayerbotAI* botAI) { return new CastMirrorImageAction(botAI); }
|
||||||
static Action* focus_magic_on_party(PlayerbotAI* botAI) { return new CastFocusMagicOnPartyAction(botAI); }
|
static Action* focus_magic_on_party(PlayerbotAI* botAI) { return new CastFocusMagicOnPartyAction(botAI); }
|
||||||
static Action* blink_back(PlayerbotAI* botAI) { return new CastBlinkBackAction(botAI); }
|
static Action* blink_back(PlayerbotAI* botAI) { return new CastBlinkBackAction(botAI); }
|
||||||
|
static Action* use_mana_sapphire(PlayerbotAI* botAI) { return new UseManaSapphireAction(botAI); }
|
||||||
|
static Action* use_mana_emerald(PlayerbotAI* botAI) { return new UseManaEmeraldAction(botAI); }
|
||||||
|
static Action* use_mana_ruby(PlayerbotAI* botAI) { return new UseManaRubyAction(botAI); }
|
||||||
|
static Action* use_mana_citrine(PlayerbotAI* botAI) { return new UseManaCitrineAction(botAI); }
|
||||||
|
static Action* use_mana_jade(PlayerbotAI* botAI) { return new UseManaJadeAction(botAI); }
|
||||||
|
static Action* use_mana_agate(PlayerbotAI* botAI) { return new UseManaAgateAction(botAI); }
|
||||||
|
static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); }
|
||||||
|
static Action* mana_shield(PlayerbotAI* botAI) { return new CastManaShieldAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
SharedNamedObjectContextList<Strategy> MageAiObjectContext::sharedStrategyContexts;
|
SharedNamedObjectContextList<Strategy> MageAiObjectContext::sharedStrategyContexts;
|
||||||
@@ -309,4 +344,4 @@ void MageAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextLis
|
|||||||
void MageAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
void MageAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
||||||
{
|
{
|
||||||
AiObjectContext::BuildSharedValueContexts(valueContexts);
|
AiObjectContext::BuildSharedValueContexts(valueContexts);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,33 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MageTriggers.h"
|
#include "MageTriggers.h"
|
||||||
|
|
||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "Spell.h"
|
||||||
|
#include "DynamicObject.h"
|
||||||
|
#include "Value.h"
|
||||||
|
#include "SpellAuraEffects.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
|
||||||
|
bool NoManaGemTrigger::IsActive()
|
||||||
|
{
|
||||||
|
static const std::vector<uint32> gemIds = {
|
||||||
|
33312, // Mana Sapphire
|
||||||
|
22044, // Mana Emerald
|
||||||
|
8008, // Mana Ruby
|
||||||
|
8007, // Mana Citrine
|
||||||
|
5513, // Mana Jade
|
||||||
|
5514 // Mana Agate
|
||||||
|
};
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
for (uint32 gemId : gemIds)
|
||||||
|
{
|
||||||
|
if (bot->GetItemCount(gemId, false) > 0) // false = only in bags
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ArcaneIntellectOnPartyTrigger::IsActive()
|
bool ArcaneIntellectOnPartyTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@@ -25,25 +49,6 @@ bool MageArmorTrigger::IsActive()
|
|||||||
!botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target);
|
!botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FingersOfFrostSingleTrigger::IsActive()
|
|
||||||
{
|
|
||||||
// Fingers of Frost "stack" count is always 1.
|
|
||||||
// The value is instead stored in the charges.
|
|
||||||
Aura* aura = botAI->GetAura("fingers of frost", bot, false, true, -1);
|
|
||||||
return (aura && aura->GetCharges() == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArcaneBlastStackTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, 3);
|
|
||||||
if (!aura)
|
|
||||||
return false;
|
|
||||||
if (aura->GetStackAmount() >= 4)
|
|
||||||
return true;
|
|
||||||
bool hasMissileBarrage = botAI->HasAura(44401, bot);
|
|
||||||
return hasMissileBarrage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FrostNovaOnTargetTrigger::IsActive()
|
bool FrostNovaOnTargetTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
@@ -84,3 +89,95 @@ bool NoFocusMagicTrigger::IsActive()
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeepFreezeCooldownTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
static const uint32 DEEP_FREEZE_SPELL_ID = 44572;
|
||||||
|
|
||||||
|
// If the bot does NOT have Deep Freeze, treat as "on cooldown"
|
||||||
|
if (!bot->HasSpell(DEEP_FREEZE_SPELL_ID))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Otherwise, use the default cooldown logic
|
||||||
|
return SpellCooldownTrigger::IsActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<uint32> FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215,
|
||||||
|
10216, 27086, 42925, 42926};
|
||||||
|
|
||||||
|
bool FlamestrikeNearbyTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
for (uint32 spellId : FLAMESTRIKE_SPELL_IDS)
|
||||||
|
{
|
||||||
|
Aura* aura = bot->GetAura(spellId, bot->GetGUID());
|
||||||
|
if (!aura)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DynamicObject* dynObj = aura->GetDynobjOwner();
|
||||||
|
if (!dynObj)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = bot->GetDistance2d(dynObj->GetPositionX(), dynObj->GetPositionY());
|
||||||
|
if (dist <= radius)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImprovedScorchTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target || !target->IsAlive() || !target->IsInWorld())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// List of all spell IDs for Improved Scorch, Winter's Chill, and Shadow Mastery
|
||||||
|
static const uint32 ImprovedScorchExclusiveDebuffs[] = {// Shadow Mastery
|
||||||
|
17794, 17797, 17798, 17799, 17800,
|
||||||
|
// Winter's Chill
|
||||||
|
12579,
|
||||||
|
// Improved Scorch
|
||||||
|
22959};
|
||||||
|
|
||||||
|
for (uint32 spellId : ImprovedScorchExclusiveDebuffs)
|
||||||
|
{
|
||||||
|
if (target->HasAura(spellId))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use default DebuffTrigger logic for the rest (only trigger if debuff is missing or expiring)
|
||||||
|
return DebuffTrigger::IsActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<uint32> BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = {
|
||||||
|
10, // Blizzard Rank 1
|
||||||
|
6141, // Blizzard Rank 2
|
||||||
|
8427, // Blizzard Rank 3
|
||||||
|
10185, // Blizzard Rank 4
|
||||||
|
10186, // Blizzard Rank 5
|
||||||
|
10187, // Blizzard Rank 6
|
||||||
|
27085, // Blizzard Rank 7
|
||||||
|
42938, // Blizzard Rank 8
|
||||||
|
42939 // Blizzard Rank 9
|
||||||
|
};
|
||||||
|
|
||||||
|
bool BlizzardChannelCheckTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
// Check if the bot is channeling a spell
|
||||||
|
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||||
|
{
|
||||||
|
// Only trigger if the spell being channeled is Blizzard
|
||||||
|
if (BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id))
|
||||||
|
{
|
||||||
|
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
|
||||||
|
return attackerCount < minEnemies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not channeling Blizzard
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,11 +9,15 @@
|
|||||||
#include "CureTriggers.h"
|
#include "CureTriggers.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
#include "Trigger.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
DEFLECT_TRIGGER(FireWardTrigger, "fire ward");
|
// Buff and Out of Combat Triggers
|
||||||
DEFLECT_TRIGGER(FrostWardTrigger, "frost ward");
|
|
||||||
|
|
||||||
class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger
|
class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger
|
||||||
{
|
{
|
||||||
@@ -37,30 +41,53 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LivingBombTrigger : public DebuffTrigger
|
class NoFocusMagicTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LivingBombTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "living bomb", 1, true) {}
|
NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {}
|
||||||
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FireballTrigger : public DebuffTrigger
|
class IceBarrierTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FireballTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "fireball", 1, true) {}
|
IceBarrierTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "ice barrier") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PyroblastTrigger : public DebuffTrigger
|
class NoManaGemTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PyroblastTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pyroblast", 1, true) {}
|
NoManaGemTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no mana gem") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FireWardTrigger : public DeflectSpellTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FireWardTrigger(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, "fire ward") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrostWardTrigger : public DeflectSpellTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FrostWardTrigger(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, "frost ward") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Proc and Boost Triggers
|
||||||
|
|
||||||
class HotStreakTrigger : public HasAuraTrigger
|
class HotStreakTrigger : public HasAuraTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HotStreakTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "hot streak") {}
|
HotStreakTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "hot streak") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FirestarterTrigger : public HasAuraTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FirestarterTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "firestarter") {}
|
||||||
|
};
|
||||||
|
|
||||||
class MissileBarrageTrigger : public HasAuraTrigger
|
class MissileBarrageTrigger : public HasAuraTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -73,30 +100,19 @@ public:
|
|||||||
ArcaneBlastTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane blast") {}
|
ArcaneBlastTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane blast") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FingersOfFrostSingleTrigger : public HasAuraStackTrigger
|
class ArcaneBlastStackTrigger : public HasAuraStackTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FingersOfFrostSingleTrigger(PlayerbotAI* ai) : HasAuraStackTrigger(ai, "fingers of frost", 1, 1) {}
|
ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 4, 1) {}
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FingersOfFrostDoubleTrigger : public HasAuraStackTrigger
|
class ArcaneBlast4StacksAndMissileBarrageTrigger : public TwoTriggers
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FingersOfFrostDoubleTrigger(PlayerbotAI* ai) : HasAuraStackTrigger(ai, "fingers of frost", 2, 1) {}
|
ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* ai)
|
||||||
// bool IsActive() override;
|
: TwoTriggers(ai, "arcane blast stack", "missile barrage")
|
||||||
};
|
{
|
||||||
|
}
|
||||||
class BrainFreezeTrigger : public HasAuraTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BrainFreezeTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fireball!") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CounterspellInterruptSpellTrigger : public InterruptSpellTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CounterspellInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "counterspell") {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CombustionTrigger : public BoostTrigger
|
class CombustionTrigger : public BoostTrigger
|
||||||
@@ -105,23 +121,50 @@ public:
|
|||||||
CombustionTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "combustion") {}
|
CombustionTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "combustion") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IcyVeinsCooldownTrigger : public SpellCooldownTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IcyVeinsCooldownTrigger(PlayerbotAI* botAI) : SpellCooldownTrigger(botAI, "icy veins") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeepFreezeCooldownTrigger : public SpellCooldownTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeepFreezeCooldownTrigger(PlayerbotAI* botAI) : SpellCooldownTrigger(botAI, "deep freeze") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ColdSnapTrigger : public TwoTriggers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ColdSnapTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "icy veins on cd", "deep freeze on cd") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MirrorImageTrigger : public BoostTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MirrorImageTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "mirror image") {}
|
||||||
|
};
|
||||||
|
|
||||||
class IcyVeinsTrigger : public BoostTrigger
|
class IcyVeinsTrigger : public BoostTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IcyVeinsTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "icy veins") {}
|
IcyVeinsTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "icy veins") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColdSnapTrigger : public BoostTrigger
|
class ArcanePowerTrigger : public BoostTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ColdSnapTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "cold snap") {}
|
ArcanePowerTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "arcane power") {}
|
||||||
|
};
|
||||||
|
class PresenceOfMindTrigger : public BoostTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PresenceOfMindTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "presence of mind") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class IceBarrierTrigger : public BuffTrigger
|
// CC, Interrupt, and Dispel Triggers
|
||||||
{
|
|
||||||
public:
|
|
||||||
IceBarrierTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "ice barrier") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PolymorphTrigger : public HasCcTargetTrigger
|
class PolymorphTrigger : public HasCcTargetTrigger
|
||||||
{
|
{
|
||||||
@@ -155,29 +198,63 @@ public:
|
|||||||
CounterspellEnemyHealerTrigger(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, "counterspell") {}
|
CounterspellEnemyHealerTrigger(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, "counterspell") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArcanePowerTrigger : public BuffTrigger
|
class CounterspellInterruptSpellTrigger : public InterruptSpellTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArcanePowerTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "arcane power") {}
|
CounterspellInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "counterspell") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PresenceOfMindTrigger : public BuffTrigger
|
// Damage and Debuff Triggers
|
||||||
|
|
||||||
|
class LivingBombTrigger : public DebuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PresenceOfMindTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "presence of mind") {}
|
LivingBombTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "living bomb", 1, true) {}
|
||||||
|
bool IsActive() override { return BuffTrigger::IsActive(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArcaneBlastStackTrigger : public HasAuraStackTrigger
|
class LivingBombOnAttackersTrigger : public DebuffOnAttackerTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArcaneBlastStackTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "arcane blast", 3, 1) {}
|
LivingBombOnAttackersTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "living bomb", true) {}
|
||||||
|
bool IsActive() override { return BuffTrigger::IsActive(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FireballTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FireballTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "fireball", 1, true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImprovedScorchTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImprovedScorchTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "improved scorch", 1, true, 0.5f) {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MirrorImageTrigger : public BoostTrigger
|
class PyroblastTrigger : public DebuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MirrorImageTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "mirror image") {}
|
PyroblastTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pyroblast", 1, true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrostfireBoltTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FrostfireBoltTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frostfire bolt", 1, true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FingersOfFrostTrigger : public HasAuraTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FingersOfFrostTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fingers of frost") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BrainFreezeTrigger : public HasAuraTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BrainFreezeTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "fireball!") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrostNovaOnTargetTrigger : public DebuffTrigger
|
class FrostNovaOnTargetTrigger : public DebuffTrigger
|
||||||
@@ -194,17 +271,74 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoFocusMagicTrigger : public Trigger
|
class FlamestrikeNearbyTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NoFocusMagicTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no focus magic") {}
|
FlamestrikeNearbyTrigger(PlayerbotAI* botAI, float radius = 30.0f)
|
||||||
|
: Trigger(botAI, "flamestrike nearby"), radius(radius)
|
||||||
|
{
|
||||||
|
}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float radius;
|
||||||
|
static const std::set<uint32> FLAMESTRIKE_SPELL_IDS;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrostfireBoltTrigger : public DebuffTrigger
|
class FlamestrikeBlizzardTrigger : public TwoTriggers
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FrostfireBoltTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frostfire bolt", 1, true) {}
|
FlamestrikeBlizzardTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "flamestrike nearby", "medium aoe") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlizzardChannelCheckTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlizzardChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
|
||||||
|
: Trigger(botAI, "blizzard channel check"), minEnemies(minEnemies) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32 minEnemies;
|
||||||
|
static const std::set<uint32> BLIZZARD_SPELL_IDS;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlastWaveOffCdTrigger(PlayerbotAI* botAI) : SpellNoCooldownTrigger(botAI, "blast wave") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlastWaveOffCdTriggerAndMediumAoeTrigger : public TwoTriggers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "blast wave off cd", "medium aoe") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NoFirestarterStrategyTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoFirestarterStrategyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no firestarter strategy") {}
|
||||||
|
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
return !botAI->HasStrategy("firestarter", BOT_STATE_COMBAT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EnemyIsCloseAndNoFirestarterStrategyTrigger : public TwoTriggers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EnemyIsCloseAndNoFirestarterStrategyTrigger(PlayerbotAI* botAI)
|
||||||
|
: TwoTriggers(botAI, "enemy is close", "no firestarter strategy") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger : public TwoTriggers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EnemyTooCloseForSpellAndNoFirestarterStrategyTrigger(PlayerbotAI* botAI)
|
||||||
|
: TwoTriggers(botAI, "enemy too close for spell", "no firestarter strategy") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ public:
|
|||||||
creators["mimiron rocket strike action"] = &RaidUlduarActionContext::mimiron_rocket_strike_action;
|
creators["mimiron rocket strike action"] = &RaidUlduarActionContext::mimiron_rocket_strike_action;
|
||||||
creators["mimiron phase 4 mark dps action"] = &RaidUlduarActionContext::mimiron_phase_4_mark_dps_action;
|
creators["mimiron phase 4 mark dps action"] = &RaidUlduarActionContext::mimiron_phase_4_mark_dps_action;
|
||||||
creators["mimiron cheat action"] = &RaidUlduarActionContext::mimiron_cheat_action;
|
creators["mimiron cheat action"] = &RaidUlduarActionContext::mimiron_cheat_action;
|
||||||
|
creators["vezax cheat action"] = &RaidUlduarActionContext::vezax_cheat_action;
|
||||||
|
creators["vezax shadow crash action"] = &RaidUlduarActionContext::vezax_shadow_crash_action;
|
||||||
|
creators["vezax mark of the faceless action"] = &RaidUlduarActionContext::vezax_mark_of_the_faceless_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -111,6 +114,9 @@ private:
|
|||||||
static Action* mimiron_rocket_strike_action(PlayerbotAI* ai) { return new MimironRocketStrikeAction(ai); }
|
static Action* mimiron_rocket_strike_action(PlayerbotAI* ai) { return new MimironRocketStrikeAction(ai); }
|
||||||
static Action* mimiron_phase_4_mark_dps_action(PlayerbotAI* ai) { return new MimironPhase4MarkDpsAction(ai); }
|
static Action* mimiron_phase_4_mark_dps_action(PlayerbotAI* ai) { return new MimironPhase4MarkDpsAction(ai); }
|
||||||
static Action* mimiron_cheat_action(PlayerbotAI* ai) { return new MimironCheatAction(ai); }
|
static Action* mimiron_cheat_action(PlayerbotAI* ai) { return new MimironCheatAction(ai); }
|
||||||
|
static Action* vezax_cheat_action(PlayerbotAI* ai) { return new VezaxCheatAction(ai); }
|
||||||
|
static Action* vezax_shadow_crash_action(PlayerbotAI* ai) { return new VezaxShadowCrashAction(ai); }
|
||||||
|
static Action* vezax_mark_of_the_faceless_action(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessAction(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ const Position ULDUAR_KOLOGARN_RESTORE_POSITION = Position(1764.3749f, -24.02903
|
|||||||
const Position ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION = Position(1781.2051f, 9.34402f, 449.0f, 0.00087690353f);
|
const Position ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION = Position(1781.2051f, 9.34402f, 449.0f, 0.00087690353f);
|
||||||
const Position ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION = Position(1763.2561f, -24.44305f, 449.0f, 0.00087690353f);
|
const Position ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION = Position(1763.2561f, -24.44305f, 449.0f, 0.00087690353f);
|
||||||
const Position ULDUAR_THORIM_JUMP_START_POINT = Position(2137.137f, -291.19025f, 438.24753f, 1.7059844f);
|
const Position ULDUAR_THORIM_JUMP_START_POINT = Position(2137.137f, -291.19025f, 438.24753f, 1.7059844f);
|
||||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
|
||||||
|
|
||||||
bool FlameLeviathanVehicleAction::Execute(Event event)
|
bool FlameLeviathanVehicleAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@@ -484,12 +483,12 @@ bool RazorscaleAvoidDevouringFlameAction::isUseful()
|
|||||||
float distance = bot->GetDistance2d(unit);
|
float distance = bot->GetDistance2d(unit);
|
||||||
if (distance < safeDistance)
|
if (distance < safeDistance)
|
||||||
{
|
{
|
||||||
return true; // Bot is within the danger distance
|
return true; // Bot is within the danger distance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // No nearby flames or bot is at a safe distance
|
return false; // No nearby flames or bot is at a safe distance
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RazorscaleAvoidSentinelAction::Execute(Event event)
|
bool RazorscaleAvoidSentinelAction::Execute(Event event)
|
||||||
@@ -529,17 +528,17 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event)
|
|||||||
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
|
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
|
||||||
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
||||||
|
|
||||||
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player
|
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player
|
||||||
{
|
{
|
||||||
// Iterate through the first 3 bot tanks to assign the Skull marker
|
// Iterate through the first 3 bot tanks to assign the Skull marker
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
{
|
{
|
||||||
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (group && lowestHealthSentinel)
|
if (group && lowestHealthSentinel)
|
||||||
{
|
{
|
||||||
int8 skullIndex = 7; // Skull
|
int8 skullIndex = 7; // Skull
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
||||||
|
|
||||||
// If there's no skull set yet, or the skull is on a different target, set the sentinel
|
// If there's no skull set yet, or the skull is on a different target, set the sentinel
|
||||||
@@ -548,16 +547,16 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event)
|
|||||||
group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break; // Stop after finding the first valid bot tank
|
break; // Stop after finding the first valid bot tank
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isMainTank && lowestHealthSentinel) // Bot is the main tank
|
else if (isMainTank && lowestHealthSentinel) // Bot is the main tank
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (group)
|
if (group)
|
||||||
{
|
{
|
||||||
int8 skullIndex = 7; // Skull
|
int8 skullIndex = 7; // Skull
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
||||||
|
|
||||||
// If there's no skull set yet, or the skull is on a different target, set the sentinel
|
// If there's no skull set yet, or the skull is on a different target, set the sentinel
|
||||||
@@ -568,8 +567,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return movedAway; // Return true if moved
|
||||||
return movedAway; // Return true if moved
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RazorscaleAvoidSentinelAction::isUseful()
|
bool RazorscaleAvoidSentinelAction::isUseful()
|
||||||
@@ -585,13 +583,13 @@ bool RazorscaleAvoidSentinelAction::isUseful()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the main tank is a human, check if this bot is one of the first three valid bot tanks
|
// If the main tank is a human, check if this bot is one of the first three valid bot tanks
|
||||||
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
{
|
{
|
||||||
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
||||||
{
|
{
|
||||||
return true; // This bot should assist with marking
|
return true; // This bot should assist with marking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -654,7 +652,8 @@ bool RazorscaleAvoidWhirlwindAction::isUseful()
|
|||||||
Unit* unit = botAI->GetUnit(npc);
|
Unit* unit = botAI->GetUnit(npc);
|
||||||
if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL)
|
if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL)
|
||||||
{
|
{
|
||||||
if (unit->HasAura(RazorscaleBossHelper::SPELL_SENTINEL_WHIRLWIND) || unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
if (unit->HasAura(RazorscaleBossHelper::SPELL_SENTINEL_WHIRLWIND) ||
|
||||||
|
unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||||
{
|
{
|
||||||
if (bot->GetDistance2d(unit) < radius)
|
if (bot->GetDistance2d(unit) < radius)
|
||||||
{
|
{
|
||||||
@@ -679,11 +678,11 @@ bool RazorscaleIgnoreBossAction::isUseful()
|
|||||||
if (boss->GetPositionZ() >= RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD)
|
if (boss->GetPositionZ() >= RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD)
|
||||||
{
|
{
|
||||||
// Check if the bot is outside the designated area
|
// Check if the bot is outside the designated area
|
||||||
if (bot->GetDistance2d(
|
if (bot->GetDistance2d(RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y) >
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y) > RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS + 25.0f)
|
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS + 25.0f)
|
||||||
{
|
{
|
||||||
return true; // Movement to the center is the top priority for all bots
|
return true; // Movement to the center is the top priority for all bots
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!botAI->IsTank(bot))
|
if (!botAI->IsTank(bot))
|
||||||
@@ -698,11 +697,11 @@ bool RazorscaleIgnoreBossAction::isUseful()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the boss is already set as the moon marker
|
// Check if the boss is already set as the moon marker
|
||||||
int8 moonIndex = 4; // Moon marker index
|
int8 moonIndex = 4; // Moon marker index
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
||||||
if (currentMoonTarget == boss->GetGUID())
|
if (currentMoonTarget == boss->GetGUID())
|
||||||
{
|
{
|
||||||
return false; // Moon marker is already correctly set, no further action needed
|
return false; // Moon marker is already correctly set, no further action needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proceed to tank-specific logic
|
// Proceed to tank-specific logic
|
||||||
@@ -716,13 +715,13 @@ bool RazorscaleIgnoreBossAction::isUseful()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the main tank is a human, check if this bot is the lowest-indexed bot tank
|
// If the main tank is a human, check if this bot is the lowest-indexed bot tank
|
||||||
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes
|
for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes
|
||||||
{
|
{
|
||||||
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank
|
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank
|
||||||
{
|
{
|
||||||
return true; // This bot should assign the marker
|
return true; // This bot should assign the marker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,18 +750,13 @@ bool RazorscaleIgnoreBossAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the bot is outside the designated area and move inside first
|
// Check if the bot is outside the designated area and move inside first
|
||||||
if (bot->GetDistance2d(
|
if (bot->GetDistance2d(RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y) >
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y) > RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS + 25.0f)
|
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS + 25.0f)
|
||||||
{
|
{
|
||||||
return MoveInside(
|
return MoveInside(ULDUAR_MAP_ID, RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
ULDUAR_MAP_ID,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y, bot->GetPositionZ(),
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 10.0f, MovementPriority::MOVEMENT_NORMAL);
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y,
|
|
||||||
bot->GetPositionZ(),
|
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 10.0f,
|
|
||||||
MovementPriority::MOVEMENT_NORMAL
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!botAI->IsTank(bot))
|
if (!botAI->IsTank(bot))
|
||||||
@@ -775,7 +769,7 @@ bool RazorscaleIgnoreBossAction::Execute(Event event)
|
|||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
||||||
if (currentMoonTarget == boss->GetGUID())
|
if (currentMoonTarget == boss->GetGUID())
|
||||||
{
|
{
|
||||||
return false; // Moon marker is already correctly set
|
return false; // Moon marker is already correctly set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the main tank and determine role
|
// Get the main tank and determine role
|
||||||
@@ -783,33 +777,28 @@ bool RazorscaleIgnoreBossAction::Execute(Event event)
|
|||||||
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
||||||
|
|
||||||
// If the main tank is a human, assign the moon marker using the lowest-indexed bot tank
|
// If the main tank is a human, assign the moon marker using the lowest-indexed bot tank
|
||||||
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player
|
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a real player
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes
|
for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes
|
||||||
{
|
{
|
||||||
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
SetNextMovementDelay(1000);
|
SetNextMovementDelay(1000);
|
||||||
break; // Assign the moon marker and stop
|
break; // Assign the moon marker and stop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mainTankUnit == bot) // If this bot is the main tank
|
else if (mainTankUnit == bot) // If this bot is the main tank
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
SetNextMovementDelay(1000);
|
SetNextMovementDelay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tanks move inside the arena
|
// Tanks move inside the arena
|
||||||
return MoveInside(
|
return MoveInside(ULDUAR_MAP_ID, RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
ULDUAR_MAP_ID,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y, bot->GetPositionZ(),
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 10.0f, MovementPriority::MOVEMENT_NORMAL);
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y,
|
|
||||||
bot->GetPositionZ(),
|
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 10.0f,
|
|
||||||
MovementPriority::MOVEMENT_NORMAL
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RazorscaleGroundedAction::isUseful()
|
bool RazorscaleGroundedAction::isUseful()
|
||||||
@@ -875,9 +864,8 @@ bool RazorscaleGroundedAction::isUseful()
|
|||||||
float bossY = boss->GetPositionY();
|
float bossY = boss->GetPositionY();
|
||||||
float bossZ = boss->GetPositionZ();
|
float bossZ = boss->GetPositionZ();
|
||||||
|
|
||||||
bool atInitialLandingPosition = (fabs(bossX - landingX) < 2.0f) &&
|
bool atInitialLandingPosition =
|
||||||
(fabs(bossY - landingY) < 2.0f) &&
|
(fabs(bossX - landingX) < 2.0f) && (fabs(bossY - landingY) < 2.0f) && (fabs(bossZ - landingZ) < 1.0f);
|
||||||
(fabs(bossZ - landingZ) < 1.0f);
|
|
||||||
|
|
||||||
constexpr float initialLandingRadius = 14.0f;
|
constexpr float initialLandingRadius = 14.0f;
|
||||||
constexpr float normalRadius = 12.0f;
|
constexpr float normalRadius = 12.0f;
|
||||||
@@ -891,7 +879,8 @@ bool RazorscaleGroundedAction::isUseful()
|
|||||||
return distanceToAdjustedCenter > initialLandingRadius;
|
return distanceToAdjustedCenter > initialLandingRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
float distanceToCenter = bot->GetDistance2d(RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X, RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y);
|
float distanceToCenter = bot->GetDistance2d(RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y);
|
||||||
return distanceToCenter > normalRadius;
|
return distanceToCenter > normalRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -911,12 +900,12 @@ bool RazorscaleGroundedAction::Execute(Event event)
|
|||||||
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
|
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
|
||||||
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr;
|
||||||
|
|
||||||
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
|
||||||
{
|
{
|
||||||
// Iterate through the first 3 bot tanks to handle the moon marker
|
// Iterate through the first 3 bot tanks to handle the moon marker
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
{
|
{
|
||||||
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
|
||||||
{
|
{
|
||||||
int8 moonIndex = 4;
|
int8 moonIndex = 4;
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
||||||
@@ -931,7 +920,7 @@ bool RazorscaleGroundedAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (botAI->IsMainTank(bot)) // Bot is the main tank
|
else if (botAI->IsMainTank(bot)) // Bot is the main tank
|
||||||
{
|
{
|
||||||
int8 moonIndex = 4;
|
int8 moonIndex = 4;
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
||||||
@@ -961,33 +950,22 @@ bool RazorscaleGroundedAction::Execute(Event event)
|
|||||||
float bossY = boss->GetPositionY();
|
float bossY = boss->GetPositionY();
|
||||||
float bossZ = boss->GetPositionZ();
|
float bossZ = boss->GetPositionZ();
|
||||||
|
|
||||||
bool atInitialLandingPosition = (fabs(bossX - landingX) < 2.0f) &&
|
bool atInitialLandingPosition =
|
||||||
(fabs(bossY - landingY) < 2.0f) &&
|
(fabs(bossX - landingX) < 2.0f) && (fabs(bossY - landingY) < 2.0f) && (fabs(bossZ - landingZ) < 1.0f);
|
||||||
(fabs(bossZ - landingZ) < 1.0f);
|
|
||||||
|
|
||||||
if (atInitialLandingPosition)
|
if (atInitialLandingPosition)
|
||||||
{
|
{
|
||||||
// If at the initial landing position, use 12-yard radius with a
|
// If at the initial landing position, use 12-yard radius with a
|
||||||
// 20 yard offset on the Y axis so everyone is behind the boss
|
// 20 yard offset on the Y axis so everyone is behind the boss
|
||||||
return MoveInside(
|
return MoveInside(ULDUAR_MAP_ID, RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
ULDUAR_MAP_ID,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y - 20.0f, bot->GetPositionZ(),
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 12.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y - 20.0f,
|
|
||||||
bot->GetPositionZ(),
|
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 12.0f,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, move inside a 12-yard radius around the arena center
|
// Otherwise, move inside a 12-yard radius around the arena center
|
||||||
return MoveInside(
|
return MoveInside(ULDUAR_MAP_ID, RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
||||||
ULDUAR_MAP_ID,
|
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y, bot->GetPositionZ(), 12.0f,
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_X,
|
MovementPriority::MOVEMENT_COMBAT);
|
||||||
RazorscaleBossHelper::RAZORSCALE_ARENA_CENTER_Y,
|
|
||||||
bot->GetPositionZ(),
|
|
||||||
12.0f,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1064,9 +1042,7 @@ bool RazorscaleHarpoonAction::Execute(Event event)
|
|||||||
float botDist = bot->GetDistance(closestHarpoon);
|
float botDist = bot->GetDistance(closestHarpoon);
|
||||||
if (botDist > INTERACTION_DISTANCE - 1.0f)
|
if (botDist > INTERACTION_DISTANCE - 1.0f)
|
||||||
{
|
{
|
||||||
return MoveTo(bot->GetMapId(),
|
return MoveTo(bot->GetMapId(), closestHarpoon->GetPositionX(), closestHarpoon->GetPositionY(),
|
||||||
closestHarpoon->GetPositionX(),
|
|
||||||
closestHarpoon->GetPositionY(),
|
|
||||||
closestHarpoon->GetPositionZ());
|
closestHarpoon->GetPositionZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1453,7 +1429,12 @@ bool KologarnEyebeamAction::Execute(Event event)
|
|||||||
bool KologarnEyebeamAction::isUseful()
|
bool KologarnEyebeamAction::isUseful()
|
||||||
{
|
{
|
||||||
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
|
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
|
||||||
return kologarnEyebeamTrigger.IsActive();
|
if (!kologarnEyebeamTrigger.IsActive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->HasCheat(BotCheatMask::raid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KologarnRtiTargetAction::isUseful()
|
bool KologarnRtiTargetAction::isUseful()
|
||||||
@@ -1477,7 +1458,12 @@ bool KologarnRtiTargetAction::Execute(Event event)
|
|||||||
bool KologarnCrunchArmorAction::isUseful()
|
bool KologarnCrunchArmorAction::isUseful()
|
||||||
{
|
{
|
||||||
KologarnCrunchArmorTrigger kologarnCrunchArmorTrigger(botAI);
|
KologarnCrunchArmorTrigger kologarnCrunchArmorTrigger(botAI);
|
||||||
return kologarnCrunchArmorTrigger.IsActive();
|
if (!kologarnCrunchArmorTrigger.IsActive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->HasCheat(BotCheatMask::raid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KologarnCrunchArmorAction::Execute(Event event)
|
bool KologarnCrunchArmorAction::Execute(Event event)
|
||||||
@@ -1576,6 +1562,11 @@ bool HodirBitingColdJumpAction::Execute(Event event)
|
|||||||
// return true;
|
// return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HodirBitingColdJumpAction::isUseful()
|
||||||
|
{
|
||||||
|
return botAI->HasCheat(BotCheatMask::raid);
|
||||||
|
}
|
||||||
|
|
||||||
bool FreyaMoveAwayNatureBombAction::isUseful()
|
bool FreyaMoveAwayNatureBombAction::isUseful()
|
||||||
{
|
{
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
@@ -1798,7 +1789,12 @@ bool FreyaMoveToHealingSporeAction::Execute(Event event)
|
|||||||
bool ThorimUnbalancingStrikeAction::isUseful()
|
bool ThorimUnbalancingStrikeAction::isUseful()
|
||||||
{
|
{
|
||||||
ThorimUnbalancingStrikeTrigger thorimUnbalancingStrikeTrigger(botAI);
|
ThorimUnbalancingStrikeTrigger thorimUnbalancingStrikeTrigger(botAI);
|
||||||
return thorimUnbalancingStrikeTrigger.IsActive();
|
if (!thorimUnbalancingStrikeTrigger.IsActive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->HasCheat(BotCheatMask::raid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ThorimUnbalancingStrikeAction::Execute(Event event)
|
bool ThorimUnbalancingStrikeAction::Execute(Event event)
|
||||||
@@ -2245,7 +2241,7 @@ bool MimironShockBlastAction::Execute(Event event)
|
|||||||
float dy = bot->GetPositionY() + sin(angle) * distance;
|
float dy = bot->GetPositionY() + sin(angle) * distance;
|
||||||
float dz = bot->GetPositionZ();
|
float dz = bot->GetPositionZ();
|
||||||
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
bot->GetPositionZ(), dx, dy, dz))
|
bot->GetPositionZ(), dx, dy, dz))
|
||||||
{
|
{
|
||||||
bot->TeleportTo(target->GetMapId(), dx, dy, dz, target->GetOrientation());
|
bot->TeleportTo(target->GetMapId(), dx, dy, dz, target->GetOrientation());
|
||||||
return true;
|
return true;
|
||||||
@@ -2448,7 +2444,7 @@ bool MimironAerialCommandUnitAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
group->SetTargetIcon(crossIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(crossIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assaultBot)
|
if (assaultBot)
|
||||||
{
|
{
|
||||||
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||||
@@ -2647,3 +2643,67 @@ bool MimironCheatAction::Execute(Event event)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VezaxCheatAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// Restore bot's mana to full
|
||||||
|
uint32 maxMana = bot->GetMaxPower(POWER_MANA);
|
||||||
|
if (maxMana > 0)
|
||||||
|
{
|
||||||
|
bot->SetPower(POWER_MANA, maxMana);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VezaxShadowCrashAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// Find General Vezax boss
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
|
||||||
|
if (!boss || !boss->IsAlive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get bot's current position relative to boss
|
||||||
|
float bossX = boss->GetPositionX();
|
||||||
|
float bossY = boss->GetPositionY();
|
||||||
|
float bossZ = boss->GetPositionZ();
|
||||||
|
|
||||||
|
float botX = bot->GetPositionX();
|
||||||
|
float botY = bot->GetPositionY();
|
||||||
|
|
||||||
|
// Calculate current angle and distance from boss
|
||||||
|
float currentAngle = atan2(botY - bossY, botX - bossX);
|
||||||
|
float currentDistance = bot->GetDistance2d(boss);
|
||||||
|
|
||||||
|
// Set desired distance from boss (stay close enough for melee, far enough for ranged)
|
||||||
|
float desiredDistance = 15.0f;
|
||||||
|
|
||||||
|
// If too close or too far, adjust distance first
|
||||||
|
if (currentDistance < desiredDistance - 2.0f || currentDistance > desiredDistance + 2.0f)
|
||||||
|
{
|
||||||
|
currentDistance = desiredDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate movement increment - move in increments around the boss
|
||||||
|
float angleIncrement = M_PI / 10;
|
||||||
|
float newAngle = currentAngle + angleIncrement;
|
||||||
|
|
||||||
|
// Calculate new position
|
||||||
|
float newX = bossX + currentDistance * cos(newAngle);
|
||||||
|
float newY = bossY + currentDistance * sin(newAngle);
|
||||||
|
float newZ = bossZ; // Keep same Z level as boss
|
||||||
|
|
||||||
|
// Move to the new position
|
||||||
|
return MoveTo(boss->GetMapId(), newX, newY, newZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VezaxMarkOfTheFacelessAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
return MoveTo(bot->GetMapId(), ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(),
|
||||||
|
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY(),
|
||||||
|
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionZ(), false, false, false, true,
|
||||||
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
|
}
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class HodirBitingColdJumpAction : public MovementAction
|
|||||||
public:
|
public:
|
||||||
HodirBitingColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "hodir biting cold jump") {}
|
HodirBitingColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "hodir biting cold jump") {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FreyaMoveAwayNatureBombAction : public MovementAction
|
class FreyaMoveAwayNatureBombAction : public MovementAction
|
||||||
@@ -353,5 +354,28 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VezaxCheatAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxCheatAction(PlayerbotAI* ai) : Action(ai, "vezax cheat action") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VezaxShadowCrashAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxShadowCrashAction(PlayerbotAI* ai) : MovementAction(ai, "vezax shadow crash action") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VezaxMarkOfTheFacelessAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxMarkOfTheFacelessAction(PlayerbotAI* ai) : MovementAction(ai, "vezax mark of the faceless action") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -225,6 +225,21 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"mimiron cheat trigger",
|
"mimiron cheat trigger",
|
||||||
NextAction::array(0, new NextAction("mimiron cheat action", ACTION_RAID), nullptr)));
|
NextAction::array(0, new NextAction("mimiron cheat action", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
//
|
||||||
|
// General Vezax
|
||||||
|
//
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"vezax cheat trigger",
|
||||||
|
NextAction::array(0, new NextAction("vezax cheat action", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"vezax shadow crash trigger",
|
||||||
|
NextAction::array(0, new NextAction("vezax shadow crash action", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"vezax mark of the faceless trigger",
|
||||||
|
NextAction::array(0, new NextAction("vezax mark of the faceless action", ACTION_RAID), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public:
|
|||||||
creators["mimiron rocket strike trigger"] = &RaidUlduarTriggerContext::mimiron_rocket_strike_trigger;
|
creators["mimiron rocket strike trigger"] = &RaidUlduarTriggerContext::mimiron_rocket_strike_trigger;
|
||||||
creators["mimiron phase 4 mark dps trigger"] = &RaidUlduarTriggerContext::mimiron_phase_4_mark_dps_trigger;
|
creators["mimiron phase 4 mark dps trigger"] = &RaidUlduarTriggerContext::mimiron_phase_4_mark_dps_trigger;
|
||||||
creators["mimiron cheat trigger"] = &RaidUlduarTriggerContext::mimiron_cheat_trigger;
|
creators["mimiron cheat trigger"] = &RaidUlduarTriggerContext::mimiron_cheat_trigger;
|
||||||
|
creators["vezax cheat trigger"] = &RaidUlduarTriggerContext::vezax_cheat_trigger;
|
||||||
|
creators["vezax shadow crash trigger"] = &RaidUlduarTriggerContext::vezax_shadow_crash_trigger;
|
||||||
|
creators["vezax mark of the faceless trigger"] = &RaidUlduarTriggerContext::vezax_mark_of_the_faceless_trigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -115,6 +118,9 @@ private:
|
|||||||
static Trigger* mimiron_rocket_strike_trigger(PlayerbotAI* ai) { return new MimironRocketStrikeTrigger(ai); }
|
static Trigger* mimiron_rocket_strike_trigger(PlayerbotAI* ai) { return new MimironRocketStrikeTrigger(ai); }
|
||||||
static Trigger* mimiron_phase_4_mark_dps_trigger(PlayerbotAI* ai) { return new MimironPhase4MarkDpsTrigger(ai); }
|
static Trigger* mimiron_phase_4_mark_dps_trigger(PlayerbotAI* ai) { return new MimironPhase4MarkDpsTrigger(ai); }
|
||||||
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
|
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
|
||||||
|
static Trigger* vezax_cheat_trigger(PlayerbotAI* ai) { return new VezaxCheatTrigger(ai); }
|
||||||
|
static Trigger* vezax_shadow_crash_trigger(PlayerbotAI* ai) { return new VezaxShadowCrashTrigger(ai); }
|
||||||
|
static Trigger* vezax_mark_of_the_faceless_trigger(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1533,6 +1533,11 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
|
|||||||
|
|
||||||
bool MimironCheatTrigger::IsActive()
|
bool MimironCheatTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!botAI->IsMainTank(bot))
|
if (!botAI->IsMainTank(bot))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -1557,3 +1562,60 @@ bool MimironCheatTrigger::IsActive()
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VezaxCheatTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
|
||||||
|
|
||||||
|
// Check boss and it is alive
|
||||||
|
if (!boss || !boss->IsAlive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AI_VALUE2(bool, "has mana", "self target"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VezaxShadowCrashTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
|
||||||
|
|
||||||
|
// Check boss and it is alive
|
||||||
|
if (!boss || !boss->IsAlive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->HasAura(SPELL_SHADOW_CRASH, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
|
||||||
|
|
||||||
|
// Check boss and it is alive
|
||||||
|
if (!boss || !boss->IsAlive())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(),
|
||||||
|
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY());
|
||||||
|
|
||||||
|
return distance > 2.0f;
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ enum UlduarIDs
|
|||||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||||
|
|
||||||
|
//General Vezax
|
||||||
|
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||||
|
SPELL_SHADOW_CRASH = 63277,
|
||||||
|
|
||||||
// Buffs
|
// Buffs
|
||||||
SPELL_FROST_TRAP = 13809
|
SPELL_FROST_TRAP = 13809
|
||||||
@@ -106,6 +110,7 @@ const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f
|
|||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||||
|
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||||
@@ -117,6 +122,7 @@ const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569
|
|||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||||
|
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Flame Levi
|
// Flame Levi
|
||||||
@@ -418,4 +424,28 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// General Vezax
|
||||||
|
//
|
||||||
|
class VezaxCheatTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxCheatTrigger(PlayerbotAI* ai) : Trigger(ai, "vezax cheat trigger") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VezaxShadowCrashTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxShadowCrashTrigger(PlayerbotAI* ai) : Trigger(ai, "vezax shadow crash trigger") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VezaxMarkOfTheFacelessTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VezaxMarkOfTheFacelessTrigger(PlayerbotAI* ai) : Trigger(ai, "vezax mark of the faceless trigger") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user