mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-02-21 10:30:01 +01:00
Merge branch 'master' into bwl-strats
This commit is contained in:
commit
57e7db6ad1
4
.github/workflows/macos_build.yml
vendored
4
.github/workflows/macos_build.yml
vendored
@ -1,9 +1,9 @@
|
|||||||
name: macos-build
|
name: macos-build
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [never-match-this-branch]
|
branches: [ "master" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [never-match-this-branch]
|
branches: [ "master" ]
|
||||||
|
|
||||||
# concurrency:
|
# concurrency:
|
||||||
# group: ${{ github.head_ref }} || concat(${{ github.ref }}, ${{ github.workflow }})
|
# group: ${{ github.head_ref }} || concat(${{ github.ref }}, ${{ github.workflow }})
|
||||||
|
|||||||
@ -80,6 +80,7 @@ AiPlayerbot.Enabled = 1
|
|||||||
AiPlayerbot.RandomBotAutologin = 1
|
AiPlayerbot.RandomBotAutologin = 1
|
||||||
|
|
||||||
# Random bot account
|
# Random bot account
|
||||||
|
# Please ensure that RandomBotAccountCount is greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||||
AiPlayerbot.RandomBotAccountCount = 200
|
AiPlayerbot.RandomBotAccountCount = 200
|
||||||
|
|
||||||
# Random bot count
|
# Random bot count
|
||||||
@ -108,12 +109,16 @@ AiPlayerbot.DeleteRandomBotAccounts = 0
|
|||||||
AiPlayerbot.MaxAddedBots = 40
|
AiPlayerbot.MaxAddedBots = 40
|
||||||
|
|
||||||
# Maximum number of bots per class added by one account
|
# Maximum number of bots per class added by one account
|
||||||
AiPlayerbot.MaxAddedBotsPerClass = 10
|
AiPlayerbot.MaxAddedBotsPerClass = 40
|
||||||
|
|
||||||
# Enable/Disable create bot by addclass command (0 = GM only, 1 = enable)
|
# Enable/Disable create bot by addclass command (0 = GM only, 1 = enable)
|
||||||
# default: 1 (enable)
|
# default: 1 (enable)
|
||||||
AiPlayerbot.AddClassCommand = 1
|
AiPlayerbot.AddClassCommand = 1
|
||||||
|
|
||||||
|
# Set the addclass command account pool size
|
||||||
|
# Addclass command uses a subset of accounts from RandomBotAccountCount
|
||||||
|
AiPlayerbot.AddClassAccountPoolSize = 50
|
||||||
|
|
||||||
# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept)
|
# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept)
|
||||||
# default: 1 (accept based on level)
|
# default: 1 (accept based on level)
|
||||||
AiPlayerbot.GroupInvitationPermission = 1
|
AiPlayerbot.GroupInvitationPermission = 1
|
||||||
@ -211,6 +216,9 @@ AiPlayerbot.AutoEquipUpgradeLoot = 1
|
|||||||
# Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current)
|
# Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current)
|
||||||
AiPlayerbot.EquipUpgradeThreshold = 1.1
|
AiPlayerbot.EquipUpgradeThreshold = 1.1
|
||||||
|
|
||||||
|
# Two rounds of equipment initialization to create more suitable gear
|
||||||
|
AiPlayerbot.TwoRoundsGearInit = 0
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@ -337,6 +345,7 @@ AiPlayerbot.MediumHealth = 65
|
|||||||
AiPlayerbot.AlmostFullHealth = 85
|
AiPlayerbot.AlmostFullHealth = 85
|
||||||
AiPlayerbot.LowMana = 15
|
AiPlayerbot.LowMana = 15
|
||||||
AiPlayerbot.MediumMana = 40
|
AiPlayerbot.MediumMana = 40
|
||||||
|
AiPlayerbot.HighMana = 65
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@ -348,7 +357,7 @@ AiPlayerbot.MediumMana = 40
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# Bots pick their quest reward (yes = picks first useful item, no = list all rewards, ask = pick useful item and lists if multiple)
|
# Bots pick their quest reward (yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple)
|
||||||
AiPlayerbot.AutoPickReward = yes
|
AiPlayerbot.AutoPickReward = yes
|
||||||
|
|
||||||
# Sync quests with player (Bots will complete quests the moment you hand them in. Bots will ignore looting quest items.)
|
# Sync quests with player (Bots will complete quests the moment you hand them in. Bots will ignore looting quest items.)
|
||||||
@ -376,9 +385,11 @@ AiPlayerbot.ApplyInstanceStrategies = 1
|
|||||||
# Default: 1 (enable)
|
# Default: 1 (enable)
|
||||||
AiPlayerbot.AutoAvoidAoe = 1
|
AiPlayerbot.AutoAvoidAoe = 1
|
||||||
|
|
||||||
# Tell which spell is avoiding (experimental)
|
# Only avoid aoe spells with a radius smaller than this value
|
||||||
# Default: 1 (enable)
|
AiPlayerbot.MaxAoeAvoidRadius = 15.0
|
||||||
AiPlayerbot.TellWhenAvoidAoe = 1
|
|
||||||
|
# A whitelist of aoe spell IDs that should not be avoided
|
||||||
|
AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810
|
||||||
|
|
||||||
# Enable healer bot save mana
|
# Enable healer bot save mana
|
||||||
# Default: 1 (enable)
|
# Default: 1 (enable)
|
||||||
@ -765,12 +776,12 @@ AiPlayerbot.RandomBotInWorldWithRotationDisabled = 31104000
|
|||||||
|
|
||||||
AiPlayerbot.PremadeSpecName.1.0 = arms pve
|
AiPlayerbot.PremadeSpecName.1.0 = arms pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421
|
AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421
|
||||||
AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100202012013031241
|
AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100002012211231241
|
||||||
AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032123335100202012013031251-32505010002
|
AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032023335100102012213231251-305-2033
|
||||||
AiPlayerbot.PremadeSpecName.1.1 = fury pve
|
AiPlayerbot.PremadeSpecName.1.1 = fury pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432
|
AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432
|
||||||
AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351
|
AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351
|
||||||
AiPlayerbot.PremadeSpecLink.1.1.80 = 30202300233-305053000500310153120511351
|
AiPlayerbot.PremadeSpecLink.1.1.80 = 32002300233-305053000500310153120511351
|
||||||
AiPlayerbot.PremadeSpecName.1.2 = prot pve
|
AiPlayerbot.PremadeSpecName.1.2 = prot pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
|
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
|
||||||
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
|
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
|
||||||
@ -815,13 +826,25 @@ AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732
|
|||||||
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351
|
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351
|
||||||
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021
|
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021
|
||||||
AiPlayerbot.PremadeSpecName.3.1 = mm pve
|
AiPlayerbot.PremadeSpecName.3.1 = mm pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732
|
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
|
||||||
AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051
|
AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051
|
||||||
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002
|
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-025335101030013233135031351-5000002
|
||||||
AiPlayerbot.PremadeSpecName.3.2 = surv pve
|
AiPlayerbot.PremadeSpecName.3.2 = surv pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
|
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
|
||||||
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331
|
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311
|
||||||
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331
|
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321
|
||||||
|
|
||||||
|
# HUNTER PET
|
||||||
|
#
|
||||||
|
# Ferocity
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122
|
||||||
|
# Tenacity
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002
|
||||||
|
# Cunning
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.2.16 = 2100020330000211001
|
||||||
|
AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@ -889,8 +912,8 @@ AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351
|
|||||||
AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003
|
AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003
|
||||||
AiPlayerbot.PremadeSpecName.6.2 = unholy pve
|
AiPlayerbot.PremadeSpecName.6.2 = unholy pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549
|
AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549
|
||||||
AiPlayerbot.PremadeSpecLink.6.2.60 = --2300303050032152000150213130051
|
AiPlayerbot.PremadeSpecLink.6.2.60 = --2301303050032151000150013131151
|
||||||
AiPlayerbot.PremadeSpecLink.6.2.80 = -320053500002-2300303050032152000150213130051
|
AiPlayerbot.PremadeSpecLink.6.2.80 = -320033500002-2301303050032151000150013133151
|
||||||
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
|
AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
|
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
|
||||||
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
|
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
|
||||||
@ -911,7 +934,7 @@ AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776
|
|||||||
AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351
|
AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351
|
||||||
AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031
|
AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031
|
||||||
AiPlayerbot.PremadeSpecName.7.1 = enh pve
|
AiPlayerbot.PremadeSpecName.7.1 = enh pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,41540
|
AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,44923,45771
|
||||||
AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051
|
AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051
|
||||||
AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051
|
AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051
|
||||||
AiPlayerbot.PremadeSpecName.7.2 = resto pve
|
AiPlayerbot.PremadeSpecName.7.2 = resto pve
|
||||||
@ -959,7 +982,7 @@ AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55
|
|||||||
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501
|
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501
|
||||||
AiPlayerbot.PremadeSpecName.9.1 = emo pve
|
AiPlayerbot.PremadeSpecName.9.1 = emo pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459
|
AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459
|
||||||
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55
|
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051
|
||||||
AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55
|
AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55
|
||||||
AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005
|
AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005
|
||||||
AiPlayerbot.PremadeSpecName.9.2 = destro pve
|
AiPlayerbot.PremadeSpecName.9.2 = destro pve
|
||||||
@ -991,7 +1014,7 @@ AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051
|
|||||||
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051
|
AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051
|
||||||
AiPlayerbot.PremadeSpecName.11.3 = cat pve
|
AiPlayerbot.PremadeSpecName.11.3 = cat pve
|
||||||
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
|
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
|
||||||
AiPlayerbot.PremadeSpecLink.11.3.60 = -553202032322010052100030310501
|
AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501
|
||||||
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
|
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1159,11 +1182,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
AiPlayerbot.RandomClassSpecProb.9.0 = 40
|
AiPlayerbot.RandomClassSpecProb.9.0 = 45
|
||||||
AiPlayerbot.RandomClassSpecIndex.9.0 = 0
|
AiPlayerbot.RandomClassSpecIndex.9.0 = 0
|
||||||
AiPlayerbot.RandomClassSpecProb.9.1 = 40
|
AiPlayerbot.RandomClassSpecProb.9.1 = 45
|
||||||
AiPlayerbot.RandomClassSpecIndex.9.1 = 1
|
AiPlayerbot.RandomClassSpecIndex.9.1 = 1
|
||||||
AiPlayerbot.RandomClassSpecProb.9.2 = 20
|
AiPlayerbot.RandomClassSpecProb.9.2 = 10
|
||||||
AiPlayerbot.RandomClassSpecIndex.9.2 = 2
|
AiPlayerbot.RandomClassSpecIndex.9.2 = 2
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1178,11 +1201,12 @@ AiPlayerbot.RandomClassSpecIndex.9.2 = 2
|
|||||||
|
|
||||||
AiPlayerbot.RandomClassSpecProb.11.0 = 20
|
AiPlayerbot.RandomClassSpecProb.11.0 = 20
|
||||||
AiPlayerbot.RandomClassSpecIndex.11.0 = 0
|
AiPlayerbot.RandomClassSpecIndex.11.0 = 0
|
||||||
AiPlayerbot.RandomClassSpecProb.11.1 = 40
|
AiPlayerbot.RandomClassSpecProb.11.1 = 25
|
||||||
AiPlayerbot.RandomClassSpecIndex.11.1 = 1
|
AiPlayerbot.RandomClassSpecIndex.11.1 = 1
|
||||||
AiPlayerbot.RandomClassSpecProb.11.2 = 40
|
AiPlayerbot.RandomClassSpecProb.11.2 = 35
|
||||||
AiPlayerbot.RandomClassSpecIndex.11.2 = 2
|
AiPlayerbot.RandomClassSpecIndex.11.2 = 2
|
||||||
|
AiPlayerbot.RandomClassSpecProb.11.3 = 20
|
||||||
|
AiPlayerbot.RandomClassSpecIndex.11.3 = 3
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@ -1259,6 +1283,10 @@ AiPlayerbot.LogInGroupOnly = 1
|
|||||||
AiPlayerbot.LogValuesPerTick = 0
|
AiPlayerbot.LogValuesPerTick = 0
|
||||||
AiPlayerbot.RandomChangeMultiplier = 1
|
AiPlayerbot.RandomChangeMultiplier = 1
|
||||||
|
|
||||||
|
# Tell which spell is avoiding (experimental)
|
||||||
|
# Default: 0 (disable)
|
||||||
|
AiPlayerbot.TellWhenAvoidAoe = 0
|
||||||
|
|
||||||
# Enables/Disables performance monitor
|
# Enables/Disables performance monitor
|
||||||
AiPlayerbot.PerfMonEnabled = 0
|
AiPlayerbot.PerfMonEnabled = 0
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,6 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot)
|
|||||||
max = tabs[i];
|
max = tabs[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -481,11 +480,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
|
if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
|
||||||
engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
|
engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
|
||||||
|
|
||||||
if (player->getClass() == CLASS_DRUID && tab == 1)
|
// if (player->getClass() == CLASS_DRUID && tab == 1)
|
||||||
engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
|
// engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
|
||||||
|
|
||||||
if (player->getClass() == CLASS_ROGUE)
|
// if (player->getClass() == CLASS_ROGUE)
|
||||||
engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
|
// engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -593,34 +593,41 @@ void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<uint32> ChatHelper::ExtractAllQuestIds(const std::string& text)
|
std::set<uint32> extractGeneric(std::string_view text, std::string_view prefix)
|
||||||
{
|
{
|
||||||
std::set<uint32> ids;
|
std::set<uint32_t> ids;
|
||||||
|
std::string_view text_view = text;
|
||||||
|
|
||||||
std::regex rgx("Hquest:[0-9]+");
|
size_t pos = 0;
|
||||||
auto begin = std::sregex_iterator(text.begin(), text.end(), rgx);
|
while ((pos = text_view.find(prefix, pos)) != std::string::npos)
|
||||||
auto end = std::sregex_iterator();
|
|
||||||
for (std::sregex_iterator i = begin; i != end; ++i)
|
|
||||||
{
|
{
|
||||||
std::smatch match = *i;
|
// skip "Hquest:/Hitem:"
|
||||||
ids.insert(std::stoi(match.str().erase(0, 7)));
|
pos += prefix.size();
|
||||||
|
|
||||||
|
// extract everything after "Hquest:/Hitem:"
|
||||||
|
size_t end_pos = text_view.find_first_not_of("0123456789", pos);
|
||||||
|
std::string_view number_str = text_view.substr(pos, end_pos - pos);
|
||||||
|
|
||||||
|
uint32 number = 0;
|
||||||
|
|
||||||
|
auto [ptr, ec] = std::from_chars(number_str.data(), number_str.data() + number_str.size(), number);
|
||||||
|
|
||||||
|
if (ec == std::errc())
|
||||||
|
{
|
||||||
|
ids.insert(number);
|
||||||
|
}
|
||||||
|
pos = end_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<uint32> ChatHelper::ExtractAllQuestIds(const std::string& text)
|
||||||
|
{
|
||||||
|
return extractGeneric(text, "Hquest:");
|
||||||
|
}
|
||||||
|
|
||||||
std::set<uint32> ChatHelper::ExtractAllItemIds(const std::string& text)
|
std::set<uint32> ChatHelper::ExtractAllItemIds(const std::string& text)
|
||||||
{
|
{
|
||||||
std::set<uint32> ids;
|
return extractGeneric(text, "Hitem:");
|
||||||
|
|
||||||
std::regex rgx("Hitem:[0-9]+");
|
|
||||||
auto begin = std::sregex_iterator(text.begin(), text.end(), rgx);
|
|
||||||
auto end = std::sregex_iterator();
|
|
||||||
for (std::sregex_iterator i = begin; i != end; ++i)
|
|
||||||
{
|
|
||||||
std::smatch match = *i;
|
|
||||||
ids.insert(std::stoi(match.str().erase(0, 6)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ids;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -310,24 +310,23 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType()));
|
bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CanUpdateAI())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// check activity
|
|
||||||
AllowActivity();
|
AllowActivity();
|
||||||
|
|
||||||
// if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
if (currentSpell && currentSpell->getState() == SPELL_STATE_CASTING && currentSpell->GetCastTime())
|
if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING)
|
||||||
{
|
{
|
||||||
nextAICheckDelay = currentSpell->GetCastTime() + sPlayerbotAIConfig->reactDelay;
|
if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive() &&
|
||||||
SetNextCheckDelay(nextAICheckDelay);
|
currentSpell->GetSpellInfo() && !currentSpell->GetSpellInfo()->IsAllowingDeadTarget())
|
||||||
if (!CanUpdateAI())
|
{
|
||||||
|
bot->InterruptSpell(CURRENT_GENERIC_SPELL);
|
||||||
|
SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!CanUpdateAI())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup())
|
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup())
|
||||||
{
|
{
|
||||||
Player* leader = bot->GetGroup()->GetLeader();
|
Player* leader = bot->GetGroup()->GetLeader();
|
||||||
@ -898,9 +897,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
|||||||
p >> casterGuid.ReadAsPacked();
|
p >> casterGuid.ReadAsPacked();
|
||||||
if (casterGuid != bot->GetGUID())
|
if (casterGuid != bot->GetGUID())
|
||||||
return;
|
return;
|
||||||
|
uint8 count, result;
|
||||||
uint32 spellId;
|
uint32 spellId;
|
||||||
p >> spellId;
|
p >> count >> spellId >> result;
|
||||||
SpellInterrupted(spellId);
|
SpellInterrupted(spellId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1005,26 +1004,25 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
|||||||
if (lang == LANG_ADDON)
|
if (lang == LANG_ADDON)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Disable since ExtractAllItemIds bad performance
|
if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) &&
|
||||||
// if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) &&
|
(GetChatHelper()->ExtractAllItemIds(message).size() > 0 ||
|
||||||
// (GetChatHelper()->ExtractAllItemIds(message).size() > 0 ||
|
GetChatHelper()->ExtractAllQuestIds(message).size() > 0) &&
|
||||||
// GetChatHelper()->ExtractAllQuestIds(message).size() > 0) &&
|
sPlayerbotAIConfig->toxicLinksRepliesChance)
|
||||||
// sPlayerbotAIConfig->toxicLinksRepliesChance)
|
{
|
||||||
// {
|
if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance)
|
||||||
// if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance)
|
{
|
||||||
// {
|
return;
|
||||||
// return;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) &&
|
||||||
// else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) &&
|
sPlayerbotAIConfig->thunderfuryRepliesChance))
|
||||||
// sPlayerbotAIConfig->thunderfuryRepliesChance))
|
{
|
||||||
// {
|
if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance)
|
||||||
// if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance)
|
{
|
||||||
// {
|
return;
|
||||||
// return;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
else
|
||||||
// else
|
|
||||||
{
|
{
|
||||||
if (isFromFreeBot && urand(0, 20))
|
if (isFromFreeBot && urand(0, 20))
|
||||||
return;
|
return;
|
||||||
@ -1135,21 +1133,15 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
|||||||
|
|
||||||
void PlayerbotAI::SpellInterrupted(uint32 spellid)
|
void PlayerbotAI::SpellInterrupted(uint32 spellid)
|
||||||
{
|
{
|
||||||
|
for (uint8 type = CURRENT_MELEE_SPELL; type < CURRENT_CHANNELED_SPELL; type++)
|
||||||
|
{
|
||||||
|
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
|
||||||
|
if (!spell)
|
||||||
|
continue;
|
||||||
|
if (spell->GetSpellInfo()->Id == spellid)
|
||||||
|
bot->InterruptSpell((CurrentSpellTypes)type);
|
||||||
|
}
|
||||||
LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
|
LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
|
||||||
if (!spellid || lastSpell.id != spellid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
time_t now = time(nullptr);
|
|
||||||
if (now <= lastSpell.timer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32 castTimeSpent = 1000 * (now - lastSpell.timer);
|
|
||||||
int32 globalCooldown = CalculateGlobalCooldown(lastSpell.id);
|
|
||||||
if (static_cast<int32>(castTimeSpent) < globalCooldown)
|
|
||||||
SetNextCheckDelay(globalCooldown - castTimeSpent);
|
|
||||||
else
|
|
||||||
SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
|
|
||||||
|
|
||||||
lastSpell.id = 0;
|
lastSpell.id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1507,17 +1499,21 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 409:
|
case 409:
|
||||||
strategyName = "mc";
|
strategyName = "mc";
|
||||||
break;
|
break;
|
||||||
|
case 509:
|
||||||
|
strategyName = "aq20";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (strategyName.empty())
|
||||||
|
return;
|
||||||
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
|
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
|
||||||
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
|
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
|
||||||
if (tellMaster && !strategyName.empty())
|
if (tellMaster && !strategyName.empty())
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "Add " << strategyName << " instance strategy";
|
out << "Add " << strategyName << " instance strategy";
|
||||||
TellMaster(out.str());
|
TellMasterNoFacing(out.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1628,10 +1624,10 @@ void PlayerbotAI::ResetStrategies(bool load)
|
|||||||
// sPlayerbotDbStore->Load(this);
|
// sPlayerbotDbStore->Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsRanged(Player* player)
|
bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
||||||
if (botAi)
|
if (!bySpec && botAi)
|
||||||
return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED);
|
return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED);
|
||||||
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(player);
|
int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
@ -1664,18 +1660,18 @@ bool PlayerbotAI::IsRanged(Player* player)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsMelee(Player* player) { return !IsRanged(player); }
|
bool PlayerbotAI::IsMelee(Player* player, bool bySpec) { return !IsRanged(player, bySpec); }
|
||||||
|
|
||||||
bool PlayerbotAI::IsCaster(Player* player) { return IsRanged(player) && player->getClass() != CLASS_HUNTER; }
|
bool PlayerbotAI::IsCaster(Player* player, bool bySpec) { return IsRanged(player, bySpec) && player->getClass() != CLASS_HUNTER; }
|
||||||
|
|
||||||
bool PlayerbotAI::IsCombo(Player* player)
|
bool PlayerbotAI::IsCombo(Player* player, bool bySpec)
|
||||||
{
|
{
|
||||||
// int tab = AiFactory::GetPlayerSpecTab(player);
|
// int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
return player->getClass() == CLASS_ROGUE ||
|
return player->getClass() == CLASS_ROGUE ||
|
||||||
(player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid
|
(player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsRangedDps(Player* player) { return IsRanged(player) && IsDps(player); }
|
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
|
||||||
|
|
||||||
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
||||||
{
|
{
|
||||||
@ -1898,10 +1894,10 @@ int32 PlayerbotAI::GetMeleeIndex(Player* player)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsTank(Player* player)
|
bool PlayerbotAI::IsTank(Player* player, bool bySpec)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
||||||
if (botAi)
|
if (!bySpec && botAi)
|
||||||
return botAi->ContainsStrategy(STRATEGY_TYPE_TANK);
|
return botAi->ContainsStrategy(STRATEGY_TYPE_TANK);
|
||||||
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(player);
|
int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
@ -1936,10 +1932,10 @@ bool PlayerbotAI::IsTank(Player* player)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsHeal(Player* player)
|
bool PlayerbotAI::IsHeal(Player* player, bool bySpec)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
||||||
if (botAi)
|
if (!bySpec && botAi)
|
||||||
return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL);
|
return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL);
|
||||||
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(player);
|
int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
@ -1973,10 +1969,10 @@ bool PlayerbotAI::IsHeal(Player* player)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsDps(Player* player)
|
bool PlayerbotAI::IsDps(Player* player, bool bySpec)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
|
||||||
if (botAi)
|
if (!bySpec && botAi)
|
||||||
return botAi->ContainsStrategy(STRATEGY_TYPE_DPS);
|
return botAi->ContainsStrategy(STRATEGY_TYPE_DPS);
|
||||||
|
|
||||||
int tab = AiFactory::GetPlayerSpecTab(player);
|
int tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
@ -1998,6 +1994,10 @@ bool PlayerbotAI::IsDps(Player* player)
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (tab == DRUID_TAB_FERAL && !IsTank(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CLASS_SHAMAN:
|
case CLASS_SHAMAN:
|
||||||
if (tab != SHAMAN_TAB_RESTORATION)
|
if (tab != SHAMAN_TAB_RESTORATION)
|
||||||
@ -2792,8 +2792,8 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration();
|
uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration();
|
||||||
bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT;
|
// bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT;
|
||||||
if ((CastingTime || interruptOnMove) && bot->isMoving())
|
if ((CastingTime || spellInfo->IsAutoRepeatRangedSpell()) && bot->isMoving())
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
|
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
|
||||||
{
|
{
|
||||||
@ -3610,6 +3610,7 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo
|
|||||||
|
|
||||||
void PlayerbotAI::WaitForSpellCast(Spell* spell)
|
void PlayerbotAI::WaitForSpellCast(Spell* spell)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||||
uint32 castTime = spell->GetCastTime();
|
uint32 castTime = spell->GetCastTime();
|
||||||
// float castTime = spell->GetCastTime();
|
// float castTime = spell->GetCastTime();
|
||||||
@ -4319,7 +4320,7 @@ void PlayerbotAI::_fillGearScoreData(Player* player, Item* item, std::vector<uin
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint8 type = proto->InventoryType;
|
uint8 type = proto->InventoryType;
|
||||||
uint32 level = mixed ? proto->ItemLevel * (1 + proto->Quality) : proto->ItemLevel;
|
uint32 level = mixed ? proto->ItemLevel * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(proto->Quality)) : proto->ItemLevel;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
@ -5174,6 +5175,32 @@ uint32 PlayerbotAI::GetBuffedCount(Player* player, std::string const spellname)
|
|||||||
return bcount;
|
return bcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 PlayerbotAI::GetNearGroupMemberCount(float dis)
|
||||||
|
{
|
||||||
|
int count = 1; // yourself
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
|
{
|
||||||
|
Player* member = gref->GetSource();
|
||||||
|
if (member == bot) // calculated
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!member || !member->IsInWorld())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->GetMapId() != bot->GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->GetExactDist(bot) > dis)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::CanMove()
|
bool PlayerbotAI::CanMove()
|
||||||
{
|
{
|
||||||
// do not allow if not vehicle driver
|
// do not allow if not vehicle driver
|
||||||
@ -5224,6 +5251,7 @@ bool PlayerbotAI::EqualLowercaseName(std::string s1, std::string s2)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A custom CanEquipItem (remove AutoUnequipOffhand in FindEquipSlot to prevent unequip on `item usage` calculation)
|
||||||
InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading) const
|
InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading) const
|
||||||
{
|
{
|
||||||
dest = 0;
|
dest = 0;
|
||||||
@ -5244,11 +5272,9 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem,
|
|||||||
if (pItem->IsBindedNotWith(bot))
|
if (pItem->IsBindedNotWith(bot))
|
||||||
return EQUIP_ERR_DONT_OWN_THAT_ITEM;
|
return EQUIP_ERR_DONT_OWN_THAT_ITEM;
|
||||||
|
|
||||||
// Yunfan: skip it
|
InventoryResult res = bot->CanTakeMoreSimilarItems(pItem);
|
||||||
// // check count of items (skip for auto move for same player from bank)
|
if (res != EQUIP_ERR_OK)
|
||||||
// InventoryResult res = bot->CanTakeMoreSimilarItems(pItem);
|
return res;
|
||||||
// if (res != EQUIP_ERR_OK)
|
|
||||||
// return res;
|
|
||||||
|
|
||||||
ScalingStatDistributionEntry const* ssd =
|
ScalingStatDistributionEntry const* ssd =
|
||||||
pProto->ScalingStatDistribution
|
pProto->ScalingStatDistribution
|
||||||
@ -5267,7 +5293,7 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem,
|
|||||||
if (!bot->CanUseAttackType(bot->GetAttackBySlot(eslot)))
|
if (!bot->CanUseAttackType(bot->GetAttackBySlot(eslot)))
|
||||||
return EQUIP_ERR_NOT_WHILE_DISARMED;
|
return EQUIP_ERR_NOT_WHILE_DISARMED;
|
||||||
|
|
||||||
InventoryResult res = bot->CanUseItem(pItem, not_loading);
|
res = bot->CanUseItem(pItem, not_loading);
|
||||||
if (res != EQUIP_ERR_OK)
|
if (res != EQUIP_ERR_OK)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
@ -5805,3 +5831,32 @@ void PlayerbotAI::PetFollow()
|
|||||||
charmInfo->SetForcedSpell(0);
|
charmInfo->SetForcedSpell(0);
|
||||||
charmInfo->SetForcedTargetGUID();
|
charmInfo->SetForcedTargetGUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float PlayerbotAI::GetItemScoreMultiplier(ItemQualities quality)
|
||||||
|
{
|
||||||
|
switch (quality)
|
||||||
|
{
|
||||||
|
// each quality increase 1.1x
|
||||||
|
case ITEM_QUALITY_POOR:
|
||||||
|
return 1.0f;
|
||||||
|
break;
|
||||||
|
case ITEM_QUALITY_NORMAL:
|
||||||
|
return 1.1f;
|
||||||
|
break;
|
||||||
|
case ITEM_QUALITY_UNCOMMON:
|
||||||
|
return 1.21f;
|
||||||
|
break;
|
||||||
|
case ITEM_QUALITY_RARE:
|
||||||
|
return 1.331f;
|
||||||
|
break;
|
||||||
|
case ITEM_QUALITY_EPIC:
|
||||||
|
return 1.4641f;
|
||||||
|
break;
|
||||||
|
case ITEM_QUALITY_LEGENDARY:
|
||||||
|
return 1.61051f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
@ -401,14 +401,14 @@ public:
|
|||||||
void ResetStrategies(bool load = false);
|
void ResetStrategies(bool load = false);
|
||||||
void ReInitCurrentEngine();
|
void ReInitCurrentEngine();
|
||||||
void Reset(bool full = false);
|
void Reset(bool full = false);
|
||||||
static bool IsTank(Player* player);
|
static bool IsTank(Player* player, bool bySpec = false);
|
||||||
static bool IsHeal(Player* player);
|
static bool IsHeal(Player* player, bool bySpec = false);
|
||||||
static bool IsDps(Player* player);
|
static bool IsDps(Player* player, bool bySpec = false);
|
||||||
static bool IsRanged(Player* player);
|
static bool IsRanged(Player* player, bool bySpec = false);
|
||||||
static bool IsMelee(Player* player);
|
static bool IsMelee(Player* player, bool bySpec = false);
|
||||||
static bool IsCaster(Player* player);
|
static bool IsCaster(Player* player, bool bySpec = false);
|
||||||
static bool IsCombo(Player* player);
|
static bool IsCombo(Player* player, bool bySpec = false);
|
||||||
static bool IsRangedDps(Player* player);
|
static bool IsRangedDps(Player* player, bool bySpec = false);
|
||||||
static bool IsMainTank(Player* player);
|
static bool IsMainTank(Player* player);
|
||||||
bool IsAssistTank(Player* player);
|
bool IsAssistTank(Player* player);
|
||||||
bool IsAssistTankOfIndex(Player* player, int index);
|
bool IsAssistTankOfIndex(Player* player, int index);
|
||||||
@ -469,6 +469,7 @@ public:
|
|||||||
void ImbueItem(Item* item);
|
void ImbueItem(Item* item);
|
||||||
void EnchantItemT(uint32 spellid, uint8 slot);
|
void EnchantItemT(uint32 spellid, uint8 slot);
|
||||||
uint32 GetBuffedCount(Player* player, std::string const spellname);
|
uint32 GetBuffedCount(Player* player, std::string const spellname);
|
||||||
|
int32 GetNearGroupMemberCount(float dis = sPlayerbotAIConfig->sightDistance);
|
||||||
|
|
||||||
virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
|
virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
|
||||||
virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
|
virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
|
||||||
@ -568,6 +569,7 @@ public:
|
|||||||
std::set<uint32> GetAllCurrentQuestIds();
|
std::set<uint32> GetAllCurrentQuestIds();
|
||||||
std::set<uint32> GetCurrentIncompleteQuestIds();
|
std::set<uint32> GetCurrentIncompleteQuestIds();
|
||||||
void PetFollow();
|
void PetFollow();
|
||||||
|
static float GetItemScoreMultiplier(ItemQualities quality);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RandomItemMgr.h"
|
#include "RandomItemMgr.h"
|
||||||
#include "RandomPlayerbotFactory.h"
|
#include "RandomPlayerbotFactory.h"
|
||||||
|
#include "RandomPlayerbotMgr.h"
|
||||||
#include "Talentspec.h"
|
#include "Talentspec.h"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -28,6 +29,19 @@ void LoadList(std::string const value, T& list)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void LoadSet(std::string const value, T& set)
|
||||||
|
{
|
||||||
|
std::vector<std::string> ids = split(value, ',');
|
||||||
|
for (std::vector<std::string>::iterator i = ids.begin(); i != ids.end(); i++)
|
||||||
|
{
|
||||||
|
uint32 id = atoi((*i).c_str());
|
||||||
|
// if (!id)
|
||||||
|
// continue;
|
||||||
|
set.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void LoadListString(std::string const value, T& list)
|
void LoadListString(std::string const value, T& list)
|
||||||
{
|
{
|
||||||
@ -93,10 +107,14 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
almostFullHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.AlmostFullHealth", 85);
|
almostFullHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.AlmostFullHealth", 85);
|
||||||
lowMana = sConfigMgr->GetOption<int32>("AiPlayerbot.LowMana", 15);
|
lowMana = sConfigMgr->GetOption<int32>("AiPlayerbot.LowMana", 15);
|
||||||
mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40);
|
mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40);
|
||||||
|
highMana = sConfigMgr->GetOption<int32>("AiPlayerbot.HighMana", 65);
|
||||||
autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true);
|
autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true);
|
||||||
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
|
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
|
||||||
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
|
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
|
||||||
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", true);
|
maxAoeAvoidRadius = sConfigMgr->GetOption<float>("AiPlayerbot.MaxAoeAvoidRadius", 15.0f);
|
||||||
|
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810"),
|
||||||
|
aoeAvoidSpellWhitelist);
|
||||||
|
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
|
||||||
|
|
||||||
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
|
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
|
||||||
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
|
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
|
||||||
@ -330,14 +348,32 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (uint32 spec = 0; spec < 3; ++spec)
|
for (uint32 spec = 0; spec < 3; ++spec)
|
||||||
|
{
|
||||||
|
for (uint32 points = 0; points < 21; ++points)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "AiPlayerbot.PremadeHunterPetLink." << spec << "." << points;
|
||||||
|
premadeHunterPetLink[spec][points] = sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false);
|
||||||
|
parsedHunterPetLinkOrder[spec][points] =
|
||||||
|
ParseTempPetTalentsOrder(spec, premadeHunterPetLink[spec][points]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32 spec = 0; spec < MAX_SPECNO; ++spec)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "AiPlayerbot.RandomClassSpecProb." << cls << "." << spec;
|
os << "AiPlayerbot.RandomClassSpecProb." << cls << "." << spec;
|
||||||
randomClassSpecProb[cls][spec] = sConfigMgr->GetOption<uint32>(os.str().c_str(), 33);
|
uint32 def;
|
||||||
|
if (spec <= 1)
|
||||||
|
def = 33;
|
||||||
|
else if (spec == 2)
|
||||||
|
def = 34;
|
||||||
|
else
|
||||||
|
def = 0;
|
||||||
|
randomClassSpecProb[cls][spec] = sConfigMgr->GetOption<uint32>(os.str().c_str(), def, false);
|
||||||
os.str("");
|
os.str("");
|
||||||
os.clear();
|
os.clear();
|
||||||
os << "AiPlayerbot.RandomClassSpecIndex." << cls << "." << spec;
|
os << "AiPlayerbot.RandomClassSpecIndex." << cls << "." << spec;
|
||||||
randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption<uint32>(os.str().c_str(), spec + 1);
|
randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption<uint32>(os.str().c_str(), spec, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,6 +456,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
maxAddedBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBots", 40);
|
maxAddedBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBots", 40);
|
||||||
maxAddedBotsPerClass = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBotsPerClass", 10);
|
maxAddedBotsPerClass = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxAddedBotsPerClass", 10);
|
||||||
addClassCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassCommand", 1);
|
addClassCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassCommand", 1);
|
||||||
|
addClassAccountPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassAccountPoolSize", 50);
|
||||||
maintenanceCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.MaintenanceCommand", 1);
|
maintenanceCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.MaintenanceCommand", 1);
|
||||||
autoGearCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommand", 1);
|
autoGearCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommand", 1);
|
||||||
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
|
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
|
||||||
@ -450,6 +487,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
autoPickReward = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoPickReward", "yes");
|
autoPickReward = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoPickReward", "yes");
|
||||||
autoEquipUpgradeLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoEquipUpgradeLoot", true);
|
autoEquipUpgradeLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoEquipUpgradeLoot", true);
|
||||||
equipUpgradeThreshold = sConfigMgr->GetOption<float>("AiPlayerbot.EquipUpgradeThreshold", 1.1f);
|
equipUpgradeThreshold = sConfigMgr->GetOption<float>("AiPlayerbot.EquipUpgradeThreshold", 1.1f);
|
||||||
|
twoRoundsGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.TwoRoundsGearInit", false);
|
||||||
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
|
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
|
||||||
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
|
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
|
||||||
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
|
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
|
||||||
@ -478,6 +516,9 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (sPlayerbotAIConfig->addClassCommand)
|
||||||
|
sRandomPlayerbotMgr->PrepareAddclassCache();
|
||||||
|
|
||||||
sRandomItemMgr->Init();
|
sRandomItemMgr->Init();
|
||||||
sRandomItemMgr->InitAfterAhBot();
|
sRandomItemMgr->InitAfterAhBot();
|
||||||
sPlayerbotTextMgr->LoadBotTexts();
|
sPlayerbotTextMgr->LoadBotTexts();
|
||||||
@ -747,3 +788,48 @@ std::vector<std::vector<uint32>> PlayerbotAIConfig::ParseTempTalentsOrder(uint32
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<uint32>> PlayerbotAIConfig::ParseTempPetTalentsOrder(uint32 spec, std::string tab_link)
|
||||||
|
{
|
||||||
|
// check bad link
|
||||||
|
// uint32 classMask = 1 << (cls - 1);
|
||||||
|
std::vector<TalentEntry const*> spells;
|
||||||
|
std::vector<std::vector<uint32>> orders;
|
||||||
|
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
||||||
|
{
|
||||||
|
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
||||||
|
if (!talentInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
|
||||||
|
if (!talentTabInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!((1 << spec) & talentTabInfo->petTalentMask))
|
||||||
|
continue;
|
||||||
|
// skip some duplicate spells like dash/dive
|
||||||
|
if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 ||
|
||||||
|
talentInfo->TalentID == 2203)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
spells.push_back(talentInfo);
|
||||||
|
}
|
||||||
|
std::sort(spells.begin(), spells.end(),
|
||||||
|
[&](TalentEntry const* lhs, TalentEntry const* rhs)
|
||||||
|
{ return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; });
|
||||||
|
for (int i = 0; i < tab_link.size(); i++)
|
||||||
|
{
|
||||||
|
if (i >= spells.size())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int lvl = tab_link[i] - '0';
|
||||||
|
if (lvl == 0)
|
||||||
|
continue;
|
||||||
|
orders.push_back({spells[i]->Row, spells[i]->Col, (uint32)lvl});
|
||||||
|
}
|
||||||
|
// sort by talent tab size
|
||||||
|
std::sort(orders.begin(), orders.end(), [&](auto& lhs, auto& rhs) { return lhs.size() > rhs.size(); });
|
||||||
|
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|||||||
@ -63,10 +63,12 @@ public:
|
|||||||
tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance,
|
tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance,
|
||||||
targetPosRecalcDistance, farDistance, healDistance, aggroDistance;
|
targetPosRecalcDistance, farDistance, healDistance, aggroDistance;
|
||||||
uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth;
|
uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth;
|
||||||
uint32 lowMana, mediumMana;
|
uint32 lowMana, mediumMana, highMana;
|
||||||
bool autoSaveMana;
|
bool autoSaveMana;
|
||||||
uint32 saveManaThreshold;
|
uint32 saveManaThreshold;
|
||||||
bool autoAvoidAoe;
|
bool autoAvoidAoe;
|
||||||
|
float maxAoeAvoidRadius;
|
||||||
|
std::set<uint32> aoeAvoidSpellWhitelist;
|
||||||
bool tellWhenAvoidAoe;
|
bool tellWhenAvoidAoe;
|
||||||
|
|
||||||
uint32 openGoSpell;
|
uint32 openGoSpell;
|
||||||
@ -195,7 +197,9 @@ public:
|
|||||||
std::string premadeSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
std::string premadeSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
||||||
std::vector<uint32> parsedSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
std::vector<uint32> parsedSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
||||||
std::string premadeSpecLink[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
std::string premadeSpecLink[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
||||||
|
std::string premadeHunterPetLink[3][21];
|
||||||
std::vector<std::vector<uint32>> parsedSpecLinkOrder[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
std::vector<std::vector<uint32>> parsedSpecLinkOrder[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
||||||
|
std::vector<std::vector<uint32>> parsedHunterPetLinkOrder[3][21];
|
||||||
uint32 randomClassSpecProb[MAX_CLASSES][MAX_SPECNO];
|
uint32 randomClassSpecProb[MAX_CLASSES][MAX_SPECNO];
|
||||||
uint32 randomClassSpecIndex[MAX_CLASSES][MAX_SPECNO];
|
uint32 randomClassSpecIndex[MAX_CLASSES][MAX_SPECNO];
|
||||||
|
|
||||||
@ -269,6 +273,7 @@ public:
|
|||||||
std::string autoPickReward;
|
std::string autoPickReward;
|
||||||
bool autoEquipUpgradeLoot;
|
bool autoEquipUpgradeLoot;
|
||||||
float equipUpgradeThreshold;
|
float equipUpgradeThreshold;
|
||||||
|
bool twoRoundsGearInit;
|
||||||
bool syncQuestWithPlayer;
|
bool syncQuestWithPlayer;
|
||||||
bool syncQuestForPlayer;
|
bool syncQuestForPlayer;
|
||||||
std::string autoTrainSpells;
|
std::string autoTrainSpells;
|
||||||
@ -306,6 +311,7 @@ public:
|
|||||||
float autoInitEquipLevelLimitRatio;
|
float autoInitEquipLevelLimitRatio;
|
||||||
int32 maxAddedBots, maxAddedBotsPerClass;
|
int32 maxAddedBots, maxAddedBotsPerClass;
|
||||||
int32 addClassCommand;
|
int32 addClassCommand;
|
||||||
|
int32 addClassAccountPoolSize;
|
||||||
int32 maintenanceCommand;
|
int32 maintenanceCommand;
|
||||||
int32 autoGearCommand, autoGearQualityLimit, autoGearScoreLimit;
|
int32 autoGearCommand, autoGearQualityLimit, autoGearScoreLimit;
|
||||||
|
|
||||||
@ -324,6 +330,7 @@ public:
|
|||||||
|
|
||||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
|
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
|
||||||
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
|
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
|
||||||
|
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()
|
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()
|
||||||
|
|||||||
@ -18,12 +18,14 @@
|
|||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "GroupMgr.h"
|
#include "GroupMgr.h"
|
||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
|
#include "ObjectGuid.h"
|
||||||
#include "ObjectMgr.h"
|
#include "ObjectMgr.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "PlayerbotDbStore.h"
|
#include "PlayerbotDbStore.h"
|
||||||
#include "PlayerbotFactory.h"
|
#include "PlayerbotFactory.h"
|
||||||
#include "PlayerbotSecurity.h"
|
#include "PlayerbotSecurity.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RandomPlayerbotMgr.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "WorldSession.h"
|
#include "WorldSession.h"
|
||||||
#include "ChannelMgr.h"
|
#include "ChannelMgr.h"
|
||||||
@ -126,6 +128,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
|||||||
allowed = false;
|
allowed = false;
|
||||||
out << "Failure: You are not allowed to control bot " << bot->GetName().c_str();
|
out << "Failure: You are not allowed to control bot " << bot->GetName().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowed && masterSession)
|
if (allowed && masterSession)
|
||||||
{
|
{
|
||||||
Player* player = masterSession->GetPlayer();
|
Player* player = masterSession->GetPlayer();
|
||||||
@ -143,6 +146,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
|||||||
out << "Failure: You have added too many bots for this class";
|
out << "Failure: You have added too many bots for this class";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowed)
|
if (allowed)
|
||||||
{
|
{
|
||||||
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
||||||
@ -722,14 +726,14 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
|||||||
sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||||
factory.Randomize(false);
|
factory.Randomize(false);
|
||||||
return "ok, gear score limit: " + std::to_string(mixedGearScore / (ITEM_QUALITY_EPIC + 1)) +
|
return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) +
|
||||||
"(for epic)";
|
"(for epic)";
|
||||||
}
|
}
|
||||||
else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1)
|
else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1)
|
||||||
{
|
{
|
||||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs);
|
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs);
|
||||||
factory.Randomize(false);
|
factory.Randomize(false);
|
||||||
return "ok, gear score limit: " + std::to_string(gs / (ITEM_QUALITY_EPIC + 1)) + "(for epic)";
|
return "ok, gear score limit: " + std::to_string(gs / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,6 +847,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
|
|
||||||
if (!strncmp(cmd, "initself=", 9))
|
if (!strncmp(cmd, "initself=", 9))
|
||||||
{
|
{
|
||||||
|
if (!strcmp(cmd, "initself=uncommon"))
|
||||||
|
{
|
||||||
|
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||||
|
{
|
||||||
|
// OnBotLogin(master);
|
||||||
|
PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_UNCOMMON);
|
||||||
|
factory.Randomize(false);
|
||||||
|
messages.push_back("initself ok");
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
messages.push_back("ERROR: Only GM can use this command.");
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!strcmp(cmd, "initself=rare"))
|
if (!strcmp(cmd, "initself=rare"))
|
||||||
{
|
{
|
||||||
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||||
@ -875,6 +895,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!strcmp(cmd, "initself=legendary"))
|
||||||
|
{
|
||||||
|
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||||
|
{
|
||||||
|
// OnBotLogin(master);
|
||||||
|
PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY);
|
||||||
|
factory.Randomize(false);
|
||||||
|
messages.push_back("initself ok");
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
messages.push_back("ERROR: Only GM can use this command.");
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
int32 gs;
|
int32 gs;
|
||||||
if (sscanf(cmd, "initself=%d", &gs) != -1)
|
if (sscanf(cmd, "initself=%d", &gs) != -1)
|
||||||
{
|
{
|
||||||
@ -1003,44 +1039,20 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
messages.push_back("Error: Invalid Class. Try again.");
|
messages.push_back("Error: Invalid Class. Try again.");
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
uint8 master_race = master->getRace();
|
uint8 teamId = master->GetTeamId(true);
|
||||||
std::string race_limit;
|
std::vector<ObjectGuid> &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)];
|
||||||
switch (master_race)
|
for (size_t i = 0; i < guidCache.size(); i++)
|
||||||
{
|
{
|
||||||
case 1:
|
ObjectGuid guid = guidCache[i];
|
||||||
case 3:
|
if (botLoading.find(guid) != botLoading.end())
|
||||||
case 4:
|
continue;
|
||||||
case 7:
|
if (ObjectAccessor::FindConnectedPlayer(guid))
|
||||||
case 11:
|
continue;
|
||||||
race_limit = "1, 3, 4, 7, 11";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
case 8:
|
|
||||||
case 10:
|
|
||||||
race_limit = "2, 5, 6, 8, 10";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back();
|
|
||||||
// find a bot fit conditions and not in any guild
|
|
||||||
QueryResult results = CharacterDatabase.Query(
|
|
||||||
"SELECT guid FROM characters "
|
|
||||||
"WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) AND "
|
|
||||||
"guid NOT IN ( SELECT guid FROM guild_member ) "
|
|
||||||
"AND account <= {} "
|
|
||||||
"ORDER BY account DESC LIMIT 1",
|
|
||||||
claz, race_limit, maxAccountId);
|
|
||||||
if (results)
|
|
||||||
{
|
|
||||||
Field* fields = results->Fetch();
|
|
||||||
ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get<uint32>());
|
|
||||||
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
||||||
|
|
||||||
messages.push_back("Add class " + std::string(charname));
|
messages.push_back("Add class " + std::string(charname));
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
messages.push_back("Add class failed.");
|
messages.push_back("Add class failed, no available characters!");
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1295,7 +1307,7 @@ uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls)
|
|||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
Player* const bot = it->second;
|
Player* const bot = it->second;
|
||||||
if (bot->getClass() == cls)
|
if (bot && bot->IsInWorld() && bot->getClass() == cls)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -769,49 +769,61 @@ bool RandomItemMgr::CanEquipWeapon(uint8 clazz, ItemTemplate const* proto)
|
|||||||
{
|
{
|
||||||
case CLASS_PRIEST:
|
case CLASS_PRIEST:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_MAGE:
|
case CLASS_MAGE:
|
||||||
case CLASS_WARLOCK:
|
case CLASS_WARLOCK:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_WARRIOR:
|
case CLASS_WARRIOR:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_PALADIN:
|
case CLASS_PALADIN:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
case CLASS_DEATH_KNIGHT:
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD)
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_SHAMAN:
|
case CLASS_SHAMAN:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_DRUID:
|
case CLASS_DRUID:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_HUNTER:
|
case CLASS_HUNTER:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_ROGUE:
|
case CLASS_ROGUE:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -972,7 +984,8 @@ void RandomItemMgr::BuildItemInfoCache()
|
|||||||
strstr(proto->Name1.c_str(), " TEST") || strstr(proto->Name1.c_str(), "2200 ") ||
|
strstr(proto->Name1.c_str(), " TEST") || strstr(proto->Name1.c_str(), "2200 ") ||
|
||||||
strstr(proto->Name1.c_str(), "Deprecated ") || strstr(proto->Name1.c_str(), "Unused ") ||
|
strstr(proto->Name1.c_str(), "Deprecated ") || strstr(proto->Name1.c_str(), "Unused ") ||
|
||||||
strstr(proto->Name1.c_str(), "Monster ") || strstr(proto->Name1.c_str(), "[PH]") ||
|
strstr(proto->Name1.c_str(), "Monster ") || strstr(proto->Name1.c_str(), "[PH]") ||
|
||||||
strstr(proto->Name1.c_str(), "(OLD)") || strstr(proto->Name1.c_str(), "QR"))
|
strstr(proto->Name1.c_str(), "(OLD)") || strstr(proto->Name1.c_str(), "QR") ||
|
||||||
|
strstr(proto->Name1.c_str(), "zzOLD"))
|
||||||
{
|
{
|
||||||
itemForTest.insert(proto->ItemId);
|
itemForTest.insert(proto->ItemId);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -370,7 +370,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginBots)
|
if (loginBots && botLoading.empty())
|
||||||
{
|
{
|
||||||
loginBots += updateBots;
|
loginBots += updateBots;
|
||||||
loginBots = std::min(loginBots, maxNewBots);
|
loginBots = std::min(loginBots, maxNewBots);
|
||||||
@ -1041,6 +1041,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
|||||||
|
|
||||||
SetEventValue(bot, "login", 0, 0);
|
SetEventValue(bot, "login", 0, 0);
|
||||||
|
|
||||||
|
if (!player->IsInWorld())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (player->GetGroup() || player->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
if (player->GetGroup() || player->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1473,40 +1476,44 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
} while (results->NextRow());
|
} while (results->NextRow());
|
||||||
}
|
}
|
||||||
LOG_INFO("playerbots", "{} banker locations for level collected.", collected_locs);
|
LOG_INFO("playerbots", "{} banker locations for level collected.", collected_locs);
|
||||||
|
}
|
||||||
|
|
||||||
// temporary only use locsPerLevelCache, so disable rpgLocsCacheLevel cache
|
void RandomPlayerbotMgr::PrepareAddclassCache()
|
||||||
|
{
|
||||||
// LOG_INFO("playerbots", "Preparing RPG teleport caches for {} factions...", sFactionTemplateStore.GetNumRows());
|
int32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back();
|
||||||
// results = WorldDatabase.Query("SELECT map, position_x, position_y, position_z, r.race, r.minl, r.maxl FROM
|
int32 minIdx =
|
||||||
// creature c INNER JOIN playerbots_rpg_races r ON c.id1 = r.entry "
|
sPlayerbotAIConfig->randomBotAccounts.size() - 1 >= sPlayerbotAIConfig->addClassAccountPoolSize
|
||||||
// "WHERE r.race < 15");
|
? sPlayerbotAIConfig->randomBotAccounts.size() - sPlayerbotAIConfig->addClassAccountPoolSize : 0;
|
||||||
// if (results)
|
int32 minAccountId = sPlayerbotAIConfig->randomBotAccounts[minIdx];
|
||||||
// {
|
if (minAccountId < 0)
|
||||||
// do
|
{
|
||||||
// {
|
LOG_ERROR("playerbots", "No available account for add class!");
|
||||||
// Field* fields = results->Fetch();
|
}
|
||||||
// uint16 mapId = fields[0].Get<uint16>();
|
int32 collected = 0;
|
||||||
// float x = fields[1].Get<float>();
|
for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++)
|
||||||
// float y = fields[2].Get<float>();
|
{
|
||||||
// float z = fields[3].Get<float>();
|
if (claz == 10)
|
||||||
// uint32 race = fields[4].Get<uint32>();
|
continue;
|
||||||
// uint32 minl = fields[5].Get<uint32>();
|
QueryResult results = CharacterDatabase.Query(
|
||||||
// uint32 maxl = fields[6].Get<uint32>();
|
"SELECT guid, race FROM characters "
|
||||||
|
"WHERE account >= {} AND account <= {} AND class = '{}' AND online = 0 AND "
|
||||||
// for (uint32 level = 1; level < sPlayerbotAIConfig->randomBotMaxLevel + 1; level++)
|
"guid NOT IN ( SELECT guid FROM guild_member ) "
|
||||||
// {
|
"ORDER BY account DESC",
|
||||||
// if (level > maxl || level < minl)
|
minAccountId, maxAccountId, claz);
|
||||||
// continue;
|
if (results)
|
||||||
|
{
|
||||||
// WorldLocation loc(mapId, x, y, z, 0);
|
do
|
||||||
// for (uint32 r = 1; r < MAX_RACES; r++)
|
{
|
||||||
// {
|
Field* fields = results->Fetch();
|
||||||
// if (race == r || race == 0)
|
ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get<uint32>());
|
||||||
// rpgLocsCacheLevel[r][level].push_back(loc);
|
uint32 race = fields[1].Get<uint32>();
|
||||||
// }
|
bool isAlliance = race == 1 || race == 3 || race == 4 || race == 7 || race == 11;
|
||||||
// }
|
addclassCache[GetTeamClassIdx(isAlliance, claz)].push_back(guid);
|
||||||
// } while (results->NextRow());
|
collected++;
|
||||||
// }
|
} while (results->NextRow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_INFO("playerbots", "{} characters collected for addclass command.", collected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
|
void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
|
||||||
@ -1860,6 +1867,7 @@ void RandomPlayerbotMgr::GetBots()
|
|||||||
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT);
|
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT);
|
||||||
stmt->SetData(0, 0);
|
stmt->SetData(0, 0);
|
||||||
stmt->SetData(1, "add");
|
stmt->SetData(1, "add");
|
||||||
|
uint32 maxAllowedBotCount = GetEventValue(0, "bot_count");
|
||||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
@ -1868,6 +1876,9 @@ void RandomPlayerbotMgr::GetBots()
|
|||||||
uint32 bot = fields[0].Get<uint32>();
|
uint32 bot = fields[0].Get<uint32>();
|
||||||
if (GetEventValue(bot, "add"))
|
if (GetEventValue(bot, "add"))
|
||||||
currentBots.push_back(bot);
|
currentBots.push_back(bot);
|
||||||
|
|
||||||
|
if (currentBots.size() >= maxAllowedBotCount)
|
||||||
|
break;
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2396,51 +2407,12 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
else
|
else
|
||||||
++engine_dead;
|
++engine_dead;
|
||||||
|
|
||||||
uint8 spec = AiFactory::GetPlayerSpecTab(bot);
|
if (botAI->IsHeal(bot, true))
|
||||||
switch (bot->getClass())
|
|
||||||
{
|
|
||||||
case CLASS_DRUID:
|
|
||||||
if (spec == 2)
|
|
||||||
++heal;
|
++heal;
|
||||||
else
|
else if (botAI->IsTank(bot, true))
|
||||||
++dps;
|
|
||||||
break;
|
|
||||||
case CLASS_PALADIN:
|
|
||||||
if (spec == 1)
|
|
||||||
++tank;
|
|
||||||
else if (spec == 0)
|
|
||||||
++heal;
|
|
||||||
else
|
|
||||||
++dps;
|
|
||||||
break;
|
|
||||||
case CLASS_PRIEST:
|
|
||||||
if (spec != 2)
|
|
||||||
++heal;
|
|
||||||
else
|
|
||||||
++dps;
|
|
||||||
break;
|
|
||||||
case CLASS_SHAMAN:
|
|
||||||
if (spec == 2)
|
|
||||||
++heal;
|
|
||||||
else
|
|
||||||
++dps;
|
|
||||||
break;
|
|
||||||
case CLASS_WARRIOR:
|
|
||||||
if (spec == 2)
|
|
||||||
++tank;
|
++tank;
|
||||||
else
|
else
|
||||||
++dps;
|
++dps;
|
||||||
break;
|
|
||||||
case CLASS_DEATH_KNIGHT:
|
|
||||||
if (spec == 0)
|
|
||||||
tank++;
|
|
||||||
else
|
|
||||||
dps++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
++dps;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
|
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
|
||||||
{
|
{
|
||||||
@ -2779,4 +2751,3 @@ ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, Battlegrou
|
|||||||
|
|
||||||
return battleMasterGUID;
|
return battleMasterGUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
||||||
#define _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
#define _PLAYERBOT_RANDOMPLAYERBOTMGR_H
|
||||||
|
|
||||||
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotMgr.h"
|
#include "PlayerbotMgr.h"
|
||||||
|
|
||||||
struct BattlegroundInfo
|
struct BattlegroundInfo
|
||||||
@ -166,7 +167,10 @@ public:
|
|||||||
float getActivityMod() { return activityMod; }
|
float getActivityMod() { return activityMod; }
|
||||||
float getActivityPercentage() { return activityMod * 100.0f; }
|
float getActivityPercentage() { return activityMod * 100.0f; }
|
||||||
void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; }
|
void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; }
|
||||||
|
static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; }
|
||||||
|
|
||||||
|
void PrepareAddclassCache();
|
||||||
|
std::map<uint8, std::vector<ObjectGuid>> addclassCache;
|
||||||
protected:
|
protected:
|
||||||
void OnBotLoginInternal(Player* const bot) override;
|
void OnBotLoginInternal(Player* const bot) override;
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
|
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
|
||||||
|
|
||||||
|
const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) |
|
||||||
|
(1LL << 33) | (1LL << 24) | (1LL << 34);
|
||||||
uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING,
|
uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING,
|
||||||
SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING,
|
SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING,
|
||||||
SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING,
|
SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING,
|
||||||
@ -205,7 +207,8 @@ void PlayerbotFactory::Randomize(bool incremental)
|
|||||||
{
|
{
|
||||||
ClearAllItems();
|
ClearAllItems();
|
||||||
}
|
}
|
||||||
// bot->SaveToDB(false, false);
|
bot->RemoveAllSpellCooldown();
|
||||||
|
UnbindInstance();
|
||||||
|
|
||||||
bot->GiveLevel(level);
|
bot->GiveLevel(level);
|
||||||
bot->InitStatsForLevel();
|
bot->InitStatsForLevel();
|
||||||
@ -233,6 +236,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
|||||||
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1");
|
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1");
|
||||||
LOG_DEBUG("playerbots", "Initializing spells (step 1)...");
|
LOG_DEBUG("playerbots", "Initializing spells (step 1)...");
|
||||||
// bot->LearnDefaultSkills();
|
// bot->LearnDefaultSkills();
|
||||||
|
bot->LearnDefaultSkills();
|
||||||
InitClassSpells();
|
InitClassSpells();
|
||||||
InitAvailableSpells();
|
InitAvailableSpells();
|
||||||
if (pmo)
|
if (pmo)
|
||||||
@ -286,7 +290,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
|||||||
LOG_DEBUG("playerbots", "Initializing equipmemt...");
|
LOG_DEBUG("playerbots", "Initializing equipmemt...");
|
||||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||||
{
|
{
|
||||||
InitEquipment(incremental);
|
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
|
||||||
}
|
}
|
||||||
// bot->SaveToDB(false, false);
|
// bot->SaveToDB(false, false);
|
||||||
if (pmo)
|
if (pmo)
|
||||||
@ -423,7 +427,10 @@ void PlayerbotFactory::Refresh()
|
|||||||
InitFood();
|
InitFood();
|
||||||
InitReagents();
|
InitReagents();
|
||||||
// InitPotions();
|
// InitPotions();
|
||||||
|
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||||
|
{
|
||||||
InitTalentsTree(true, true, true);
|
InitTalentsTree(true, true, true);
|
||||||
|
}
|
||||||
InitPet();
|
InitPet();
|
||||||
InitPetTalents();
|
InitPetTalents();
|
||||||
InitClassSpells();
|
InitClassSpells();
|
||||||
@ -611,8 +618,9 @@ void PlayerbotFactory::InitPetTalents()
|
|||||||
// pet_family->petTalentType);
|
// pet_family->petTalentType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// pet->resetTalents();
|
|
||||||
std::unordered_map<uint32, std::vector<TalentEntry const*>> spells;
|
std::unordered_map<uint32, std::vector<TalentEntry const*>> spells;
|
||||||
|
bool diveTypePet = (1LL << ci->family) & diveMask;
|
||||||
|
|
||||||
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
||||||
{
|
{
|
||||||
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
||||||
@ -624,15 +632,24 @@ void PlayerbotFactory::InitPetTalents()
|
|||||||
// prevent learn talent for different family (cheating)
|
// prevent learn talent for different family (cheating)
|
||||||
if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
|
if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
|
||||||
continue;
|
continue;
|
||||||
|
bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 ||
|
||||||
|
talentInfo->TalentID == 2203;
|
||||||
|
if (diveClass && !diveTypePet)
|
||||||
|
continue;
|
||||||
|
bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 ||
|
||||||
|
talentInfo->TalentID == 2109;
|
||||||
|
if (dashClass && diveTypePet)
|
||||||
|
continue;
|
||||||
spells[talentInfo->Row].push_back(talentInfo);
|
spells[talentInfo->Row].push_back(talentInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 curTalentPoints = pet->GetFreeTalentPoints();
|
std::vector<std::vector<uint32>> order =
|
||||||
|
sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20];
|
||||||
uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel());
|
uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel());
|
||||||
|
|
||||||
|
if (order.empty())
|
||||||
|
{
|
||||||
int row = 0;
|
int row = 0;
|
||||||
// LOG_INFO("playerbots", "{} learning, max talent points: {}, cur: {}", bot->GetName().c_str(), maxTalentPoints,
|
|
||||||
// curTalentPoints);
|
|
||||||
for (auto i = spells.begin(); i != spells.end(); ++i, ++row)
|
for (auto i = spells.begin(); i != spells.end(); ++i, ++row)
|
||||||
{
|
{
|
||||||
std::vector<TalentEntry const*>& spells_row = i->second;
|
std::vector<TalentEntry const*>& spells_row = i->second;
|
||||||
@ -669,6 +686,63 @@ void PlayerbotFactory::InitPetTalents()
|
|||||||
spells_row.erase(spells_row.begin() + index);
|
spells_row.erase(spells_row.begin() + index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32 spec = pet_family->petTalentType;
|
||||||
|
uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel());
|
||||||
|
while (startPoints > 1 && startPoints < 20 &&
|
||||||
|
sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0)
|
||||||
|
{
|
||||||
|
startPoints--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32 points = startPoints; points <= 20; points++)
|
||||||
|
{
|
||||||
|
if (sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points].size() == 0)
|
||||||
|
continue;
|
||||||
|
for (std::vector<uint32>& p : sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points])
|
||||||
|
{
|
||||||
|
uint32 row = p[0], col = p[1], lvl = p[2];
|
||||||
|
uint32 talentID = 0;
|
||||||
|
uint32 learnLevel = 0;
|
||||||
|
std::vector<TalentEntry const*>& spell = spells[row];
|
||||||
|
for (TalentEntry const* talentInfo : spell)
|
||||||
|
{
|
||||||
|
if (talentInfo->Col != col)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (talentInfo->DependsOn)
|
||||||
|
{
|
||||||
|
bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn,
|
||||||
|
std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1));
|
||||||
|
}
|
||||||
|
talentID = talentInfo->TalentID;
|
||||||
|
|
||||||
|
uint32 currentTalentRank = 0;
|
||||||
|
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
|
||||||
|
{
|
||||||
|
if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
|
||||||
|
{
|
||||||
|
currentTalentRank = rank + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
learnLevel = std::min(lvl, pet->GetFreeTalentPoints() + currentTalentRank) - 1;
|
||||||
|
}
|
||||||
|
bot->LearnPetTalent(pet->GetGUID(), talentID, learnLevel);
|
||||||
|
if (pet->GetFreeTalentPoints() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pet->GetFreeTalentPoints() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
bot->SendTalentsInfoData(true);
|
bot->SendTalentsInfoData(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,7 +890,7 @@ void PlayerbotFactory::ClearEverything()
|
|||||||
ClearSpells();
|
ClearSpells();
|
||||||
ClearInventory();
|
ClearInventory();
|
||||||
ResetQuests();
|
ResetQuests();
|
||||||
bot->SaveToDB(false, false);
|
// bot->SaveToDB(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotFactory::ClearSpells()
|
void PlayerbotFactory::ClearSpells()
|
||||||
@ -878,14 +952,37 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa
|
|||||||
uint32 total_tabs = tabs[0] + tabs[1] + tabs[2];
|
uint32 total_tabs = tabs[0] + tabs[1] + tabs[2];
|
||||||
if (increment && total_tabs != 0)
|
if (increment && total_tabs != 0)
|
||||||
{
|
{
|
||||||
|
/// @todo: match current talent with template
|
||||||
specTab = AiFactory::GetPlayerSpecTab(bot);
|
specTab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
|
/// @todo: fix cat druid hardcode
|
||||||
|
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 &&
|
||||||
|
!bot->HasAura(16931))
|
||||||
|
specTab = 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32 point = urand(0, 100);
|
uint32 pointSum = 0;
|
||||||
uint32 p1 = sPlayerbotAIConfig->randomClassSpecProb[cls][0];
|
for (int i = 0; i < MAX_SPECNO; i++)
|
||||||
uint32 p2 = p1 + sPlayerbotAIConfig->randomClassSpecProb[cls][1];
|
{
|
||||||
specTab = point < p1 ? 0 : (point < p2 ? 1 : 2);
|
pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i];
|
||||||
|
}
|
||||||
|
uint32 point = urand(1, pointSum);
|
||||||
|
uint32 currentP = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_SPECNO; i++)
|
||||||
|
{
|
||||||
|
currentP += sPlayerbotAIConfig->randomClassSpecProb[cls][i];
|
||||||
|
if (point <= currentP)
|
||||||
|
{
|
||||||
|
specTab = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == MAX_SPECNO)
|
||||||
|
{
|
||||||
|
specTab = 0;
|
||||||
|
LOG_ERROR("playerbots", "Fail to select spec num for bot {}! Set to 0.", bot->GetName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
@ -896,12 +993,13 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa
|
|||||||
{
|
{
|
||||||
InitTalentsByTemplate(specTab);
|
InitTalentsByTemplate(specTab);
|
||||||
}
|
}
|
||||||
else
|
// always use template now
|
||||||
{
|
// else
|
||||||
InitTalents(specTab);
|
// {
|
||||||
if (bot->GetFreeTalentPoints())
|
// InitTalents(specTab);
|
||||||
InitTalents((specTab + 1) % 3);
|
// if (bot->GetFreeTalentPoints())
|
||||||
}
|
// InitTalents((specTab + 1) % 3);
|
||||||
|
// }
|
||||||
bot->SendTalentsInfoData(false);
|
bot->SendTalentsInfoData(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,7 +1297,7 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto)
|
|||||||
{
|
{
|
||||||
case CLASS_PRIEST:
|
case CLASS_PRIEST:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_MAGE:
|
case CLASS_MAGE:
|
||||||
@ -1213,15 +1311,12 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto)
|
|||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_PALADIN:
|
case CLASS_PALADIN:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 &&
|
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 &&
|
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD)
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
case CLASS_DEATH_KNIGHT:
|
case CLASS_DEATH_KNIGHT:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 &&
|
||||||
@ -1233,28 +1328,30 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto)
|
|||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_DRUID:
|
case CLASS_DRUID:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_HUNTER:
|
case CLASS_HUNTER:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case CLASS_ROGUE:
|
case CLASS_ROGUE:
|
||||||
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE &&
|
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW &&
|
||||||
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN)
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN &&
|
||||||
|
proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1274,7 +1371,8 @@ bool PlayerbotFactory::CanEquipItem(ItemTemplate const* proto)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint32 requiredLevel = proto->RequiredLevel;
|
uint32 requiredLevel = proto->RequiredLevel;
|
||||||
bool hasItem = bot->HasItemCount(proto->ItemId, 1, true);
|
// disable since bad performance
|
||||||
|
bool hasItem = bot->HasItemCount(proto->ItemId, 1, false);
|
||||||
// bot->GetItemCount()
|
// bot->GetItemCount()
|
||||||
// !requiredLevel -> it's a quest reward item
|
// !requiredLevel -> it's a quest reward item
|
||||||
if (!requiredLevel && hasItem)
|
if (!requiredLevel && hasItem)
|
||||||
@ -1430,10 +1528,10 @@ void Shuffle(std::vector<uint32>& items)
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void PlayerbotFactory::InitEquipment(bool incremental)
|
void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
||||||
{
|
{
|
||||||
std::unordered_map<uint8, std::vector<uint32>> items;
|
std::unordered_map<uint8, std::vector<uint32>> items;
|
||||||
int tab = AiFactory::GetPlayerSpecTab(bot);
|
// int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
|
|
||||||
uint32 blevel = bot->GetLevel();
|
uint32 blevel = bot->GetLevel();
|
||||||
int32 delta = 2;
|
int32 delta = 2;
|
||||||
@ -1450,6 +1548,7 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
else if (blevel == 80)
|
else if (blevel == 80)
|
||||||
delta = 9;
|
delta = 9;
|
||||||
|
|
||||||
|
StatsWeightCalculator calculator(bot);
|
||||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||||
{
|
{
|
||||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||||
@ -1467,6 +1566,15 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||||
|
|
||||||
|
if (second_chance && oldItem)
|
||||||
|
{
|
||||||
|
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||||
|
|
||||||
uint32 desiredQuality = itemQuality;
|
uint32 desiredQuality = itemQuality;
|
||||||
if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL)
|
if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL)
|
||||||
{
|
{
|
||||||
@ -1481,10 +1589,13 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
{
|
{
|
||||||
for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType))
|
for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType))
|
||||||
{
|
{
|
||||||
if (itemId == 46978)
|
if (itemId == 46978) // shaman earth ring totem
|
||||||
{ // shaman earth ring totem
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
uint32 skipProb = 25;
|
||||||
|
if (urand(1, 100) <= skipProb)
|
||||||
|
continue;
|
||||||
|
|
||||||
// disable next expansion gear
|
// disable next expansion gear
|
||||||
if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728)
|
if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728)
|
||||||
@ -1510,9 +1621,9 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
|
|
||||||
if (proto->Quality != desiredQuality)
|
if (proto->Quality != desiredQuality)
|
||||||
continue;
|
continue;
|
||||||
|
// delay heavy check
|
||||||
if (!CanEquipItem(proto))
|
// if (!CanEquipItem(proto))
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
if (proto->Class == ITEM_CLASS_ARMOR &&
|
if (proto->Class == ITEM_CLASS_ARMOR &&
|
||||||
(slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS ||
|
(slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS ||
|
||||||
@ -1528,9 +1639,9 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE &&
|
if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE &&
|
||||||
proto->Class != ITEM_CLASS_WEAPON)
|
proto->Class != ITEM_CLASS_WEAPON)
|
||||||
continue;
|
continue;
|
||||||
|
// delay heavy check
|
||||||
uint16 dest = 0;
|
// uint16 dest = 0;
|
||||||
if (CanEquipUnseenItem(slot, dest, itemId))
|
// if (CanEquipUnseenItem(slot, dest, itemId))
|
||||||
items[slot].push_back(itemId);
|
items[slot].push_back(itemId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1538,72 +1649,130 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL);
|
} while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL);
|
||||||
}
|
|
||||||
|
|
||||||
StatsWeightCalculator calculator(bot);
|
|
||||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
|
||||||
{
|
|
||||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (level < 25 && slot == EQUIPMENT_SLOT_NECK)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (level < 25 && slot == EQUIPMENT_SLOT_HEAD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::vector<uint32>& ids = items[slot];
|
std::vector<uint32>& ids = items[slot];
|
||||||
if (ids.empty())
|
if (ids.empty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
|
||||||
|
|
||||||
if (incremental && !IsDesiredReplacement(oldItem))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float bestScoreForSlot = -1;
|
float bestScoreForSlot = -1;
|
||||||
uint32 bestItemForSlot = 0;
|
uint32 bestItemForSlot = 0;
|
||||||
for (int index = 0; index < ids.size(); index++)
|
for (int index = 0; index < ids.size(); index++)
|
||||||
{
|
{
|
||||||
uint32 skipProb = 25;
|
|
||||||
if (urand(0, 100) <= skipProb)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint32 newItemId = ids[index];
|
uint32 newItemId = ids[index];
|
||||||
|
|
||||||
uint16 dest;
|
|
||||||
|
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||||
|
|
||||||
if (!CanEquipItem(proto))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (oldItem && oldItem->GetTemplate()->ItemId == newItemId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float cur_score = calculator.CalculateItem(newItemId);
|
float cur_score = calculator.CalculateItem(newItemId);
|
||||||
if (cur_score > bestScoreForSlot)
|
if (cur_score > bestScoreForSlot)
|
||||||
{
|
{
|
||||||
|
// delay heavy check to here
|
||||||
|
if (!CanEquipItem(proto))
|
||||||
|
continue;
|
||||||
|
uint16 dest;
|
||||||
|
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
||||||
|
continue;
|
||||||
bestScoreForSlot = cur_score;
|
bestScoreForSlot = cur_score;
|
||||||
bestItemForSlot = newItemId;
|
bestItemForSlot = newItemId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestItemForSlot == 0)
|
if (bestItemForSlot == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
uint16 dest;
|
||||||
|
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incremental && oldItem)
|
||||||
|
{
|
||||||
|
float old_score = calculator.CalculateItem(oldItem->GetEntry());
|
||||||
|
if (bestScoreForSlot < 1.2f * old_score)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (oldItem)
|
if (oldItem)
|
||||||
{
|
{
|
||||||
|
uint8 bagIndex = oldItem->GetBagSlot();
|
||||||
|
uint8 slot = oldItem->GetSlot();
|
||||||
|
uint8 dstBag = NULL_BAG;
|
||||||
|
|
||||||
|
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
||||||
|
packet << bagIndex << slot << dstBag;
|
||||||
|
bot->GetSession()->HandleAutoStoreBagItemOpcode(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||||
|
// fail to store in bag
|
||||||
|
if (oldItem)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
||||||
|
bot->AutoUnequipOffhandIfNeed();
|
||||||
|
// if (newItem)
|
||||||
|
// {
|
||||||
|
// newItem->AddToWorld();
|
||||||
|
// newItem->AddToUpdateQueueOf(bot);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// Secondary init for better equips
|
||||||
|
/// @todo: clean up duplicate code
|
||||||
|
if (second_chance)
|
||||||
|
{
|
||||||
|
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||||
|
{
|
||||||
|
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (level < 30 && slot == EQUIPMENT_SLOT_NECK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (level < 25 && slot == EQUIPMENT_SLOT_HEAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||||
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||||
|
|
||||||
|
std::vector<uint32>& ids = items[slot];
|
||||||
|
if (ids.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float bestScoreForSlot = -1;
|
||||||
|
uint32 bestItemForSlot = 0;
|
||||||
|
for (int index = 0; index < ids.size(); index++)
|
||||||
|
{
|
||||||
|
uint32 newItemId = ids[index];
|
||||||
|
|
||||||
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||||
|
|
||||||
|
float cur_score = calculator.CalculateItem(newItemId);
|
||||||
|
if (cur_score > bestScoreForSlot)
|
||||||
|
{
|
||||||
|
// delay heavy check to here
|
||||||
|
if (!CanEquipItem(proto))
|
||||||
|
continue;
|
||||||
|
uint16 dest;
|
||||||
|
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
||||||
|
continue;
|
||||||
|
bestScoreForSlot = cur_score;
|
||||||
|
bestItemForSlot = newItemId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestItemForSlot == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
uint16 dest;
|
uint16 dest;
|
||||||
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
||||||
@ -1611,12 +1780,12 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
||||||
if (newItem)
|
bot->AutoUnequipOffhandIfNeed();
|
||||||
{
|
// if (newItem)
|
||||||
newItem->AddToWorld();
|
// {
|
||||||
newItem->AddToUpdateQueueOf(bot);
|
// newItem->AddToWorld();
|
||||||
// bot->AutoUnequipOffhandIfNeed();
|
// newItem->AddToUpdateQueueOf(bot);
|
||||||
// EnchantItem(newItem);
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1788,11 +1957,11 @@ void PlayerbotFactory::InitBags(bool destroyOld)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item* newItem = bot->EquipNewItem(dest, newItemId, true);
|
Item* newItem = bot->EquipNewItem(dest, newItemId, true);
|
||||||
if (newItem)
|
// if (newItem)
|
||||||
{
|
// {
|
||||||
newItem->AddToWorld();
|
// newItem->AddToWorld();
|
||||||
newItem->AddToUpdateQueueOf(bot);
|
// newItem->AddToUpdateQueueOf(bot);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1892,7 +2061,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item)
|
|||||||
|
|
||||||
if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true))
|
if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true))
|
||||||
{
|
{
|
||||||
InventoryResult result = bot->CanEquipItem(slot, dest, pItem, true, false);
|
InventoryResult result = botAI ? botAI->CanEquipItem(slot, dest, pItem, true, true)
|
||||||
|
: bot->CanEquipItem(slot, dest, pItem, true, true);
|
||||||
pItem->RemoveFromUpdateQueueOf(bot);
|
pItem->RemoveFromUpdateQueueOf(bot);
|
||||||
delete pItem;
|
delete pItem;
|
||||||
return result == EQUIP_ERR_OK;
|
return result == EQUIP_ERR_OK;
|
||||||
@ -2147,8 +2317,6 @@ void PlayerbotFactory::SetRandomSkill(uint16 id)
|
|||||||
|
|
||||||
void PlayerbotFactory::InitAvailableSpells()
|
void PlayerbotFactory::InitAvailableSpells()
|
||||||
{
|
{
|
||||||
bot->LearnDefaultSkills();
|
|
||||||
|
|
||||||
if (trainerIdCache.empty())
|
if (trainerIdCache.empty())
|
||||||
{
|
{
|
||||||
CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
|
CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
|
||||||
@ -2166,6 +2334,8 @@ void PlayerbotFactory::InitAvailableSpells()
|
|||||||
trainerIdCache.push_back(trainerId);
|
trainerIdCache.push_back(trainerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// uint32 learnedCounter = 0;
|
||||||
|
// uint32 oktest = 0;
|
||||||
for (uint32 trainerId : trainerIdCache)
|
for (uint32 trainerId : trainerIdCache)
|
||||||
{
|
{
|
||||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||||
@ -2209,16 +2379,19 @@ void PlayerbotFactory::InitAvailableSpells()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// oktest++;
|
||||||
if (tSpell->learnedSpell[0])
|
if (tSpell->learnedSpell[0])
|
||||||
{
|
{
|
||||||
bot->learnSpell(tSpell->learnedSpell[0], false);
|
bot->learnSpell(tSpell->learnedSpell[0], false);
|
||||||
}
|
}
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
botAI->CastSpell(tSpell->spell, bot);
|
// botAI->CastSpell(tSpell->spell, bot);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
// LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest);
|
||||||
|
// if (++learnedCounter > 20)
|
||||||
|
// break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2628,7 +2801,10 @@ void PlayerbotFactory::InitAmmo()
|
|||||||
bot->SetAmmo(entry);
|
bot->SetAmmo(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * (quality + 1); }
|
uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality)
|
||||||
|
{
|
||||||
|
return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality));
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerbotFactory::InitMounts()
|
void PlayerbotFactory::InitMounts()
|
||||||
{
|
{
|
||||||
@ -2796,7 +2972,8 @@ std::vector<uint32> PlayerbotFactory::GetCurrentGemsCount()
|
|||||||
Item* pItem2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
|
Item* pItem2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
|
||||||
if (pItem2 && !pItem2->IsBroken() && pItem2->HasSocket())
|
if (pItem2 && !pItem2->IsBroken() && pItem2->HasSocket())
|
||||||
{
|
{
|
||||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; ++enchant_slot)
|
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT;
|
||||||
|
++enchant_slot)
|
||||||
{
|
{
|
||||||
if (enchant_slot == BONUS_ENCHANTMENT_SLOT)
|
if (enchant_slot == BONUS_ENCHANTMENT_SLOT)
|
||||||
continue;
|
continue;
|
||||||
@ -3062,6 +3239,9 @@ void PlayerbotFactory::InitGlyphs(bool increment)
|
|||||||
|
|
||||||
uint8 cls = bot->getClass();
|
uint8 cls = bot->getClass();
|
||||||
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
|
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
|
||||||
|
/// @todo: fix cat druid hardcode
|
||||||
|
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
|
||||||
|
tab = 3;
|
||||||
std::list<uint32> glyphs;
|
std::list<uint32> glyphs;
|
||||||
ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore();
|
ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore();
|
||||||
for (ItemTemplateContainer::const_iterator i = itemTemplates->begin(); i != itemTemplates->end(); ++i)
|
for (ItemTemplateContainer::const_iterator i = itemTemplates->begin(); i != itemTemplates->end(); ++i)
|
||||||
@ -3701,7 +3881,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
}
|
}
|
||||||
availableGems.push_back(enchantGem);
|
availableGems.push_back(enchantGem);
|
||||||
}
|
}
|
||||||
|
StatsWeightCalculator calculator(bot);
|
||||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||||
{
|
{
|
||||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||||
@ -3751,7 +3931,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (enchant->requiredSkill && (!bot->HasSkill(enchant->requiredSkill) || (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue)))
|
if (enchant->requiredSkill &&
|
||||||
|
(!bot->HasSkill(enchant->requiredSkill) ||
|
||||||
|
(bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3759,7 +3941,6 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
StatsWeightCalculator calculator(bot);
|
|
||||||
float score = calculator.CalculateEnchant(enchant_id);
|
float score = calculator.CalculateEnchant(enchant_id);
|
||||||
if (score >= bestScore)
|
if (score >= bestScore)
|
||||||
{
|
{
|
||||||
@ -3798,7 +3979,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
if (!gemProperties)
|
if (!gemProperties)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((socketColor & gemProperties->color) == 0)
|
if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint32 enchant_id = gemProperties->spellitemenchantement;
|
uint32 enchant_id = gemProperties->spellitemenchantement;
|
||||||
@ -3809,6 +3990,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
StatsWeightCalculator calculator(bot);
|
StatsWeightCalculator calculator(bot);
|
||||||
float score = calculator.CalculateEnchant(enchant_id);
|
float score = calculator.CalculateEnchant(enchant_id);
|
||||||
if (curCount[0] != 0)
|
if (curCount[0] != 0)
|
||||||
|
{
|
||||||
// Ensure meta gem activation
|
// Ensure meta gem activation
|
||||||
for (int i = 1; i < curCount.size(); i++)
|
for (int i = 1; i < curCount.size(); i++)
|
||||||
{
|
{
|
||||||
@ -3818,6 +4000,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (socketColor & gemProperties->color)
|
||||||
|
score *= 1.2;
|
||||||
if (score > bestGemScore)
|
if (score > bestGemScore)
|
||||||
{
|
{
|
||||||
enchantIdChosen = enchant_id;
|
enchantIdChosen = enchant_id;
|
||||||
|
|||||||
@ -122,7 +122,7 @@ public:
|
|||||||
static void InitTalentsByParsedSpecLink(Player* bot, std::vector<std::vector<uint32>> parsedSpecLink, bool reset);
|
static void InitTalentsByParsedSpecLink(Player* bot, std::vector<std::vector<uint32>> parsedSpecLink, bool reset);
|
||||||
void InitAvailableSpells();
|
void InitAvailableSpells();
|
||||||
void InitClassSpells();
|
void InitClassSpells();
|
||||||
void InitEquipment(bool incremental);
|
void InitEquipment(bool incremental, bool second_chance = false);
|
||||||
void InitPet();
|
void InitPet();
|
||||||
void InitAmmo();
|
void InitAmmo();
|
||||||
static uint32 CalcMixedGearScore(uint32 gs, uint32 quality);
|
static uint32 CalcMixedGearScore(uint32 gs, uint32 quality);
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
#include "UpdateFields.h"
|
#include "UpdateFields.h"
|
||||||
|
|
||||||
StatsCollector::StatsCollector(CollectorType type) : type_(type) { Reset(); }
|
StatsCollector::StatsCollector(CollectorType type, int32 cls) : type_(type), cls_(cls) { Reset(); }
|
||||||
|
|
||||||
void StatsCollector::Reset()
|
void StatsCollector::Reset()
|
||||||
{
|
{
|
||||||
@ -233,7 +233,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
|||||||
if (type_ != CollectorType::SPELL_HEAL)
|
if (type_ != CollectorType::SPELL_HEAL)
|
||||||
stats[STATS_TYPE_CRIT] += 50;
|
stats[STATS_TYPE_CRIT] += 50;
|
||||||
return true;
|
return true;
|
||||||
case 59620:
|
case 59620: // Berserk
|
||||||
if (type_ == CollectorType::MELEE)
|
if (type_ == CollectorType::MELEE)
|
||||||
stats[STATS_TYPE_ATTACK_POWER] += 120;
|
stats[STATS_TYPE_ATTACK_POWER] += 120;
|
||||||
return true;
|
return true;
|
||||||
@ -243,6 +243,18 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
|||||||
case 67771: // Death's Verdict (heroic)
|
case 67771: // Death's Verdict (heroic)
|
||||||
stats[STATS_TYPE_ATTACK_POWER] += 260;
|
stats[STATS_TYPE_ATTACK_POWER] += 260;
|
||||||
return true;
|
return true;
|
||||||
|
case 71406: // Tiny Abomination in a Jar
|
||||||
|
if (cls_ == CLASS_PALADIN)
|
||||||
|
stats[STATS_TYPE_ATTACK_POWER] += 600;
|
||||||
|
else
|
||||||
|
stats[STATS_TYPE_ATTACK_POWER] += 150;
|
||||||
|
return true;
|
||||||
|
case 71545: // Tiny Abomination in a Jar (heroic)
|
||||||
|
if (cls_ == CLASS_PALADIN)
|
||||||
|
stats[STATS_TYPE_ATTACK_POWER] += 800;
|
||||||
|
else
|
||||||
|
stats[STATS_TYPE_ATTACK_POWER] += 200;
|
||||||
|
return true;
|
||||||
case 71519: // Deathbringer's Will
|
case 71519: // Deathbringer's Will
|
||||||
stats[STATS_TYPE_ATTACK_POWER] += 350;
|
stats[STATS_TYPE_ATTACK_POWER] += 350;
|
||||||
return true;
|
return true;
|
||||||
@ -254,6 +266,10 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
|||||||
/// Noticing that heroic item can not be triggered, probably a bug to report to AC
|
/// Noticing that heroic item can not be triggered, probably a bug to report to AC
|
||||||
if (type_ == CollectorType::SPELL_HEAL)
|
if (type_ == CollectorType::SPELL_HEAL)
|
||||||
return true;
|
return true;
|
||||||
|
break;
|
||||||
|
case 71903: // Shadowmourne
|
||||||
|
stats[STATS_TYPE_STRENGTH] += 200;
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,7 @@ enum CollectorType : uint8
|
|||||||
class StatsCollector
|
class StatsCollector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StatsCollector(CollectorType type);
|
StatsCollector(CollectorType type, int32 cls = -1);
|
||||||
StatsCollector(StatsCollector& stats) = default;
|
StatsCollector(StatsCollector& stats) = default;
|
||||||
void Reset();
|
void Reset();
|
||||||
void CollectItemStats(ItemTemplate const* proto);
|
void CollectItemStats(ItemTemplate const* proto);
|
||||||
@ -78,6 +78,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CollectorType type_;
|
CollectorType type_;
|
||||||
|
uint32 cls_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -12,6 +12,7 @@
|
|||||||
#include "ItemTemplate.h"
|
#include "ItemTemplate.h"
|
||||||
#include "ObjectMgr.h"
|
#include "ObjectMgr.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotFactory.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "StatsCollector.h"
|
#include "StatsCollector.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
@ -26,10 +27,19 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
|
|||||||
type_ = CollectorType::MELEE;
|
type_ = CollectorType::MELEE;
|
||||||
else
|
else
|
||||||
type_ = CollectorType::RANGED;
|
type_ = CollectorType::RANGED;
|
||||||
collector_ = std::make_unique<StatsCollector>(type_);
|
|
||||||
|
|
||||||
cls = player->getClass();
|
cls = player->getClass();
|
||||||
tab = AiFactory::GetPlayerSpecTab(player);
|
tab = AiFactory::GetPlayerSpecTab(player);
|
||||||
|
collector_ = std::make_unique<StatsCollector>(type_, cls);
|
||||||
|
|
||||||
|
|
||||||
|
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||||
|
hitOverflowType_ = CollectorType::SPELL;
|
||||||
|
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)
|
||||||
|
hitOverflowType_ = CollectorType::SPELL;
|
||||||
|
else if (cls == CLASS_ROGUE)
|
||||||
|
hitOverflowType_ = CollectorType::SPELL;
|
||||||
|
else
|
||||||
|
hitOverflowType_ = type_;
|
||||||
|
|
||||||
enable_overflow_penalty_ = true;
|
enable_overflow_penalty_ = true;
|
||||||
enable_item_set_bonus_ = true;
|
enable_item_set_bonus_ = true;
|
||||||
@ -57,6 +67,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
|||||||
|
|
||||||
collector_->CollectItemStats(proto);
|
collector_->CollectItemStats(proto);
|
||||||
|
|
||||||
|
if (enable_overflow_penalty_)
|
||||||
|
ApplyOverflowPenalty(player_);
|
||||||
|
|
||||||
GenerateWeights(player_);
|
GenerateWeights(player_);
|
||||||
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
||||||
{
|
{
|
||||||
@ -72,7 +85,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
|||||||
|
|
||||||
if (enable_quality_blend_)
|
if (enable_quality_blend_)
|
||||||
// Blend with item quality and level
|
// Blend with item quality and level
|
||||||
weight_ *= (proto->Quality + 1) * proto->ItemLevel;
|
weight_ *= PlayerbotFactory::CalcMixedGearScore(proto->ItemLevel, proto->Quality);
|
||||||
|
|
||||||
return weight_;
|
return weight_;
|
||||||
}
|
}
|
||||||
@ -88,6 +101,9 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
|
|||||||
|
|
||||||
collector_->CollectEnchantStats(enchant);
|
collector_->CollectEnchantStats(enchant);
|
||||||
|
|
||||||
|
if (enable_overflow_penalty_)
|
||||||
|
ApplyOverflowPenalty(player_);
|
||||||
|
|
||||||
GenerateWeights(player_);
|
GenerateWeights(player_);
|
||||||
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
||||||
{
|
{
|
||||||
@ -101,9 +117,7 @@ void StatsWeightCalculator::GenerateWeights(Player* player)
|
|||||||
{
|
{
|
||||||
GenerateBasicWeights(player);
|
GenerateBasicWeights(player);
|
||||||
GenerateAdditionalWeights(player);
|
GenerateAdditionalWeights(player);
|
||||||
|
ApplyWeightFinetune(player);
|
||||||
if (enable_overflow_penalty_)
|
|
||||||
ApplyOverflowPenalty(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||||
@ -114,46 +128,58 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
|||||||
|
|
||||||
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
|
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 2.4f;
|
stats_weights_[STATS_TYPE_AGILITY] += 2.5f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.6f;
|
stats_weights_[STATS_TYPE_HIT] += 1.7f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.4f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.6f;
|
||||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f;
|
stats_weights_[STATS_TYPE_RANGED_DPS] += 7.5f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP)
|
else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP)
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 2.2f;
|
stats_weights_[STATS_TYPE_AGILITY] += 2.3f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.2f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.25f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 2.1f;
|
stats_weights_[STATS_TYPE_HIT] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 2.0f;
|
stats_weights_[STATS_TYPE_CRIT] += 2.0f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
||||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f;
|
stats_weights_[STATS_TYPE_RANGED_DPS] += 10.0f;
|
||||||
}
|
}
|
||||||
else if ((cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player)))
|
else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT)
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
|
stats_weights_[STATS_TYPE_AGILITY] += 1.9f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.8f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 2.0f;
|
stats_weights_[STATS_TYPE_HIT] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.6f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.4f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.7f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||||
|
}
|
||||||
|
else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player))
|
||||||
|
{
|
||||||
|
stats_weights_[STATS_TYPE_AGILITY] += 2.2f;
|
||||||
|
stats_weights_[STATS_TYPE_STRENGTH] += 2.4f;
|
||||||
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.3f;
|
||||||
|
stats_weights_[STATS_TYPE_HIT] += 1.9f;
|
||||||
|
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
|
||||||
|
stats_weights_[STATS_TYPE_HASTE] += 2.1f;
|
||||||
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
|
||||||
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 15.0f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY))
|
else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY))
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
|
stats_weights_[STATS_TYPE_AGILITY] += 1.5f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.6f;
|
stats_weights_[STATS_TYPE_HIT] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.3f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.1f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.5f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury
|
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury
|
||||||
@ -182,71 +208,86 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
|||||||
}
|
}
|
||||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk
|
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
|
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.6f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 2.8f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.7f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 2.3f;
|
stats_weights_[STATS_TYPE_HIT] += 2.3f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 2.2f;
|
stats_weights_[STATS_TYPE_CRIT] += 2.2f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
stats_weights_[STATS_TYPE_HASTE] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 0.5f;
|
stats_weights_[STATS_TYPE_AGILITY] += 0.9f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.0f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.8f;
|
stats_weights_[STATS_TYPE_HIT] += 2.2f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.0f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.7f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.7f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
|
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 1.1f;
|
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
||||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.15f;
|
stats_weights_[STATS_TYPE_INTELLECT] += 0.1f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f;
|
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.8f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.9f;
|
stats_weights_[STATS_TYPE_HIT] += 1.9f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.2f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.7f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.3f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.6f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 9.0f;
|
||||||
}
|
}
|
||||||
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement
|
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
|
stats_weights_[STATS_TYPE_AGILITY] += 1.4f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
||||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.5f;
|
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
|
||||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.95f;
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f;
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.9f;
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.7f;
|
stats_weights_[STATS_TYPE_HIT] += 2.1f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 1.4f;
|
stats_weights_[STATS_TYPE_CRIT] += 1.5f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.8f;
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.2f;
|
stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_WARLOCK ||
|
else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
|
||||||
cls == CLASS_MAGE ||
|
|
||||||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
|
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
|
||||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element
|
|
||||||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
|
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.5f;
|
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
|
||||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.4f;
|
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
|
||||||
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
|
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_SPELL_PENETRATION] += 1.0f;
|
|
||||||
stats_weights_[STATS_TYPE_HIT] += 1.1f;
|
stats_weights_[STATS_TYPE_HIT] += 1.1f;
|
||||||
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
|
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
|
||||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
||||||
}
|
}
|
||||||
|
else if (cls == CLASS_MAGE && tab == MAGE_TAB_FIRE)
|
||||||
|
{
|
||||||
|
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
|
||||||
|
stats_weights_[STATS_TYPE_SPIRIT] += 0.7f;
|
||||||
|
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
|
||||||
|
stats_weights_[STATS_TYPE_HIT] += 1.2f;
|
||||||
|
stats_weights_[STATS_TYPE_CRIT] += 1.1f;
|
||||||
|
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
|
||||||
|
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
||||||
|
}
|
||||||
|
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL)
|
||||||
|
{
|
||||||
|
stats_weights_[STATS_TYPE_INTELLECT] += 0.25f;
|
||||||
|
stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f;
|
||||||
|
stats_weights_[STATS_TYPE_HIT] += 1.1f;
|
||||||
|
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
|
||||||
|
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||||
|
}
|
||||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
||||||
(cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
(cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION) || // heal
|
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION) || // heal
|
||||||
@ -260,7 +301,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
|||||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f;
|
||||||
}
|
}
|
||||||
else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))
|
else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||||
|
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))
|
||||||
{
|
{
|
||||||
stats_weights_[STATS_TYPE_AGILITY] += 2.0f;
|
stats_weights_[STATS_TYPE_AGILITY] += 2.0f;
|
||||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.0f;
|
stats_weights_[STATS_TYPE_STRENGTH] += 1.0f;
|
||||||
@ -323,6 +365,8 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player)
|
|||||||
{
|
{
|
||||||
if (player->HasAura(34484))
|
if (player->HasAura(34484))
|
||||||
stats_weights_[STATS_TYPE_INTELLECT] += 1.1f;
|
stats_weights_[STATS_TYPE_INTELLECT] += 1.1f;
|
||||||
|
if (player->HasAura(56341))
|
||||||
|
stats_weights_[STATS_TYPE_STAMINA] += 0.3f;
|
||||||
}
|
}
|
||||||
else if (cls == CLASS_WARRIOR)
|
else if (cls == CLASS_WARRIOR)
|
||||||
{
|
{
|
||||||
@ -383,7 +427,8 @@ void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate c
|
|||||||
void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto)
|
void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto)
|
||||||
{
|
{
|
||||||
uint32 socketNum = 0;
|
uint32 socketNum = 0;
|
||||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
|
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS;
|
||||||
|
++enchant_slot)
|
||||||
{
|
{
|
||||||
uint8 socketColor = proto->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color;
|
uint8 socketColor = proto->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color;
|
||||||
|
|
||||||
@ -400,12 +445,12 @@ void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate co
|
|||||||
|
|
||||||
void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||||
{
|
{
|
||||||
// penalty for different type armor
|
// // penalty for different type armor
|
||||||
if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH &&
|
// if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH &&
|
||||||
proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass))
|
// proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass))
|
||||||
{
|
// {
|
||||||
weight_ *= 0.8;
|
// weight_ *= 1.0;
|
||||||
}
|
// }
|
||||||
// double hand
|
// double hand
|
||||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||||
{
|
{
|
||||||
@ -430,7 +475,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
|||||||
}
|
}
|
||||||
// spec with double hand
|
// spec with double hand
|
||||||
// fury without duel wield, arms, bear, retribution, blood dk
|
// fury without duel wield, arms, bear, retribution, blood dk
|
||||||
if (isDoubleHand &&
|
if (!isDoubleHand &&
|
||||||
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
|
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
|
||||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
||||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
||||||
@ -438,13 +483,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
|||||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
||||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
|
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
|
||||||
{
|
{
|
||||||
weight_ *= 10;
|
weight_ *= 0.1;
|
||||||
}
|
}
|
||||||
// fury with titan's grip
|
// fury with titan's grip
|
||||||
if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) &&
|
||||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip()))
|
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip()))
|
||||||
{
|
{
|
||||||
weight_ *= 10;
|
weight_ *= 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||||
@ -457,16 +502,19 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
|||||||
{
|
{
|
||||||
weight_ *= 0.1;
|
weight_ *= 0.1;
|
||||||
}
|
}
|
||||||
if (cls == CLASS_ROGUE && player_->HasAura(13964)
|
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
|
||||||
&& (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
|
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
|
||||||
{
|
{
|
||||||
weight_ *= 1.1;
|
weight_ *= 1.1;
|
||||||
}
|
}
|
||||||
if (cls == CLASS_WARRIOR && player_->HasAura(12785)
|
if (cls == CLASS_WARRIOR && player_->HasAura(12785) &&
|
||||||
&& (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2))
|
(proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2))
|
||||||
{
|
{
|
||||||
weight_ *= 1.1;
|
weight_ *= 1.1;
|
||||||
}
|
}
|
||||||
|
bool slowDelay = proto->Delay > 2500;
|
||||||
|
if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay)
|
||||||
|
weight_ *= 1.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,37 +539,57 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
float hit_current, hit_overflow;
|
float hit_current, hit_overflow;
|
||||||
if (type_ == CollectorType::SPELL)
|
float validPoints;
|
||||||
|
// m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||||
|
// m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE);
|
||||||
|
if (hitOverflowType_ == CollectorType::SPELL)
|
||||||
{
|
{
|
||||||
hit_current = player->GetRatingBonusValue(CR_HIT_SPELL);
|
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
|
||||||
|
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
|
||||||
hit_overflow = SPELL_HIT_OVERFLOW;
|
hit_overflow = SPELL_HIT_OVERFLOW;
|
||||||
|
if (hit_overflow > hit_current)
|
||||||
|
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_SPELL);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
}
|
}
|
||||||
else if (type_ == CollectorType::MELEE)
|
else if (hitOverflowType_ == CollectorType::MELEE)
|
||||||
{
|
{
|
||||||
hit_current = player->GetRatingBonusValue(CR_HIT_MELEE);
|
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||||
|
hit_current += player->GetRatingBonusValue(CR_HIT_MELEE);
|
||||||
hit_overflow = MELEE_HIT_OVERFLOW;
|
hit_overflow = MELEE_HIT_OVERFLOW;
|
||||||
|
if (hit_overflow > hit_current)
|
||||||
|
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_MELEE);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hit_current = player->GetRatingBonusValue(CR_HIT_RANGED);
|
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||||
|
hit_current += player->GetRatingBonusValue(CR_HIT_RANGED);
|
||||||
hit_overflow = RANGED_HIT_OVERFLOW;
|
hit_overflow = RANGED_HIT_OVERFLOW;
|
||||||
|
if (hit_overflow > hit_current)
|
||||||
|
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_RANGED);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
}
|
}
|
||||||
if (hit_current >= hit_overflow)
|
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints);
|
||||||
stats_weights_[STATS_TYPE_HIT] = 0.0f;
|
|
||||||
else if (hit_current >= hit_overflow * 0.8)
|
|
||||||
stats_weights_[STATS_TYPE_HIT] /= 1.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if (type_ == CollectorType::MELEE)
|
if (type_ == CollectorType::MELEE)
|
||||||
{
|
{
|
||||||
float expertise_current, expertise_overflow;
|
float expertise_current, expertise_overflow;
|
||||||
expertise_current = player->GetRatingBonusValue(CR_EXPERTISE);
|
expertise_current = player->GetUInt32Value(PLAYER_EXPERTISE);
|
||||||
|
expertise_current += player->GetRatingBonusValue(CR_EXPERTISE);
|
||||||
expertise_overflow = EXPERTISE_OVERFLOW;
|
expertise_overflow = EXPERTISE_OVERFLOW;
|
||||||
if (expertise_current >= expertise_overflow)
|
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] = 0.0f;
|
float validPoints;
|
||||||
else if (expertise_current >= expertise_overflow * 0.8)
|
if (expertise_overflow > expertise_current)
|
||||||
stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5;
|
validPoints = (expertise_overflow - expertise_current) / player->GetRatingMultiplier(CR_EXPERTISE);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
|
|
||||||
|
collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,10 +599,14 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
|||||||
float defense_current, defense_overflow;
|
float defense_current, defense_overflow;
|
||||||
defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL);
|
defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL);
|
||||||
defense_overflow = DEFENSE_OVERFLOW;
|
defense_overflow = DEFENSE_OVERFLOW;
|
||||||
if (defense_current >= defense_overflow)
|
|
||||||
stats_weights_[STATS_TYPE_DEFENSE] /= 2;
|
float validPoints;
|
||||||
else if (defense_current >= defense_overflow * 0.8)
|
if (defense_overflow > defense_current)
|
||||||
stats_weights_[STATS_TYPE_DEFENSE] /= 1.5;
|
validPoints = (defense_overflow - defense_current) / player->GetRatingMultiplier(CR_DEFENSE_SKILL);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
|
|
||||||
|
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,10 +616,27 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
|||||||
float armor_penetration_current, armor_penetration_overflow;
|
float armor_penetration_current, armor_penetration_overflow;
|
||||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||||
armor_penetration_overflow = ARMOR_PENETRATION_OVERFLOW;
|
armor_penetration_overflow = ARMOR_PENETRATION_OVERFLOW;
|
||||||
if (armor_penetration_current >= armor_penetration_overflow)
|
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] = 0.0f;
|
float validPoints;
|
||||||
if (armor_penetration_current >= armor_penetration_overflow * 0.8)
|
if (armor_penetration_overflow > armor_penetration_current)
|
||||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] /= 1.5;
|
validPoints = (armor_penetration_overflow - armor_penetration_current) / player->GetRatingMultiplier(CR_ARMOR_PENETRATION);
|
||||||
|
else
|
||||||
|
validPoints = 0;
|
||||||
|
|
||||||
|
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED)
|
||||||
|
{
|
||||||
|
float armor_penetration_current, armor_penetration_overflow;
|
||||||
|
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||||
|
if (armor_penetration_current > 50)
|
||||||
|
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
enum StatsOverflowThreshold
|
enum StatsOverflowThreshold
|
||||||
{
|
{
|
||||||
SPELL_HIT_OVERFLOW = 17,
|
SPELL_HIT_OVERFLOW = 14,
|
||||||
MELEE_HIT_OVERFLOW = 8,
|
MELEE_HIT_OVERFLOW = 8,
|
||||||
RANGED_HIT_OVERFLOW = 8,
|
RANGED_HIT_OVERFLOW = 8,
|
||||||
EXPERTISE_OVERFLOW = 26,
|
EXPERTISE_OVERFLOW = 26,
|
||||||
@ -48,10 +48,12 @@ private:
|
|||||||
bool NotBestArmorType(uint32 item_subclass_armor);
|
bool NotBestArmorType(uint32 item_subclass_armor);
|
||||||
|
|
||||||
void ApplyOverflowPenalty(Player* player);
|
void ApplyOverflowPenalty(Player* player);
|
||||||
|
void ApplyWeightFinetune(Player* player);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Player* player_;
|
Player* player_;
|
||||||
CollectorType type_;
|
CollectorType type_;
|
||||||
|
CollectorType hitOverflowType_;
|
||||||
std::unique_ptr<StatsCollector> collector_;
|
std::unique_ptr<StatsCollector> collector_;
|
||||||
uint8 cls;
|
uint8 cls;
|
||||||
int tab;
|
int tab;
|
||||||
|
|||||||
@ -24,6 +24,8 @@
|
|||||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||||
#include "raids/moltencore/RaidMcActionContext.h"
|
#include "raids/moltencore/RaidMcActionContext.h"
|
||||||
#include "raids/moltencore/RaidMcTriggerContext.h"
|
#include "raids/moltencore/RaidMcTriggerContext.h"
|
||||||
|
#include "raids/aq20/RaidAq20ActionContext.h"
|
||||||
|
#include "raids/aq20/RaidAq20TriggerContext.h"
|
||||||
|
|
||||||
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||||
{
|
{
|
||||||
@ -40,6 +42,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
|||||||
actionContexts.Add(new RaidNaxxActionContext());
|
actionContexts.Add(new RaidNaxxActionContext());
|
||||||
actionContexts.Add(new RaidUlduarActionContext());
|
actionContexts.Add(new RaidUlduarActionContext());
|
||||||
actionContexts.Add(new RaidMcActionContext());
|
actionContexts.Add(new RaidMcActionContext());
|
||||||
|
actionContexts.Add(new RaidAq20ActionContext());
|
||||||
|
|
||||||
triggerContexts.Add(new TriggerContext());
|
triggerContexts.Add(new TriggerContext());
|
||||||
triggerContexts.Add(new ChatTriggerContext());
|
triggerContexts.Add(new ChatTriggerContext());
|
||||||
@ -48,6 +51,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
|||||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||||
triggerContexts.Add(new RaidMcTriggerContext());
|
triggerContexts.Add(new RaidMcTriggerContext());
|
||||||
|
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||||
|
|
||||||
valueContexts.Add(new ValueContext());
|
valueContexts.Add(new ValueContext());
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,7 @@ private:
|
|||||||
{
|
{
|
||||||
return new ActionNode("mana potion",
|
return new ActionNode("mana potion",
|
||||||
/*P*/ nullptr,
|
/*P*/ nullptr,
|
||||||
/*A*/ NextAction::array(0, new NextAction("drink"), nullptr),
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,7 +51,7 @@ bool AcceptQuestAction::Execute(Event event)
|
|||||||
guid = unit->GetGUID().GetRawValue();
|
guid = unit->GetGUID().GetRawValue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (unit && text == "*" && sqrt(bot->GetDistance(unit)) <= INTERACTION_DISTANCE)
|
if (unit && text == "*" && bot->GetDistance(unit) <= INTERACTION_DISTANCE)
|
||||||
hasAccept |= QuestAction::ProcessQuests(unit);
|
hasAccept |= QuestAction::ProcessQuests(unit);
|
||||||
}
|
}
|
||||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
@ -63,7 +63,7 @@ bool AcceptQuestAction::Execute(Event event)
|
|||||||
guid = go->GetGUID().GetRawValue();
|
guid = go->GetGUID().GetRawValue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (go && text == "*" && sqrt(bot->GetDistance(go)) <= INTERACTION_DISTANCE)
|
if (go && text == "*" && bot->GetDistance(go) <= INTERACTION_DISTANCE)
|
||||||
hasAccept |= QuestAction::ProcessQuests(go);
|
hasAccept |= QuestAction::ProcessQuests(go);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,7 +172,7 @@ public:
|
|||||||
creators["rtsc"] = &ChatActionContext::rtsc;
|
creators["rtsc"] = &ChatActionContext::rtsc;
|
||||||
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
|
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
|
||||||
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
||||||
creators["tell expected dps"] = &ChatActionContext::tell_expected_dps;
|
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
|
||||||
creators["join"] = &ChatActionContext::join;
|
creators["join"] = &ChatActionContext::join;
|
||||||
creators["calc"] = &ChatActionContext::calc;
|
creators["calc"] = &ChatActionContext::calc;
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ private:
|
|||||||
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
|
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
|
||||||
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
|
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
|
||||||
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
|
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
|
||||||
static Action* tell_expected_dps(PlayerbotAI* ai) { return new TellExpectedDpsAction(ai); }
|
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
|
||||||
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
||||||
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
|
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
|
|||||||
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
Player* master = botAI->GetGroupMaster();
|
Player* gmaster = botAI->GetGroupMaster();
|
||||||
Player* realMaster = botAI->GetMaster();
|
Player* realMaster = botAI->GetMaster();
|
||||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
|
|
||||||
@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!master || bot == master)
|
if (!gmaster || bot == gmaster)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (bot->GetDistance(master) > sPlayerbotAIConfig->rpgDistance * 2)
|
if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||||
float distance = master->GetDistance2d(pos.getX(), pos.getY());
|
float distance = gmaster->GetDistance2d(pos.getX(), pos.getY());
|
||||||
|
|
||||||
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
||||||
{
|
{
|
||||||
Player* player = master;
|
Player* player = gmaster;
|
||||||
if (!master->isMoving() ||
|
if (gmaster && !gmaster->isMoving() ||
|
||||||
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((inDungeon || !master->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == master && distance > 5.0f)
|
if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!master->isMoving() && distance < 25.0f)
|
if (!gmaster->isMoving() && distance < 25.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (distance < formation->GetMaxDistance())
|
if (distance < formation->GetMaxDistance())
|
||||||
|
|||||||
@ -162,8 +162,14 @@ bool CastMeleeDebuffSpellAction::isUseful()
|
|||||||
|
|
||||||
bool CastAuraSpellAction::isUseful()
|
bool CastAuraSpellAction::isUseful()
|
||||||
{
|
{
|
||||||
return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() &&
|
if (!GetTarget() || !CastSpellAction::isUseful())
|
||||||
!botAI->HasAura(spell, GetTarget(), false, isOwner);
|
return false;
|
||||||
|
Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration);
|
||||||
|
if (!aura)
|
||||||
|
return true;
|
||||||
|
if (beforeDuration && aura->GetDuration() < beforeDuration)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell)
|
CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
@ -251,8 +257,8 @@ Value<Unit*>* CastDebuffSpellOnMeleeAttackerAction::GetTargetValue()
|
|||||||
return context->GetValue<Unit*>("melee attacker without aura", spell);
|
return context->GetValue<Unit*>("melee attacker without aura", spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner)
|
CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner, uint32 beforeDuration)
|
||||||
: CastAuraSpellAction(botAI, spell, checkIsOwner)
|
: CastAuraSpellAction(botAI, spell, checkIsOwner, false, beforeDuration)
|
||||||
{
|
{
|
||||||
range = botAI->GetRange("spell");
|
range = botAI->GetRange("spell");
|
||||||
}
|
}
|
||||||
@ -364,5 +370,5 @@ bool CastDebuffSpellAction::isUseful()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return CastAuraSpellAction::isUseful() &&
|
return CastAuraSpellAction::isUseful() &&
|
||||||
(target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime;
|
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
||||||
}
|
}
|
||||||
@ -37,16 +37,20 @@ protected:
|
|||||||
class CastAuraSpellAction : public CastSpellAction
|
class CastAuraSpellAction : public CastSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false)
|
CastAuraSpellAction(PlayerbotAI* botAI, std::string const spell, bool isOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
|
||||||
: CastSpellAction(botAI, spell)
|
: CastSpellAction(botAI, spell)
|
||||||
{
|
{
|
||||||
this->isOwner = isOwner;
|
this->isOwner = isOwner;
|
||||||
|
this->beforeDuration = beforeDuration;
|
||||||
|
this->checkDuration = checkDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isOwner;
|
bool isOwner;
|
||||||
|
bool checkDuration;
|
||||||
|
uint32 beforeDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastMeleeSpellAction : public CastSpellAction
|
class CastMeleeSpellAction : public CastSpellAction
|
||||||
@ -107,7 +111,7 @@ public:
|
|||||||
class CastBuffSpellAction : public CastAuraSpellAction
|
class CastBuffSpellAction : public CastAuraSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false);
|
CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = false, uint32 beforeDuration = 0);
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "self target"; }
|
std::string const GetTargetName() override { return "self target"; }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,13 +11,13 @@
|
|||||||
std::map<uint32, SkillLineAbilityEntry const*> ListSpellsAction::skillSpells;
|
std::map<uint32, SkillLineAbilityEntry const*> ListSpellsAction::skillSpells;
|
||||||
std::set<uint32> ListSpellsAction::vendorItems;
|
std::set<uint32> ListSpellsAction::vendorItems;
|
||||||
|
|
||||||
bool CompareSpells(std::pair<uint32, std::string>& s1, std::pair<uint32, std::string>& s2)
|
bool CompareSpells(const std::pair<uint32, std::string>& s1, const std::pair<uint32, std::string>& s2)
|
||||||
{
|
{
|
||||||
SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first);
|
SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first);
|
||||||
SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first);
|
SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first);
|
||||||
if (!si1 || !si2)
|
if (!si1 || !si2)
|
||||||
{
|
{
|
||||||
LOG_ERROR("playerbots", "SpellInfo missing.");
|
LOG_ERROR("playerbots", "SpellInfo missing. {} {}", s1.first, s2.first);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32 p1 = si1->SchoolMask * 20000;
|
uint32 p1 = si1->SchoolMask * 20000;
|
||||||
@ -54,7 +54,7 @@ bool CompareSpells(std::pair<uint32, std::string>& s1, std::pair<uint32, std::st
|
|||||||
|
|
||||||
if (p1 == p2)
|
if (p1 == p2)
|
||||||
{
|
{
|
||||||
return strcmp(si1->SpellName[0], si1->SpellName[1]) > 0;
|
return strcmp(si1->SpellName[0], si2->SpellName[0]) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p1 > p2;
|
return p1 > p2;
|
||||||
@ -274,6 +274,10 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
if (out.str().empty())
|
if (out.str().empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (itr->first == 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("playerbots", "?! {}", itr->first);
|
||||||
|
}
|
||||||
spells.push_back(std::pair<uint32, std::string>(itr->first, out.str()));
|
spells.push_back(std::pair<uint32, std::string>(itr->first, out.str()));
|
||||||
alreadySeenList += spellInfo->SpellName[0];
|
alreadySeenList += spellInfo->SpellName[0];
|
||||||
alreadySeenList += ",";
|
alreadySeenList += ",";
|
||||||
|
|||||||
@ -762,7 +762,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
// return true;
|
// return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovementAction::MoveTo(Unit* target, float distance, MovementPriority priority)
|
bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriority priority)
|
||||||
{
|
{
|
||||||
if (!IsMovingAllowed(target))
|
if (!IsMovingAllowed(target))
|
||||||
return false;
|
return false;
|
||||||
@ -814,39 +814,25 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
|
|||||||
float combatDistance = bot->GetCombatReach() + target->GetCombatReach();
|
float combatDistance = bot->GetCombatReach() + target->GetCombatReach();
|
||||||
distance += combatDistance;
|
distance += combatDistance;
|
||||||
|
|
||||||
if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD)) // target is moving forward, predict the position
|
|
||||||
{
|
|
||||||
float needToGo = bot->GetExactDist(target) - distance;
|
|
||||||
float timeToGo = MoveDelay(abs(needToGo)) + sPlayerbotAIConfig->reactDelay / 1000.0f;
|
|
||||||
float targetMoveDist = timeToGo * target->GetSpeed(MOVE_RUN);
|
|
||||||
targetMoveDist = std::min(5.0f, targetMoveDist);
|
|
||||||
tx += targetMoveDist * cos(target->GetOrientation());
|
|
||||||
ty += targetMoveDist * sin(target->GetOrientation());
|
|
||||||
if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(),
|
|
||||||
target->GetPositionZ(), tx, ty, tz))
|
|
||||||
{
|
|
||||||
// disable prediction if position is invalid
|
|
||||||
tx = target->GetPositionX();
|
|
||||||
ty = target->GetPositionY();
|
|
||||||
tz = target->GetPositionZ();
|
|
||||||
}
|
|
||||||
// Prediction may cause this, which makes ShortenPathUntilDist fail
|
|
||||||
if (bot->GetExactDist(tx, ty, tz) <= distance)
|
|
||||||
{
|
|
||||||
tx = target->GetPositionX();
|
|
||||||
ty = target->GetPositionY();
|
|
||||||
tz = target->GetPositionZ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bot->GetExactDist(tx, ty, tz) <= distance)
|
if (bot->GetExactDist(tx, ty, tz) <= distance)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PathGenerator path(bot);
|
PathGenerator path(bot);
|
||||||
path.CalculatePath(tx, ty, tz, false);
|
path.CalculatePath(tx, ty, tz, false);
|
||||||
PathType type = path.GetPathType();
|
PathType type = path.GetPathType();
|
||||||
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
|
int typeOk = PATHFIND_NORMAL | PATHFIND_INCOMPLETE;
|
||||||
if (!(type & typeOk))
|
if (!(type & typeOk))
|
||||||
return false;
|
return false;
|
||||||
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), distance);
|
float shortenTo = distance;
|
||||||
|
|
||||||
|
// Avoid walking too far when moving towards each other
|
||||||
|
if (bot->GetDistance(tx, ty, tz) >= 10.0f)
|
||||||
|
shortenTo = std::max(distance, bot->GetDistance(tx, ty, tz) / 2);
|
||||||
|
|
||||||
|
if (bot->GetExactDist(tx, ty, tz) <= shortenTo)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
path.ShortenPathUntilDist(G3D::Vector3(tx, ty, tz), shortenTo);
|
||||||
G3D::Vector3 endPos = path.GetPath().back();
|
G3D::Vector3 endPos = path.GetPath().back();
|
||||||
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false,
|
return MoveTo(target->GetMapId(), endPos.x, endPos.y, endPos.z, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT);
|
MovementPriority::MOVEMENT_COMBAT);
|
||||||
@ -874,7 +860,7 @@ float MovementAction::GetFollowAngle()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovementAction::IsMovingAllowed(Unit* target)
|
bool MovementAction::IsMovingAllowed(WorldObject* target)
|
||||||
{
|
{
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
@ -1272,6 +1258,8 @@ bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle)
|
|||||||
|
|
||||||
// bot->GetMotionMaster()->Clear();
|
// bot->GetMotionMaster()->Clear();
|
||||||
bot->GetMotionMaster()->MoveChase((Unit*)obj, distance);
|
bot->GetMotionMaster()->MoveChase((Unit*)obj, distance);
|
||||||
|
|
||||||
|
// TODO shouldnt this use "last movement" value?
|
||||||
WaitForReach(bot->GetExactDist2d(obj) - distance);
|
WaitForReach(bot->GetExactDist2d(obj) - distance);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1295,6 +1283,7 @@ float MovementAction::MoveDelay(float distance)
|
|||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO should this be removed? (or modified to use "last movement" value?)
|
||||||
void MovementAction::WaitForReach(float distance)
|
void MovementAction::WaitForReach(float distance)
|
||||||
{
|
{
|
||||||
float delay = 1000.0f * MoveDelay(distance);
|
float delay = 1000.0f * MoveDelay(distance);
|
||||||
@ -1313,6 +1302,15 @@ void MovementAction::WaitForReach(float distance)
|
|||||||
botAI->SetNextCheckDelay((uint32)delay);
|
botAI->SetNextCheckDelay((uint32)delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// similiar to botAI->SetNextCheckDelay() but only stops movement
|
||||||
|
void MovementAction::SetNextMovementDelay(float delayMillis)
|
||||||
|
{
|
||||||
|
AI_VALUE(LastMovement&, "last movement")
|
||||||
|
.Set(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetOrientation(),
|
||||||
|
delayMillis,
|
||||||
|
MovementPriority::MOVEMENT_FORCED);
|
||||||
|
}
|
||||||
|
|
||||||
bool MovementAction::Flee(Unit* target)
|
bool MovementAction::Flee(Unit* target)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
@ -1821,12 +1819,17 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
DynamicObject* dynOwner = aura->GetDynobjOwner();
|
DynamicObject* dynOwner = aura->GetDynobjOwner();
|
||||||
if (!dynOwner || !dynOwner->IsInWorld())
|
if (!dynOwner || !dynOwner->IsInWorld())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float radius = dynOwner->GetRadius();
|
float radius = dynOwner->GetRadius();
|
||||||
|
if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius)
|
||||||
|
return false;
|
||||||
if (bot->GetDistance(dynOwner) > radius)
|
if (bot->GetDistance(dynOwner) > radius)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -1840,7 +1843,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
|||||||
lastTellTimer = time(NULL);
|
lastTellTimer = time(NULL);
|
||||||
lastMoveTimer = getMSTime();
|
lastMoveTimer = getMSTime();
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "I'm avoiding " << name.str() << "...";
|
out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius << " - [Aura]";
|
||||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1871,17 +1874,28 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// 0 trap with no despawn after cast. 1 trap despawns after cast. 2 bomb casts on spawn.
|
||||||
|
if (goInfo->trap.type != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
uint32 spellId = goInfo->trap.spellId;
|
uint32 spellId = goInfo->trap.spellId;
|
||||||
if (!spellId)
|
if (!spellId)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellId) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
if (!spellInfo || spellInfo->IsPositive())
|
if (!spellInfo || spellInfo->IsPositive())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float radius = (float)goInfo->trap.diameter / 2;
|
float radius = (float)goInfo->trap.diameter / 2;
|
||||||
|
if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius)
|
||||||
|
continue;
|
||||||
// for (int i = 0; i < MAX_SPELL_EFFECTS; i++) {
|
// for (int i = 0; i < MAX_SPELL_EFFECTS; i++) {
|
||||||
// if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) {
|
// if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) {
|
||||||
// if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) {
|
// if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) {
|
||||||
@ -1905,7 +1919,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
|
|||||||
lastTellTimer = time(NULL);
|
lastTellTimer = time(NULL);
|
||||||
lastMoveTimer = getMSTime();
|
lastMoveTimer = getMSTime();
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "I'm avoiding " << name.str() << "...";
|
out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius << " - [Trap]";
|
||||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1948,6 +1962,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
|||||||
sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell);
|
sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell);
|
||||||
if (!triggerSpellInfo)
|
if (!triggerSpellInfo)
|
||||||
continue;
|
continue;
|
||||||
|
if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end())
|
||||||
|
return false;
|
||||||
for (int j = 0; j < MAX_SPELL_EFFECTS; j++)
|
for (int j = 0; j < MAX_SPELL_EFFECTS; j++)
|
||||||
{
|
{
|
||||||
if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
|
if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
|
||||||
@ -1957,6 +1973,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius)
|
||||||
|
continue;
|
||||||
std::ostringstream name;
|
std::ostringstream name;
|
||||||
name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)";
|
name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)";
|
||||||
if (FleePosition(unit->GetPosition(), radius))
|
if (FleePosition(unit->GetPosition(), radius))
|
||||||
@ -1966,7 +1984,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
|
|||||||
lastTellTimer = time(NULL);
|
lastTellTimer = time(NULL);
|
||||||
lastMoveTimer = getMSTime();
|
lastMoveTimer = getMSTime();
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "I'm avoiding " << name.str() << "...";
|
out << "I'm avoiding " << name.str() << " (" << triggerSpellInfo->Id << ")" << " Radius " << radius << " - [Unit Trigger]";
|
||||||
bot->Say(out.str(), LANG_UNIVERSAL);
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ protected:
|
|||||||
bool MoveToLOS(WorldObject* target, bool ranged = false);
|
bool MoveToLOS(WorldObject* target, bool ranged = false);
|
||||||
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
|
bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false,
|
||||||
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||||
bool MoveTo(Unit* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||||
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||||
float GetFollowAngle();
|
float GetFollowAngle();
|
||||||
bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance);
|
bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance);
|
||||||
@ -39,7 +39,8 @@ protected:
|
|||||||
bool ReachCombatTo(Unit* target, float distance = 0.0f);
|
bool ReachCombatTo(Unit* target, float distance = 0.0f);
|
||||||
float MoveDelay(float distance);
|
float MoveDelay(float distance);
|
||||||
void WaitForReach(float distance);
|
void WaitForReach(float distance);
|
||||||
bool IsMovingAllowed(Unit* target);
|
void SetNextMovementDelay(float delayMillis);
|
||||||
|
bool IsMovingAllowed(WorldObject* target);
|
||||||
bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
|
bool IsMovingAllowed(uint32 mapId, float x, float y, float z);
|
||||||
bool IsDuplicateMove(uint32 mapId, float x, float y, float z);
|
bool IsDuplicateMove(uint32 mapId, float x, float y, float z);
|
||||||
bool IsWaitingForLastMove(MovementPriority priority);
|
bool IsWaitingForLastMove(MovementPriority priority);
|
||||||
|
|||||||
@ -205,21 +205,20 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable since ExtractAllItemIds bad performance
|
//toxic links
|
||||||
// //toxic links
|
if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix)
|
||||||
// if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix)
|
&& (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0))
|
||||||
// && (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0))
|
{
|
||||||
// {
|
HandleToxicLinksReply(bot, chatChannelSource, msg, name);
|
||||||
// HandleToxicLinksReply(bot, chatChannelSource, msg, name);
|
return;
|
||||||
// return;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// //thunderfury
|
//thunderfury
|
||||||
// if (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).count(19019))
|
if (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).count(19019))
|
||||||
// {
|
{
|
||||||
// HandleThunderfuryReply(bot, chatChannelSource, msg, name);
|
HandleThunderfuryReply(bot, chatChannelSource, msg, name);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
auto messageRepy = GenerateReplyMessage(bot, msg, guid1, name);
|
auto messageRepy = GenerateReplyMessage(bot, msg, guid1, name);
|
||||||
SendGeneralResponse(bot, chatChannelSource, messageRepy, name);
|
SendGeneralResponse(bot, chatChannelSource, messageRepy, name);
|
||||||
@ -245,6 +244,8 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat
|
|||||||
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL);
|
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
||||||
@ -309,6 +310,8 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC
|
|||||||
GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders));
|
GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
||||||
@ -317,8 +320,6 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC
|
|||||||
}
|
}
|
||||||
bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||||
{
|
{
|
||||||
// Disable since ExtractAllItemIds bad performance
|
|
||||||
return false;
|
|
||||||
auto messageItemIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg);
|
auto messageItemIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg);
|
||||||
|
|
||||||
if (messageItemIds.empty())
|
if (messageItemIds.empty())
|
||||||
@ -404,6 +405,8 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
||||||
}
|
}
|
||||||
@ -412,8 +415,6 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha
|
|||||||
}
|
}
|
||||||
bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||||
{
|
{
|
||||||
// Disable since ExtractAllQuestIds bad performance
|
|
||||||
return false;
|
|
||||||
auto messageQuestIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg);
|
auto messageQuestIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg);
|
||||||
|
|
||||||
if (messageQuestIds.empty())
|
if (messageQuestIds.empty())
|
||||||
@ -490,6 +491,8 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh
|
|||||||
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
||||||
}
|
}
|
||||||
@ -510,8 +513,10 @@ bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatCha
|
|||||||
}
|
}
|
||||||
case ChatChannelSource::SRC_GENERAL:
|
case ChatChannelSource::SRC_GENERAL:
|
||||||
{
|
{
|
||||||
//may reply to the same channel or whisper
|
//may reply to the same channel 80% or whisper
|
||||||
|
if (urand(0, 100) < 80)
|
||||||
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL);
|
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL);
|
||||||
|
else
|
||||||
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "QuestDef.h"
|
#include "QuestDef.h"
|
||||||
|
#include "StatsWeightCalculator.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include "BroadcastHelper.h"
|
#include "BroadcastHelper.h"
|
||||||
|
|
||||||
@ -176,8 +177,20 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques
|
|||||||
bestIds = BestRewards(quest);
|
bestIds = BestRewards(quest);
|
||||||
if (!bestIds.empty())
|
if (!bestIds.empty())
|
||||||
{
|
{
|
||||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[*bestIds.begin()]);
|
StatsWeightCalculator calc(bot);
|
||||||
bot->RewardQuest(quest, *bestIds.begin(), questGiver, true);
|
uint32 best = 0;
|
||||||
|
float bestScore = 0;
|
||||||
|
for (uint32 id : bestIds)
|
||||||
|
{
|
||||||
|
float score = calc.CalculateItem(quest->RewardChoiceItemId[id]);
|
||||||
|
if (score > bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
best = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[best]);
|
||||||
|
bot->RewardQuest(quest, best, questGiver, true);
|
||||||
out << "Rewarded " << ChatHelper::FormatItem(item);
|
out << "Rewarded " << ChatHelper::FormatItem(item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -130,10 +130,10 @@ bool TellAuraAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TellExpectedDpsAction::Execute(Event event)
|
bool TellEstimatedDpsAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
float dps = AI_VALUE(float, "expected group dps");
|
float dps = AI_VALUE(float, "estimated group dps");
|
||||||
botAI->TellMaster("Expected Group DPS: " + std::to_string(dps));
|
botAI->TellMaster("Estimated Group DPS: " + std::to_string(dps));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,10 +30,10 @@ public:
|
|||||||
virtual bool Execute(Event event);
|
virtual bool Execute(Event event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TellExpectedDpsAction : public Action
|
class TellEstimatedDpsAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TellExpectedDpsAction(PlayerbotAI* ai) : Action(ai, "tell expected dps") {}
|
TellEstimatedDpsAction(PlayerbotAI* ai) : Action(ai, "tell estimated dps") {}
|
||||||
|
|
||||||
virtual bool Execute(Event event);
|
virtual bool Execute(Event event);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,13 +29,14 @@ bool TellAttackersAction::Execute(Event event)
|
|||||||
botAI->TellMaster("--- Attackers ---");
|
botAI->TellMaster("--- Attackers ---");
|
||||||
|
|
||||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||||
|
int32 count = 0;
|
||||||
for (ObjectGuid const guid : attackers)
|
for (ObjectGuid const guid : attackers)
|
||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
if (!unit || !unit->IsAlive())
|
if (!unit || !unit->IsAlive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
botAI->TellMaster(unit->GetName());
|
botAI->TellMaster(std::to_string(++count) + std::string(".") + unit->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
botAI->TellMaster("--- Threat ---");
|
botAI->TellMaster("--- Threat ---");
|
||||||
|
|||||||
@ -165,16 +165,10 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
|||||||
|
|
||||||
bool SummonAction::Teleport(Player* summoner, Player* player)
|
bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
// Player* master = GetMaster();
|
||||||
// if (master->GetMap() && master->GetMap()->IsDungeon()) {
|
if (!summoner)
|
||||||
// InstanceMap* map = master->GetMap()->ToInstanceMap();
|
return false;
|
||||||
// if (map) {
|
|
||||||
// if (map->CannotEnter(player, true) == Map::CANNOT_ENTER_MAX_PLAYERS) {
|
|
||||||
// botAI->TellError("I can not enter this dungeon");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if (player->GetVehicle())
|
if (player->GetVehicle())
|
||||||
{
|
{
|
||||||
botAI->TellError("You cannot summon me while I'm on a vehicle");
|
botAI->TellError("You cannot summon me while I'm on a vehicle");
|
||||||
@ -197,13 +191,13 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
|||||||
->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on
|
->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on
|
||||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||||
|
|
||||||
if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat)
|
if (summoner->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat)
|
||||||
{
|
{
|
||||||
botAI->TellError("You cannot summon me while you're in combat");
|
botAI->TellError("You cannot summon me while you're in combat");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead)
|
if (!summoner->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead)
|
||||||
{
|
{
|
||||||
botAI->TellError("You cannot summon me while you're dead");
|
botAI->TellError("You cannot summon me while you're dead");
|
||||||
return false;
|
return false;
|
||||||
@ -218,7 +212,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
|||||||
|
|
||||||
bool revive =
|
bool revive =
|
||||||
sPlayerbotAIConfig->reviveBotWhenSummoned == 2 ||
|
sPlayerbotAIConfig->reviveBotWhenSummoned == 2 ||
|
||||||
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive());
|
(sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive());
|
||||||
|
|
||||||
if (bot->isDead() && revive)
|
if (bot->isDead() && revive)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -100,9 +100,12 @@ void BloodDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr)));
|
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 5),
|
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 4),
|
||||||
new NextAction("vampiric blood", ACTION_HIGH + 4),
|
|
||||||
new NextAction("death strike", ACTION_HIGH + 3), nullptr)));
|
new NextAction("death strike", ACTION_HIGH + 3), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("army of the dead", NextAction::array(0, new NextAction("army of the dead",
|
triggers.push_back(
|
||||||
// ACTION_HIGH + 6), nullptr)));
|
new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -165,7 +165,7 @@ public:
|
|||||||
class CastGhoulFrenzyAction : public CastBuffSpellAction
|
class CastGhoulFrenzyAction : public CastBuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy") {}
|
CastGhoulFrenzyAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "ghoul frenzy", false, 5000) {}
|
||||||
std::string const GetTargetName() override { return "pet target"; }
|
std::string const GetTargetName() override { return "pet target"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ class CastDeathAndDecayAction : public CastSpellAction
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {}
|
CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") {}
|
||||||
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
// ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastHornOfWinterAction : public CastSpellAction
|
class CastHornOfWinterAction : public CastSpellAction
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "DKTriggers.h"
|
#include "DKTriggers.h"
|
||||||
#include "FrostDKStrategy.h"
|
#include "FrostDKStrategy.h"
|
||||||
#include "GenericDKNonCombatStrategy.h"
|
#include "GenericDKNonCombatStrategy.h"
|
||||||
|
#include "GenericTriggers.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PullStrategy.h"
|
#include "PullStrategy.h"
|
||||||
#include "UnholyDKStrategy.h"
|
#include "UnholyDKStrategy.h"
|
||||||
@ -73,10 +74,14 @@ public:
|
|||||||
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
|
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
|
||||||
creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker;
|
creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker;
|
||||||
creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch;
|
creators["icy touch"] = &DeathKnightTriggerFactoryInternal::icy_touch;
|
||||||
|
creators["icy touch 8s"] = &DeathKnightTriggerFactoryInternal::icy_touch_8s;
|
||||||
|
creators["dd cd and icy touch 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_icy_touch_8s;
|
||||||
creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil;
|
creators["death coil"] = &DeathKnightTriggerFactoryInternal::death_coil;
|
||||||
creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker;
|
creators["icy touch on attacker"] = &DeathKnightTriggerFactoryInternal::icy_touch_on_attacker;
|
||||||
creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons;
|
creators["improved icy talons"] = &DeathKnightTriggerFactoryInternal::improved_icy_talons;
|
||||||
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
|
creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike;
|
||||||
|
creators["plague strike 8s"] = &DeathKnightTriggerFactoryInternal::plague_strike_8s;
|
||||||
|
creators["dd cd and plague strike 8s"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_plague_strike_8s;
|
||||||
creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter;
|
creators["horn of winter"] = &DeathKnightTriggerFactoryInternal::horn_of_winter;
|
||||||
creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze;
|
creators["mind freeze"] = &DeathKnightTriggerFactoryInternal::mind_freeze;
|
||||||
creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer;
|
creators["mind freeze on enemy healer"] = &DeathKnightTriggerFactoryInternal::mind_freeze_on_enemy_healer;
|
||||||
@ -87,8 +92,11 @@ public:
|
|||||||
creators["chains of ice"] = &DeathKnightTriggerFactoryInternal::chains_of_ice;
|
creators["chains of ice"] = &DeathKnightTriggerFactoryInternal::chains_of_ice;
|
||||||
creators["unbreakable armor"] = &DeathKnightTriggerFactoryInternal::unbreakable_armor;
|
creators["unbreakable armor"] = &DeathKnightTriggerFactoryInternal::unbreakable_armor;
|
||||||
creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune;
|
creators["high blood rune"] = &DeathKnightTriggerFactoryInternal::high_blood_rune;
|
||||||
|
creators["high frost rune"] = &DeathKnightTriggerFactoryInternal::high_frost_rune;
|
||||||
|
creators["high unholy rune"] = &DeathKnightTriggerFactoryInternal::high_unholy_rune;
|
||||||
creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog;
|
creators["freezing fog"] = &DeathKnightTriggerFactoryInternal::freezing_fog;
|
||||||
creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation;
|
creators["no desolation"] = &DeathKnightTriggerFactoryInternal::no_desolation;
|
||||||
|
creators["dd cd and no desolation"] = &DeathKnightTriggerFactoryInternal::dd_cd_and_no_desolation;
|
||||||
creators["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown;
|
creators["death and decay cooldown"] = &DeathKnightTriggerFactoryInternal::death_and_decay_cooldown;
|
||||||
creators["army of the dead"] = &DeathKnightTriggerFactoryInternal::army_of_the_dead;
|
creators["army of the dead"] = &DeathKnightTriggerFactoryInternal::army_of_the_dead;
|
||||||
}
|
}
|
||||||
@ -98,11 +106,15 @@ private:
|
|||||||
static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); }
|
static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); }
|
||||||
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
|
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
|
||||||
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
|
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
|
||||||
|
static Trigger* plague_strike_8s(PlayerbotAI* botAI) { return new PlagueStrike8sDebuffTrigger(botAI); }
|
||||||
|
static Trigger* dd_cd_and_plague_strike_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 8s"); }
|
||||||
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI)
|
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new PlagueStrikeDebuffOnAttackerTrigger(botAI);
|
return new PlagueStrikeDebuffOnAttackerTrigger(botAI);
|
||||||
}
|
}
|
||||||
static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); }
|
static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); }
|
||||||
|
static Trigger* icy_touch_8s(PlayerbotAI* botAI) { return new IcyTouch8sDebuffTrigger(botAI); }
|
||||||
|
static Trigger* dd_cd_and_icy_touch_8s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 8s"); }
|
||||||
static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); }
|
static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); }
|
||||||
static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); }
|
static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); }
|
||||||
static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); }
|
static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); }
|
||||||
@ -122,8 +134,11 @@ private:
|
|||||||
static Trigger* chains_of_ice(PlayerbotAI* botAI) { return new ChainsOfIceSnareTrigger(botAI); }
|
static Trigger* chains_of_ice(PlayerbotAI* botAI) { return new ChainsOfIceSnareTrigger(botAI); }
|
||||||
static Trigger* unbreakable_armor(PlayerbotAI* botAI) { return new UnbreakableArmorTrigger(botAI); }
|
static Trigger* unbreakable_armor(PlayerbotAI* botAI) { return new UnbreakableArmorTrigger(botAI); }
|
||||||
static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); }
|
static Trigger* high_blood_rune(PlayerbotAI* botAI) { return new HighBloodRuneTrigger(botAI); }
|
||||||
|
static Trigger* high_frost_rune(PlayerbotAI* botAI) { return new HighFrostRuneTrigger(botAI); }
|
||||||
|
static Trigger* high_unholy_rune(PlayerbotAI* botAI) { return new HighUnholyRuneTrigger(botAI); }
|
||||||
static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); }
|
static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); }
|
||||||
static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); }
|
static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); }
|
||||||
|
static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); }
|
||||||
static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); }
|
static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); }
|
||||||
static Trigger* army_of_the_dead(PlayerbotAI* botAI) { return new ArmyOfTheDeadTrigger(botAI); }
|
static Trigger* army_of_the_dead(PlayerbotAI* botAI) { return new ArmyOfTheDeadTrigger(botAI); }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -39,14 +39,21 @@ bool PestilenceGlyphTrigger::IsActive()
|
|||||||
|
|
||||||
bool HighBloodRuneTrigger::IsActive()
|
bool HighBloodRuneTrigger::IsActive()
|
||||||
{
|
{
|
||||||
// bot->Say(std::to_string(bot->GetBaseRune(0)) + "_" + std::to_string(bot->GetRuneCooldown(0)) + " " +
|
|
||||||
// std::to_string(bot->GetBaseRune(1)) + "_" + std::to_string(bot->GetRuneCooldown(1)), LANG_UNIVERSAL);
|
|
||||||
return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1);
|
return !bot->GetRuneCooldown(0) && !bot->GetRuneCooldown(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HighFrostRuneTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return !bot->GetRuneCooldown(2) && !bot->GetRuneCooldown(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HighUnholyRuneTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return !bot->GetRuneCooldown(4) && !bot->GetRuneCooldown(5);
|
||||||
|
}
|
||||||
bool DesolationTrigger::IsActive()
|
bool DesolationTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return bot->HasAura(66817) && !botAI->HasAura("desolation", GetTarget(), false, true, -1, true);
|
return bot->HasAura(66817) && BuffTrigger::IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeathAndDecayCooldownTrigger::IsActive()
|
bool DeathAndDecayCooldownTrigger::IsActive()
|
||||||
@ -55,5 +62,5 @@ bool DeathAndDecayCooldownTrigger::IsActive()
|
|||||||
if (!spellId)
|
if (!spellId)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return bot->HasSpellCooldown(spellId);
|
return bot->GetSpellCooldownDelay(spellId) >= 3000;
|
||||||
}
|
}
|
||||||
@ -17,14 +17,26 @@ BUFF_TRIGGER(ImprovedIcyTalonsTrigger, "improved icy talons");
|
|||||||
class PlagueStrikeDebuffTrigger : public DebuffTrigger
|
class PlagueStrikeDebuffTrigger : public DebuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", true, .0f) {}
|
PlagueStrikeDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PlagueStrike8sDebuffTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlagueStrike8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "blood plague", 1, true, .0f, 3000) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever");
|
// DEBUFF_CHECKISOWNER_TRIGGER(IcyTouchDebuffTrigger, "frost fever");
|
||||||
class IcyTouchDebuffTrigger : public DebuffTrigger
|
class IcyTouchDebuffTrigger : public DebuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", true, .0f) {}
|
IcyTouchDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IcyTouch8sDebuffTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IcyTouch8sDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "frost fever", 1, true, .0f, 3000) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor");
|
BUFF_TRIGGER(UnbreakableArmorTrigger, "unbreakable armor");
|
||||||
@ -139,6 +151,20 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HighFrostRuneTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighFrostRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high frost rune") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighUnholyRuneTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighUnholyRuneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high unholy rune") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class FreezingFogTrigger : public HasAuraTrigger
|
class FreezingFogTrigger : public HasAuraTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -148,7 +174,7 @@ public:
|
|||||||
class DesolationTrigger : public BuffTrigger
|
class DesolationTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation") {}
|
DesolationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "desolation", 1, false, true, 10000) {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public:
|
|||||||
// creators["icebound fortitude"] = &icebound_fortitude;
|
// creators["icebound fortitude"] = &icebound_fortitude;
|
||||||
// creators["mind freeze"] = &mind_freeze;
|
// creators["mind freeze"] = &mind_freeze;
|
||||||
// creators["hungering cold"] = &hungering_cold;
|
// creators["hungering cold"] = &hungering_cold;
|
||||||
// creators["unbreakable armor"] = &unbreakable_armor;
|
creators["unbreakable armor"] = &unbreakable_armor;
|
||||||
// creators["improved icy talons"] = &improved_icy_talons;
|
// creators["improved icy talons"] = &improved_icy_talons;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +70,13 @@ private:
|
|||||||
/*A*/ nullptr,
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
|
static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return new ActionNode("unbreakable armor",
|
||||||
|
/*P*/ NextAction::array(0, new NextAction("blood tap"), nullptr),
|
||||||
|
/*A*/ nullptr,
|
||||||
|
/*C*/ nullptr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||||
@ -80,24 +87,32 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
|||||||
NextAction** FrostDKStrategy::getDefaultActions()
|
NextAction** FrostDKStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(
|
return NextAction::array(
|
||||||
0, new NextAction("obliterate", ACTION_DEFAULT + 0.5f), new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
|
0, new NextAction("obliterate", ACTION_DEFAULT + 0.7f),
|
||||||
// new NextAction("death strike", ACTION_NORMAL + 3),
|
new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
|
||||||
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.2f),
|
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
|
||||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
|
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericDKStrategy::InitTriggers(triggers);
|
GenericDKStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_DEFAULT + 0.5f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"unbreakable armor", NextAction::array(0, new NextAction("unbreakable armor", ACTION_NORMAL + 4), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_HIGH + 1), nullptr)));
|
|
||||||
|
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune
|
// triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune
|
||||||
// weapon", ACTION_NORMAL + 4), nullptr)));
|
// weapon", ACTION_NORMAL + 4), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,7 +135,7 @@ private:
|
|||||||
{
|
{
|
||||||
return new ActionNode("death and decay",
|
return new ActionNode("death and decay",
|
||||||
/*P*/ nullptr,
|
/*P*/ nullptr,
|
||||||
/*A*/ NextAction::array(0, new NextAction("blood tap"), nullptr),
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,33 +176,14 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("mind freeze on enemy healer",
|
new TriggerNode("mind freeze on enemy healer",
|
||||||
NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr)));
|
NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_NORMAL + 1), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr)));
|
"horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("reach melee",
|
triggers.push_back(new TriggerNode("critical health",
|
||||||
// ACTION_NORMAL + 8), nullptr)));
|
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5),
|
new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5),
|
||||||
new NextAction("rune tap", ACTION_HIGH + 4), nullptr)));
|
new NextAction("rune tap", ACTION_HIGH + 4), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("medium health",
|
|
||||||
NextAction::array(0, new NextAction("rune tap", ACTION_NORMAL + 4),
|
|
||||||
new NextAction("death strike", ACTION_NORMAL + 3), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("icy touch on attacker",
|
|
||||||
NextAction::array(0, new NextAction("icy touch on attacker", ACTION_HIGH + 1), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("plague strike on attacker",
|
|
||||||
NextAction::array(0, new NextAction("plague strike on attacker", ACTION_HIGH + 1), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("high aoe",
|
|
||||||
// NextAction::array(0,
|
|
||||||
// new NextAction("death and decay", ACTION_NORMAL + 5),
|
|
||||||
// new NextAction("pestilence", ACTION_NORMAL + 4),
|
|
||||||
// new NextAction("blood boil", ACTION_NORMAL + 3), nullptr)));
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9),
|
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9),
|
||||||
new NextAction("pestilence", ACTION_NORMAL + 4),
|
new NextAction("pestilence", ACTION_NORMAL + 4),
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public:
|
|||||||
// creators["summon gargoyle"] = &army of the dead;
|
// creators["summon gargoyle"] = &army of the dead;
|
||||||
// creators["anti magic shell"] = &anti_magic_shell;
|
// creators["anti magic shell"] = &anti_magic_shell;
|
||||||
// creators["anti magic zone"] = &anti_magic_zone;
|
// creators["anti magic zone"] = &anti_magic_zone;
|
||||||
// creators["ghoul frenzy"] = &ghoul_frenzy;
|
creators["ghoul frenzy"] = &ghoul_frenzy;
|
||||||
creators["corpse explosion"] = &corpse_explosion;
|
creators["corpse explosion"] = &corpse_explosion;
|
||||||
creators["icy touch"] = &icy_touch;
|
creators["icy touch"] = &icy_touch;
|
||||||
}
|
}
|
||||||
@ -35,15 +35,21 @@ private:
|
|||||||
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new ActionNode("death strike",
|
return new ActionNode("death strike",
|
||||||
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
|
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||||
|
/*A*/ nullptr,
|
||||||
|
/*C*/ nullptr);
|
||||||
|
}
|
||||||
|
static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return new ActionNode("ghoul frenzy",
|
||||||
|
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||||
/*A*/ nullptr,
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new ActionNode("corpse explosion",
|
return new ActionNode("corpse explosion",
|
||||||
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
|
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||||
/*A*/ nullptr,
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
@ -51,14 +57,14 @@ private:
|
|||||||
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new ActionNode("scourge strike",
|
return new ActionNode("scourge strike",
|
||||||
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
|
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||||
/*A*/ nullptr,
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
return new ActionNode("icy touch",
|
return new ActionNode("icy touch",
|
||||||
/*P*/ NextAction::array(0, new NextAction("unholy presence"), nullptr),
|
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||||
/*A*/ nullptr,
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
@ -72,29 +78,57 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
|
|||||||
NextAction** UnholyDKStrategy::getDefaultActions()
|
NextAction** UnholyDKStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(
|
return NextAction::array(
|
||||||
0, new NextAction("death and decay", ACTION_DEFAULT + 0.5f),
|
0, new NextAction("death and decay", ACTION_HIGH + 5),
|
||||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.4f),
|
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
|
||||||
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.3f), new NextAction("death coil", ACTION_DEFAULT + 0.2f),
|
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
|
||||||
new NextAction("scourge strike", ACTION_NORMAL + 0.1f), new NextAction("melee", ACTION_DEFAULT), nullptr);
|
new NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
|
||||||
|
new NextAction("death coil", ACTION_DEFAULT + 0.1f),
|
||||||
|
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericDKStrategy::InitTriggers(triggers);
|
GenericDKStrategy::InitTriggers(triggers);
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"death and decay cooldown", NextAction::array(0,
|
||||||
|
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
|
||||||
|
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
|
||||||
|
new NextAction("blood boil", ACTION_DEFAULT + 0.7f),
|
||||||
|
new NextAction("icy touch", ACTION_DEFAULT + 0.6f),
|
||||||
|
new NextAction("plague strike", ACTION_DEFAULT + 0.5f),
|
||||||
|
nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("dd cd and no desolation",
|
||||||
|
NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr)));
|
||||||
|
|
||||||
|
// triggers.push_back(
|
||||||
|
// new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||||
|
// triggers.push_back(new TriggerNode(
|
||||||
|
// "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"high frost rune", NextAction::array(0,
|
||||||
|
new NextAction("icy touch", ACTION_NORMAL + 3), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"high unholy rune", NextAction::array(0,
|
||||||
|
new NextAction("plague strike", ACTION_NORMAL + 2), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 1), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("dd cd and plague strike 8s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("dd cd and icy touch 8s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 1), nullptr)));
|
||||||
|
|
||||||
|
|
||||||
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
|
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("critical health",
|
triggers.push_back(
|
||||||
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
|
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 1), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("no desolation",
|
|
||||||
NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"death and decay cooldown", NextAction::array(0, new NextAction("ghoul frenzy", ACTION_NORMAL + 5.0f),
|
|
||||||
new NextAction("scourge strike", ACTION_NORMAL + 4.0f),
|
|
||||||
new NextAction("blood boil", ACTION_NORMAL + 3.0f),
|
|
||||||
new NextAction("icy touch", ACTION_NORMAL + 2.0f),
|
|
||||||
new NextAction("plague strike", ACTION_NORMAL + 1.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
|||||||
@ -70,7 +70,7 @@ private:
|
|||||||
{
|
{
|
||||||
return new ActionNode("mangle (cat)",
|
return new ActionNode("mangle (cat)",
|
||||||
/*P*/ nullptr,
|
/*P*/ nullptr,
|
||||||
/*A*/ NextAction::array(0, new NextAction("claw"), nullptr),
|
/*A*/ nullptr,
|
||||||
/*C*/ nullptr);
|
/*C*/ nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,38 +122,58 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg
|
|||||||
|
|
||||||
NextAction** CatDpsDruidStrategy::getDefaultActions()
|
NextAction** CatDpsDruidStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.1f), nullptr);
|
return NextAction::array(0, new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
FeralDruidStrategy::InitTriggers(triggers);
|
FeralDruidStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
|
// Default priority
|
||||||
|
triggers.push_back(new TriggerNode("almost full energy available",
|
||||||
|
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("combo points not full",
|
||||||
|
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("almost full energy available",
|
||||||
|
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("combo points not full and high energy",
|
||||||
|
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("almost full energy available",
|
||||||
|
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("combo points not full and high energy",
|
||||||
|
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 2), nullptr)));
|
new TriggerNode("faerie fire (feral)",
|
||||||
|
NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr)));
|
||||||
|
|
||||||
|
// Main spell
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_NORMAL + 5), nullptr)));
|
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("combo points available",
|
||||||
|
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"combo points available", NextAction::array(0, new NextAction("ferocious bite", ACTION_NORMAL + 9), nullptr)));
|
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_EMERGENCY + 1), nullptr)));
|
new TriggerNode("target with combo points almost dead",
|
||||||
|
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("mangle (cat)",
|
||||||
|
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr)));
|
||||||
|
|
||||||
|
// AOE
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_HIGH), nullptr)));
|
"light aoe", NextAction::array(0, new NextAction("rake on attacker", ACTION_HIGH + 2), nullptr)));
|
||||||
|
// Reach target
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"tiger's fury", NextAction::array(0, new NextAction("tiger's fury", ACTION_EMERGENCY + 1), nullptr)));
|
"enemy out of melee", NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("behind target", NextAction::array(0, new NextAction("pounce", ACTION_HIGH + 1), nullptr)));
|
new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("dash", ACTION_HIGH + 8), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("player has no flag", NextAction::array(0, new NextAction("prowl",
|
|
||||||
// ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new
|
|
||||||
// NextAction("prowl", ACTION_INTERRUPT + 1), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("player has flag", NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("enemy flagcarrier near",
|
|
||||||
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatAoeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void CatAoeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) {}
|
||||||
{
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 2), nullptr)));
|
|
||||||
}
|
|
||||||
|
|||||||
@ -83,6 +83,8 @@ public:
|
|||||||
creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire;
|
creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire;
|
||||||
creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp;
|
creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp;
|
||||||
creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury;
|
creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury;
|
||||||
|
creators["berserk"] = &DruidTriggerFactoryInternal::berserk;
|
||||||
|
creators["savage roar"] = &DruidTriggerFactoryInternal::savage_roar;
|
||||||
creators["rake"] = &DruidTriggerFactoryInternal::rake;
|
creators["rake"] = &DruidTriggerFactoryInternal::rake;
|
||||||
creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild;
|
creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild;
|
||||||
creators["mark of the wild on party"] = &DruidTriggerFactoryInternal::mark_of_the_wild_on_party;
|
creators["mark of the wild on party"] = &DruidTriggerFactoryInternal::mark_of_the_wild_on_party;
|
||||||
@ -101,6 +103,8 @@ public:
|
|||||||
creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse;
|
creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse;
|
||||||
creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown;
|
creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown;
|
||||||
creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown;
|
creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown;
|
||||||
|
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
|
||||||
|
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -117,6 +121,8 @@ private:
|
|||||||
static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); }
|
static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); }
|
||||||
static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); }
|
static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); }
|
||||||
static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); }
|
static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); }
|
||||||
|
static Trigger* berserk(PlayerbotAI* botAI) { return new BerserkTrigger(botAI); }
|
||||||
|
static Trigger* savage_roar(PlayerbotAI* botAI) { return new SavageRoarTrigger(botAI); }
|
||||||
static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); }
|
static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); }
|
||||||
static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); }
|
static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); }
|
||||||
static Trigger* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new MarkOfTheWildOnPartyTrigger(botAI); }
|
static Trigger* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new MarkOfTheWildOnPartyTrigger(botAI); }
|
||||||
@ -133,6 +139,8 @@ private:
|
|||||||
static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); }
|
static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); }
|
||||||
static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); }
|
static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); }
|
||||||
static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); }
|
static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); }
|
||||||
|
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); }
|
||||||
|
static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
|
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
|
||||||
@ -174,6 +182,7 @@ public:
|
|||||||
creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat;
|
creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat;
|
||||||
creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat;
|
creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat;
|
||||||
creators["rake"] = &DruidAiObjectContextInternal::rake;
|
creators["rake"] = &DruidAiObjectContextInternal::rake;
|
||||||
|
creators["rake on attacker"] = &DruidAiObjectContextInternal::rake_on_attacker;
|
||||||
creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite;
|
creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite;
|
||||||
creators["rip"] = &DruidAiObjectContextInternal::rip;
|
creators["rip"] = &DruidAiObjectContextInternal::rip;
|
||||||
creators["cower"] = &DruidAiObjectContextInternal::cower;
|
creators["cower"] = &DruidAiObjectContextInternal::cower;
|
||||||
@ -188,6 +197,7 @@ public:
|
|||||||
creators["abolish poison on party"] = &DruidAiObjectContextInternal::abolish_poison_on_party;
|
creators["abolish poison on party"] = &DruidAiObjectContextInternal::abolish_poison_on_party;
|
||||||
creators["berserk"] = &DruidAiObjectContextInternal::berserk;
|
creators["berserk"] = &DruidAiObjectContextInternal::berserk;
|
||||||
creators["tiger's fury"] = &DruidAiObjectContextInternal::tigers_fury;
|
creators["tiger's fury"] = &DruidAiObjectContextInternal::tigers_fury;
|
||||||
|
creators["savage roar"] = &DruidAiObjectContextInternal::savage_roar;
|
||||||
creators["mark of the wild"] = &DruidAiObjectContextInternal::mark_of_the_wild;
|
creators["mark of the wild"] = &DruidAiObjectContextInternal::mark_of_the_wild;
|
||||||
creators["mark of the wild on party"] = &DruidAiObjectContextInternal::mark_of_the_wild_on_party;
|
creators["mark of the wild on party"] = &DruidAiObjectContextInternal::mark_of_the_wild_on_party;
|
||||||
creators["regrowth"] = &DruidAiObjectContextInternal::regrowth;
|
creators["regrowth"] = &DruidAiObjectContextInternal::regrowth;
|
||||||
@ -257,6 +267,7 @@ private:
|
|||||||
static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); }
|
static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); }
|
||||||
static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); }
|
static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); }
|
||||||
static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); }
|
static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); }
|
||||||
|
static Action* rake_on_attacker(PlayerbotAI* botAI) { return new CastRakeOnMeleeAttackersAction(botAI); }
|
||||||
static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); }
|
static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); }
|
||||||
static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); }
|
static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); }
|
||||||
static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); }
|
static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); }
|
||||||
@ -271,6 +282,7 @@ private:
|
|||||||
static Action* abolish_poison_on_party(PlayerbotAI* botAI) { return new CastAbolishPoisonOnPartyAction(botAI); }
|
static Action* abolish_poison_on_party(PlayerbotAI* botAI) { return new CastAbolishPoisonOnPartyAction(botAI); }
|
||||||
static Action* berserk(PlayerbotAI* botAI) { return new CastBerserkAction(botAI); }
|
static Action* berserk(PlayerbotAI* botAI) { return new CastBerserkAction(botAI); }
|
||||||
static Action* tigers_fury(PlayerbotAI* botAI) { return new CastTigersFuryAction(botAI); }
|
static Action* tigers_fury(PlayerbotAI* botAI) { return new CastTigersFuryAction(botAI); }
|
||||||
|
static Action* savage_roar(PlayerbotAI* botAI) { return new CastSavageRoarAction(botAI); }
|
||||||
static Action* mark_of_the_wild(PlayerbotAI* botAI) { return new CastMarkOfTheWildAction(botAI); }
|
static Action* mark_of_the_wild(PlayerbotAI* botAI) { return new CastMarkOfTheWildAction(botAI); }
|
||||||
static Action* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new CastMarkOfTheWildOnPartyAction(botAI); }
|
static Action* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new CastMarkOfTheWildOnPartyAction(botAI); }
|
||||||
static Action* regrowth(PlayerbotAI* botAI) { return new CastRegrowthAction(botAI); }
|
static Action* regrowth(PlayerbotAI* botAI) { return new CastRegrowthAction(botAI); }
|
||||||
|
|||||||
1
src/strategy/druid/DruidCatActions.cpp
Normal file
1
src/strategy/druid/DruidCatActions.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "DruidCatActions.h"
|
||||||
@ -35,10 +35,23 @@ public:
|
|||||||
CastTigersFuryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tiger's fury") {}
|
CastTigersFuryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tiger's fury") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CastSavageRoarAction : public CastBuffSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastSavageRoarAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "savage roar") {}
|
||||||
|
std::string const GetTargetName() override { return "current target"; }
|
||||||
|
};
|
||||||
|
|
||||||
class CastRakeAction : public CastDebuffSpellAction
|
class CastRakeAction : public CastDebuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake") {}
|
CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake", true, 6.0f) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CastRakeOnMeleeAttackersAction : public CastDebuffSpellOnMeleeAttackerAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastRakeOnMeleeAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnMeleeAttackerAction(botAI, "rake", true, 6.0f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastClawAction : public CastMeleeSpellAction
|
class CastClawAction : public CastMeleeSpellAction
|
||||||
@ -65,10 +78,10 @@ public:
|
|||||||
CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {}
|
CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastRipAction : public CastMeleeSpellAction
|
class CastRipAction : public CastMeleeDebuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CastRipAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "rip") {}
|
CastRipAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "rip", true, 12.0f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastShredAction : public CastMeleeSpellAction
|
class CastShredAction : public CastMeleeSpellAction
|
||||||
|
|||||||
@ -22,8 +22,6 @@ bool ThornsOnPartyTrigger::IsActive()
|
|||||||
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget());
|
return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoonfireTrigger::IsActive() { return DebuffTrigger::IsActive() && !GetTarget()->HasUnitState(UNIT_STATE_ROOT); }
|
|
||||||
|
|
||||||
bool EntanglingRootsKiteTrigger::IsActive()
|
bool EntanglingRootsKiteTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return DebuffTrigger::IsActive() && AI_VALUE(uint8, "attacker count") < 3 && !GetTarget()->GetPower(POWER_MANA);
|
return DebuffTrigger::IsActive() && AI_VALUE(uint8, "attacker count") < 3 && !GetTarget()->GetPower(POWER_MANA);
|
||||||
|
|||||||
@ -9,6 +9,8 @@
|
|||||||
#include "CureTriggers.h"
|
#include "CureTriggers.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@ -73,8 +75,6 @@ class MoonfireTrigger : public DebuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {}
|
MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {}
|
||||||
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FaerieFireTrigger : public DebuffTrigger
|
class FaerieFireTrigger : public DebuffTrigger
|
||||||
@ -95,10 +95,22 @@ public:
|
|||||||
BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {}
|
BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TigersFuryTrigger : public BoostTrigger
|
class TigersFuryTrigger : public BuffTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TigersFuryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "tiger's fury") {}
|
TigersFuryTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "tiger's fury") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BerserkTrigger : public BoostTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SavageRoarTrigger : public BuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SavageRoarTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "savage roar") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NaturesGraspTrigger : public BoostTrigger
|
class NaturesGraspTrigger : public BoostTrigger
|
||||||
@ -212,4 +224,43 @@ public:
|
|||||||
bool IsActive() override { return bot->HasSpellCooldown(48518); }
|
bool IsActive() override { return bot->HasSpellCooldown(48518); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MangleCatTrigger : public DebuffTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {}
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true)
|
||||||
|
&& !botAI->HasAura("trauma", GetTarget(), false, false, -1, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FerociousBiteTimeTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FerociousBiteTimeTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite time") {}
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 cp = AI_VALUE2(uint8, "combo", "current target");
|
||||||
|
if (cp < 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Aura* roar = botAI->GetAura("savage roar", bot);
|
||||||
|
bool roarCheck = !roar || roar->GetDuration() > 10000;
|
||||||
|
if (!roarCheck)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Aura* rip = botAI->GetAura("rip", target, true);
|
||||||
|
bool ripCheck = !rip || rip->GetDuration() > 10000;
|
||||||
|
if (!ripCheck)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -112,4 +112,6 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("enemy flagcarrier near",
|
triggers.push_back(new TriggerNode("enemy flagcarrier near",
|
||||||
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("berserk", NextAction::array(0, new NextAction("berserk", ACTION_HIGH + 6), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ float CastTimeMultiplier::GetValue(Action* action)
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "expected group dps")))
|
if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "estimated group dps")))
|
||||||
{
|
{
|
||||||
return 0.1f;
|
return 0.1f;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL)));
|
new TriggerNode("dps", NextAction::array(0, new NextAction("tell estimated dps", relevance), NULL)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
|
new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,19 @@
|
|||||||
|
|
||||||
#include "UseFoodStrategy.h"
|
#include "UseFoodStrategy.h"
|
||||||
|
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
void UseFoodStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void UseFoodStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
Strategy::InitTriggers(triggers);
|
Strategy::InitTriggers(triggers);
|
||||||
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
|
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
||||||
|
else
|
||||||
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
||||||
|
|
||||||
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
||||||
|
else
|
||||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,12 +41,13 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy
|
|||||||
NextAction** DpsHunterStrategy::getDefaultActions()
|
NextAction** DpsHunterStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(
|
return NextAction::array(
|
||||||
0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f),
|
0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f),
|
||||||
new NextAction("explosive shot", ACTION_DEFAULT + 0.4f), new NextAction("aimed shot", ACTION_DEFAULT + 0.3f),
|
new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f),
|
||||||
/*new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),*/ new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
|
new NextAction("silencing shot", ACTION_DEFAULT + 0.4f),
|
||||||
|
new NextAction("kill command", ACTION_DEFAULT + 0.3f),
|
||||||
|
// new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),
|
||||||
|
new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
|
||||||
new NextAction("auto shot", ACTION_DEFAULT), nullptr);
|
new NextAction("auto shot", ACTION_DEFAULT), nullptr);
|
||||||
// return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new
|
|
||||||
// NextAction("auto attack", 9.0f), nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
|||||||
@ -95,7 +95,7 @@ void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode("enemy too close for auto shot",
|
triggers.push_back(new TriggerNode("enemy too close for auto shot",
|
||||||
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
|
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("misdirection on main tank",
|
new TriggerNode("low tank threat",
|
||||||
NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL)));
|
NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr)));
|
new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr)));
|
||||||
|
|||||||
@ -78,6 +78,9 @@ END_SPELL_ACTION()
|
|||||||
BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot")
|
BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot")
|
||||||
END_SPELL_ACTION()
|
END_SPELL_ACTION()
|
||||||
|
|
||||||
|
BEGIN_RANGED_SPELL_ACTION(CastSilencingShotAction, "silencing shot")
|
||||||
|
END_SPELL_ACTION()
|
||||||
|
|
||||||
BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot")
|
BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot")
|
||||||
END_SPELL_ACTION()
|
END_SPELL_ACTION()
|
||||||
|
|
||||||
@ -139,6 +142,14 @@ public:
|
|||||||
std::string const GetTargetName() override { return "pet target"; }
|
std::string const GetTargetName() override { return "pet target"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CastKillCommandAction : public CastAuraSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastKillCommandAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "kill command") {}
|
||||||
|
|
||||||
|
std::string const GetTargetName() override { return "pet target"; }
|
||||||
|
};
|
||||||
|
|
||||||
class CastRevivePetAction : public CastBuffSpellAction
|
class CastRevivePetAction : public CastBuffSpellAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -143,6 +143,7 @@ public:
|
|||||||
creators["scorpid sting"] = &HunterAiObjectContextInternal::scorpid_sting;
|
creators["scorpid sting"] = &HunterAiObjectContextInternal::scorpid_sting;
|
||||||
creators["hunter's mark"] = &HunterAiObjectContextInternal::hunters_mark;
|
creators["hunter's mark"] = &HunterAiObjectContextInternal::hunters_mark;
|
||||||
creators["mend pet"] = &HunterAiObjectContextInternal::mend_pet;
|
creators["mend pet"] = &HunterAiObjectContextInternal::mend_pet;
|
||||||
|
creators["kill command"] = &HunterAiObjectContextInternal::kill_command;
|
||||||
creators["revive pet"] = &HunterAiObjectContextInternal::revive_pet;
|
creators["revive pet"] = &HunterAiObjectContextInternal::revive_pet;
|
||||||
creators["call pet"] = &HunterAiObjectContextInternal::call_pet;
|
creators["call pet"] = &HunterAiObjectContextInternal::call_pet;
|
||||||
creators["black arrow"] = &HunterAiObjectContextInternal::black_arrow;
|
creators["black arrow"] = &HunterAiObjectContextInternal::black_arrow;
|
||||||
@ -171,6 +172,7 @@ public:
|
|||||||
creators["steady shot"] = &HunterAiObjectContextInternal::steady_shot;
|
creators["steady shot"] = &HunterAiObjectContextInternal::steady_shot;
|
||||||
creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot;
|
creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot;
|
||||||
creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank;
|
creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank;
|
||||||
|
creators["silencing shot"] = &HunterAiObjectContextInternal::silencing_shot;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -196,6 +198,7 @@ private:
|
|||||||
static Action* scorpid_sting(PlayerbotAI* botAI) { return new CastScorpidStingAction(botAI); }
|
static Action* scorpid_sting(PlayerbotAI* botAI) { return new CastScorpidStingAction(botAI); }
|
||||||
static Action* hunters_mark(PlayerbotAI* botAI) { return new CastHuntersMarkAction(botAI); }
|
static Action* hunters_mark(PlayerbotAI* botAI) { return new CastHuntersMarkAction(botAI); }
|
||||||
static Action* mend_pet(PlayerbotAI* botAI) { return new CastMendPetAction(botAI); }
|
static Action* mend_pet(PlayerbotAI* botAI) { return new CastMendPetAction(botAI); }
|
||||||
|
static Action* kill_command(PlayerbotAI* botAI) { return new CastKillCommandAction(botAI); }
|
||||||
static Action* revive_pet(PlayerbotAI* botAI) { return new CastRevivePetAction(botAI); }
|
static Action* revive_pet(PlayerbotAI* botAI) { return new CastRevivePetAction(botAI); }
|
||||||
static Action* call_pet(PlayerbotAI* botAI) { return new CastCallPetAction(botAI); }
|
static Action* call_pet(PlayerbotAI* botAI) { return new CastCallPetAction(botAI); }
|
||||||
static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); }
|
static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); }
|
||||||
@ -217,6 +220,8 @@ private:
|
|||||||
static Action* steady_shot(PlayerbotAI* ai) { return new CastSteadyShotAction(ai); }
|
static Action* steady_shot(PlayerbotAI* ai) { return new CastSteadyShotAction(ai); }
|
||||||
static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); }
|
static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); }
|
||||||
static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); }
|
static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); }
|
||||||
|
static Action* silencing_shot(PlayerbotAI* ai) { return new CastSilencingShotAction(ai); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
|
HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
|
||||||
|
|||||||
@ -82,23 +82,32 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat
|
|||||||
|
|
||||||
NextAction** DpsPaladinStrategy::getDefaultActions()
|
NextAction** DpsPaladinStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return NextAction::array(0, new NextAction("crusader strike", ACTION_DEFAULT + 0.4f),
|
return NextAction::array(0,
|
||||||
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f),
|
new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f),
|
||||||
new NextAction("divine storm", ACTION_DEFAULT + 0.2f),
|
new NextAction("crusader strike", ACTION_DEFAULT + 0.5f),
|
||||||
|
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.4f),
|
||||||
|
new NextAction("divine storm", ACTION_DEFAULT + 0.3f),
|
||||||
new NextAction("consecration", ACTION_DEFAULT + 0.1f),
|
new NextAction("consecration", ACTION_DEFAULT + 0.1f),
|
||||||
new NextAction("melee", ACTION_DEFAULT), NULL);
|
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
GenericPaladinStrategy::InitTriggers(triggers);
|
GenericPaladinStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
|
// triggers.push_back(new TriggerNode(
|
||||||
|
// "enough mana", NextAction::array(0, new NextAction("consecration", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), NULL)));
|
new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), NULL)));
|
||||||
// triggers.push_back(new TriggerNode("seal", NextAction::array(0, new NextAction("seal of command", 90.0f),
|
// triggers.push_back(new TriggerNode("seal", NextAction::array(0, new NextAction("seal of command", 90.0f),
|
||||||
// nullptr)));
|
// nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 5), nullptr)));
|
new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 5), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("sanctity aura", NextAction::array(0, new NextAction("sanctity aura", 90.0f),
|
// triggers.push_back(new TriggerNode("sanctity aura", NextAction::array(0, new NextAction("sanctity aura", 90.0f),
|
||||||
// nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("repentance or
|
// nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("repentance or
|
||||||
// shield", ACTION_CRITICAL_HEAL + 3), new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr)));
|
// shield", ACTION_CRITICAL_HEAL + 3), new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr)));
|
||||||
@ -112,11 +121,11 @@ void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
// triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance",
|
// triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance",
|
||||||
// ACTION_INTERRUPT + 2), nullptr)));
|
// ACTION_INTERRUPT + 2), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"medium aoe", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 3), nullptr)));
|
"medium aoe", NextAction::array(0,
|
||||||
triggers.push_back(
|
new NextAction("divine storm", ACTION_HIGH + 4),
|
||||||
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr)));
|
new NextAction("consecration", ACTION_HIGH + 3), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("target critical health",
|
// triggers.push_back(new TriggerNode("target critical health",
|
||||||
NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr)));
|
// NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode(
|
// triggers.push_back(new TriggerNode(
|
||||||
// "not facing target",
|
// "not facing target",
|
||||||
// NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL)));
|
// NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL)));
|
||||||
|
|||||||
@ -37,7 +37,7 @@ void GenericPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
"protect party member",
|
"protect party member",
|
||||||
NextAction::array(0, new NextAction("blessing of protection on party", ACTION_EMERGENCY + 2), nullptr)));
|
NextAction::array(0, new NextAction("blessing of protection on party", ACTION_EMERGENCY + 2), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL)));
|
new TriggerNode("high mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaladinCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PaladinCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
@ -61,8 +61,7 @@ void PaladinCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
void PaladinBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PaladinBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("divine favor", NextAction::array(0, new NextAction("divine favor",
|
// triggers.push_back(new TriggerNode("divine favor", NextAction::array(0, new NextAction("divine favor",
|
||||||
// ACTION_HIGH + 1), nullptr)));
|
// ACTION_HIGH + 1), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,9 @@ void HealPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium group heal occasion",
|
new TriggerNode("medium group heal occasion",
|
||||||
NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), nullptr)));
|
NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5),
|
||||||
|
new NextAction("avenging wrath", ACTION_HIGH + 4),
|
||||||
|
nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("party member critical health",
|
new TriggerNode("party member critical health",
|
||||||
|
|||||||
@ -98,6 +98,8 @@ void TankPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
NextAction::array(0, new NextAction("holy shield", ACTION_HIGH + 4), nullptr)));
|
NextAction::array(0, new NextAction("holy shield", ACTION_HIGH + 4), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of sanctuary",
|
// triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of sanctuary",
|
||||||
// ACTION_HIGH + 9), nullptr)));
|
// ACTION_HIGH + 9), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("target critical health",
|
new TriggerNode("target critical health",
|
||||||
NextAction::array(0, new NextAction("hammer of wrath", ACTION_CRITICAL_HEAL), nullptr)));
|
NextAction::array(0, new NextAction("hammer of wrath", ACTION_CRITICAL_HEAL), nullptr)));
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "RaidBwlStrategy.h"
|
#include "RaidBwlStrategy.h"
|
||||||
#include "RaidNaxxStrategy.h"
|
#include "RaidNaxxStrategy.h"
|
||||||
#include "RaidMcStrategy.h"
|
#include "RaidMcStrategy.h"
|
||||||
|
#include "RaidAq20Strategy.h"
|
||||||
|
|
||||||
class RaidStrategyContext : public NamedObjectContext<Strategy>
|
class RaidStrategyContext : public NamedObjectContext<Strategy>
|
||||||
{
|
{
|
||||||
@ -19,6 +20,7 @@ public:
|
|||||||
creators["bwl"] = &RaidStrategyContext::bwl;
|
creators["bwl"] = &RaidStrategyContext::bwl;
|
||||||
creators["uld"] = &RaidStrategyContext::uld;
|
creators["uld"] = &RaidStrategyContext::uld;
|
||||||
creators["mc"] = &RaidStrategyContext::mc;
|
creators["mc"] = &RaidStrategyContext::mc;
|
||||||
|
creators["aq20"] = &RaidStrategyContext::aq20;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -26,6 +28,7 @@ private:
|
|||||||
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
||||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||||
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
|
||||||
|
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
20
src/strategy/raids/aq20/RaidAq20ActionContext.h
Normal file
20
src/strategy/raids/aq20/RaidAq20ActionContext.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20ACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
#include "RaidAq20Actions.h"
|
||||||
|
|
||||||
|
class RaidAq20ActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidAq20ActionContext()
|
||||||
|
{
|
||||||
|
creators["aq20 use crystal"] = &RaidAq20ActionContext::use_crystal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Action* use_crystal(PlayerbotAI* ai) { return new Aq20UseCrystalAction(ai); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
49
src/strategy/raids/aq20/RaidAq20Actions.cpp
Normal file
49
src/strategy/raids/aq20/RaidAq20Actions.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "RaidAq20Actions.h"
|
||||||
|
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "RaidAq20Utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool Aq20UseCrystalAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||||
|
{
|
||||||
|
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||||
|
{
|
||||||
|
float botDist = bot->GetDistance(crystal);
|
||||||
|
if (botDist > INTERACTION_DISTANCE)
|
||||||
|
return MoveTo(bot->GetMapId(),
|
||||||
|
crystal->GetPositionX() + frand(-3.5f, 3.5f),
|
||||||
|
crystal->GetPositionY() + frand(-3.5f, 3.5f),
|
||||||
|
crystal->GetPositionZ());
|
||||||
|
|
||||||
|
// if we're already in range just wait here until it's time to activate crystal
|
||||||
|
SetNextMovementDelay(500);
|
||||||
|
|
||||||
|
// don't activate crystal if boss too far or its already been activated
|
||||||
|
if (boss->GetDistance(crystal) > 25.0f ||
|
||||||
|
crystal->HasGameObjectFlag(GO_FLAG_IN_USE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// don't activate crystal if boss doesn't have buff yet AND isn't going to have it soon
|
||||||
|
// (though ideally bot should activate it ~5 seconds early due to time it takes for
|
||||||
|
// crystal to activate and remove buff)
|
||||||
|
if (!RaidAq20Utils::IsOssirianBuffActive(boss) &&
|
||||||
|
RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss) > 5000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// this makes crystal do its animation (then disappear after)
|
||||||
|
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||||
|
data1 << crystal->GetGUID();
|
||||||
|
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||||
|
|
||||||
|
// this makes crystal actually remove the buff and put on debuff (took a while to figure that out)
|
||||||
|
WorldPacket data2(CMSG_GAMEOBJ_USE);
|
||||||
|
data2 << crystal->GetGUID();
|
||||||
|
bot->GetSession()->HandleGameobjectReportUse(data2);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
14
src/strategy/raids/aq20/RaidAq20Actions.h
Normal file
14
src/strategy/raids/aq20/RaidAq20Actions.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20ACTIONS_H
|
||||||
|
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
class Aq20UseCrystalAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Aq20UseCrystalAction(PlayerbotAI* botAI, std::string const name = "aq20 use crystal") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
11
src/strategy/raids/aq20/RaidAq20Strategy.cpp
Normal file
11
src/strategy/raids/aq20/RaidAq20Strategy.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "RaidAq20Strategy.h"
|
||||||
|
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
void RaidAq20Strategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("aq20 move to crystal",
|
||||||
|
NextAction::array(0, new NextAction("aq20 use crystal", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
}
|
||||||
17
src/strategy/raids/aq20/RaidAq20Strategy.h
Normal file
17
src/strategy/raids/aq20/RaidAq20Strategy.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
class RaidAq20Strategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidAq20Strategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||||
|
virtual std::string const getName() override { return "aq20"; }
|
||||||
|
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
// virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
20
src/strategy/raids/aq20/RaidAq20TriggerContext.h
Normal file
20
src/strategy/raids/aq20/RaidAq20TriggerContext.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
#include "RaidAq20Triggers.h"
|
||||||
|
|
||||||
|
class RaidAq20TriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RaidAq20TriggerContext()
|
||||||
|
{
|
||||||
|
creators["aq20 move to crystal"] = &RaidAq20TriggerContext::move_to_crystal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Trigger* move_to_crystal(PlayerbotAI* ai) { return new Aq20MoveToCrystalTrigger(ai); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
37
src/strategy/raids/aq20/RaidAq20Triggers.cpp
Normal file
37
src/strategy/raids/aq20/RaidAq20Triggers.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "RaidAq20Triggers.h"
|
||||||
|
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "RaidAq20Utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool Aq20MoveToCrystalTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred"))
|
||||||
|
{
|
||||||
|
if (boss->IsInCombat())
|
||||||
|
{
|
||||||
|
// if buff is active move to crystal
|
||||||
|
if (RaidAq20Utils::IsOssirianBuffActive(boss))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// if buff is not active a debuff will be, buff becomes active once debuff expires
|
||||||
|
// so move to crystal when debuff almost done, or based debuff time left and
|
||||||
|
// distance bot is from crystal (ie: start moving early enough to make it)
|
||||||
|
int32 debuffTimeRemaining = RaidAq20Utils::GetOssirianDebuffTimeRemaining(boss);
|
||||||
|
if (debuffTimeRemaining < 5000)
|
||||||
|
return true;
|
||||||
|
if (debuffTimeRemaining < 30000)
|
||||||
|
{
|
||||||
|
if (GameObject* crystal = RaidAq20Utils::GetNearestCrystal(boss))
|
||||||
|
{
|
||||||
|
float botDist = bot->GetDistance(crystal);
|
||||||
|
float timeToReach = botDist / bot->GetSpeed(MOVE_RUN);
|
||||||
|
// bot should ideally activate crystal a ~5 seconds early (due to time it takes for crystal
|
||||||
|
// to activate) so aim to get there in time to do so
|
||||||
|
return debuffTimeRemaining - 5000 < timeToReach * 1000.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
14
src/strategy/raids/aq20/RaidAq20Triggers.h
Normal file
14
src/strategy/raids/aq20/RaidAq20Triggers.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20TRIGGERS_H
|
||||||
|
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
class Aq20MoveToCrystalTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Aq20MoveToCrystalTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq20 move to crystal") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
38
src/strategy/raids/aq20/RaidAq20Utils.cpp
Normal file
38
src/strategy/raids/aq20/RaidAq20Utils.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "RaidAq20Utils.h"
|
||||||
|
|
||||||
|
#include "SpellAuras.h"
|
||||||
|
|
||||||
|
uint32 const OSSIRIAN_BUFF = 25176;
|
||||||
|
uint32 const OSSIRIAN_DEBUFFS[] = {25177, 25178, 25180, 25181, 25183};
|
||||||
|
uint32 const OSSIRIAN_CRYSTAL_GO_ENTRY = 180619;
|
||||||
|
|
||||||
|
bool RaidAq20Utils::IsOssirianBuffActive(Unit* ossirian)
|
||||||
|
{
|
||||||
|
return ossirian && ossirian->HasAura(OSSIRIAN_BUFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 RaidAq20Utils::GetOssirianDebuffTimeRemaining(Unit* ossirian)
|
||||||
|
{
|
||||||
|
int32 retVal = 0xffffff;
|
||||||
|
if (ossirian)
|
||||||
|
{
|
||||||
|
for (uint32 debuff : OSSIRIAN_DEBUFFS)
|
||||||
|
{
|
||||||
|
if (AuraApplication* auraApplication = ossirian->GetAuraApplication(debuff))
|
||||||
|
{
|
||||||
|
if (Aura* aura = auraApplication->GetBase())
|
||||||
|
{
|
||||||
|
int32 duration = aura->GetDuration();
|
||||||
|
if (retVal > duration)
|
||||||
|
retVal = duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject* RaidAq20Utils::GetNearestCrystal(Unit* ossirian)
|
||||||
|
{
|
||||||
|
return ossirian ? ossirian->FindNearestGameObject(OSSIRIAN_CRYSTAL_GO_ENTRY, 200.0f) : nullptr;
|
||||||
|
}
|
||||||
15
src/strategy/raids/aq20/RaidAq20Utils.h
Normal file
15
src/strategy/raids/aq20/RaidAq20Utils.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDAQ20UTILS_H
|
||||||
|
#define _PLAYERBOT_RAIDAQ20UTILS_H
|
||||||
|
|
||||||
|
#include "GameObject.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
|
class RaidAq20Utils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool IsOssirianBuffActive(Unit* ossirian);
|
||||||
|
static int32 GetOssirianDebuffTimeRemaining(Unit* ossirian);
|
||||||
|
static GameObject* GetNearestCrystal(Unit* ossirian);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -64,6 +64,7 @@ public:
|
|||||||
this->prev_phase = 0;
|
this->prev_phase = 0;
|
||||||
this->prev_erupt = 0;
|
this->prev_erupt = 0;
|
||||||
this->prev_timer = 0;
|
this->prev_timer = 0;
|
||||||
|
ResetSafe();
|
||||||
waypoints.push_back(std::make_pair(2793.58f, -3665.93f));
|
waypoints.push_back(std::make_pair(2793.58f, -3665.93f));
|
||||||
waypoints.push_back(std::make_pair(2775.49f, -3674.43f));
|
waypoints.push_back(std::make_pair(2775.49f, -3674.43f));
|
||||||
waypoints.push_back(std::make_pair(2762.30f, -3684.59f));
|
waypoints.push_back(std::make_pair(2762.30f, -3684.59f));
|
||||||
|
|||||||
@ -83,7 +83,7 @@ void AssassinationRogueStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL)));
|
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"tricks of the trade on main tank",
|
"low tank threat",
|
||||||
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL)));
|
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
|
|||||||
@ -141,7 +141,7 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr)));
|
NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"tricks of the trade on main tank",
|
"low tank threat",
|
||||||
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr)));
|
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,8 +77,6 @@ public:
|
|||||||
creators["tricks of the trade on main tank"] = &RogueTriggerFactoryInternal::tricks_of_the_trade_on_main_tank;
|
creators["tricks of the trade on main tank"] = &RogueTriggerFactoryInternal::tricks_of_the_trade_on_main_tank;
|
||||||
creators["adrenaline rush"] = &RogueTriggerFactoryInternal::adrenaline_rush;
|
creators["adrenaline rush"] = &RogueTriggerFactoryInternal::adrenaline_rush;
|
||||||
creators["blade fury"] = &RogueTriggerFactoryInternal::blade_fury;
|
creators["blade fury"] = &RogueTriggerFactoryInternal::blade_fury;
|
||||||
creators["target with combo points almost dead"] =
|
|
||||||
&RogueTriggerFactoryInternal::target_with_combo_points_almost_dead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -102,10 +100,6 @@ private:
|
|||||||
{
|
{
|
||||||
return new TricksOfTheTradeOnMainTankTrigger(ai);
|
return new TricksOfTheTradeOnMainTankTrigger(ai);
|
||||||
}
|
}
|
||||||
static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai)
|
|
||||||
{
|
|
||||||
return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RogueAiObjectContextInternal : public NamedObjectContext<Action>
|
class RogueAiObjectContextInternal : public NamedObjectContext<Action>
|
||||||
|
|||||||
@ -124,14 +124,3 @@ bool OffHandWeaponNoEnchantTrigger::IsActive()
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TargetWithComboPointsLowerHealTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
|
||||||
if (!target || !target->IsAlive() || !target->IsInWorld())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ComboPointsAvailableTrigger::IsActive() &&
|
|
||||||
(target->GetHealth() / AI_VALUE(float, "expected group dps")) <= lifeTime;
|
|
||||||
}
|
|
||||||
@ -127,17 +127,6 @@ public:
|
|||||||
TricksOfTheTradeOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "tricks of the trade", true) {}
|
TricksOfTheTradeOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "tricks of the trade", true) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f)
|
|
||||||
: ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
bool IsActive() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
float lifeTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -51,8 +51,8 @@ void CasterShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
|
// triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
|
||||||
// ACTION_NORMAL + 9), nullptr))); triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new
|
// ACTION_NORMAL + 9), nullptr))); triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new
|
||||||
// NextAction("flametongue weapon", 23.0f), nullptr)));
|
// NextAction("flametongue weapon", 23.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
// triggers.push_back(new TriggerNode(
|
||||||
"enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr)));
|
// "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("main hand weapon no imbue",
|
triggers.push_back(new TriggerNode("main hand weapon no imbue",
|
||||||
NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr)));
|
NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr)));
|
||||||
@ -66,6 +66,8 @@ void CasterShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
// shock", 21.0f), nullptr)));
|
// shock", 21.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no fire totem", NextAction::array(0, new NextAction("totem of wrath", 15.0f), NULL)));
|
new TriggerNode("no fire totem", NextAction::array(0, new NextAction("totem of wrath", 15.0f), NULL)));
|
||||||
|
triggers.push_back(new TriggerNode("fire elemental totem",
|
||||||
|
NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium mana", NextAction::array(0, new NextAction("thunderstorm", ACTION_HIGH + 1), nullptr)));
|
new TriggerNode("medium mana", NextAction::array(0, new NextAction("thunderstorm", ACTION_HIGH + 1), nullptr)));
|
||||||
|
|||||||
@ -104,6 +104,11 @@ void HealShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana tide totem", ACTION_HIGH + 5), NULL)));
|
new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana tide totem", ACTION_HIGH + 5), NULL)));
|
||||||
|
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f),
|
||||||
|
new NextAction("searing totem", 6.0f), nullptr)));
|
||||||
|
triggers.push_back(new TriggerNode("fire elemental totem",
|
||||||
|
NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"party member to heal out of spell range",
|
"party member to heal out of spell range",
|
||||||
NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 1), nullptr)));
|
NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 1), nullptr)));
|
||||||
|
|||||||
@ -72,11 +72,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr)));
|
new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("maelstrom weapon", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr)));
|
new TriggerNode("maelstrom weapon 4", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("not facing target",
|
|
||||||
NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
|
|
||||||
// enemy contact", ACTION_NORMAL + 8), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr)));
|
"medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr)));
|
||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
@ -86,9 +83,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
"no fire totem",
|
"no fire totem",
|
||||||
NextAction::array(0, new NextAction("reach melee", 23.0f), new NextAction("magma totem", 22.0f), nullptr)));
|
NextAction::array(0, new NextAction("reach melee", 23.0f), new NextAction("magma totem", 22.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("fire elemental totem",
|
triggers.push_back(new TriggerNode(
|
||||||
NextAction::array(0, new NextAction("reach melee", 33.0f),
|
"fire elemental totem", NextAction::array(0, new NextAction("fire elemental totem melee", 32.0f), nullptr)));
|
||||||
new NextAction("fire elemental totem", 32.0f), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no air totem", NextAction::array(0, new NextAction("windfury totem", 20.0f), nullptr)));
|
new TriggerNode("no air totem", NextAction::array(0, new NextAction("windfury totem", 20.0f), nullptr)));
|
||||||
|
|||||||
@ -17,7 +17,7 @@ bool CastTotemAction::isUseful()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float dps = AI_VALUE(float, "expected group dps");
|
float dps = AI_VALUE(float, "estimated group dps");
|
||||||
if (target->GetHealth() / dps < needLifeTime)
|
if (target->GetHealth() / dps < needLifeTime)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -51,11 +51,14 @@ bool CastMagmaTotemAction::isUseful() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CastFireNovaAction::isUseful() {
|
bool CastFireNovaAction::isUseful() {
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]);
|
Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]);
|
||||||
if (!fireTotem)
|
if (!fireTotem)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->GetDistance(fireTotem) > 8.0f)
|
if (target->GetDistance(fireTotem) > 8.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return CastMeleeSpellAction::isUseful();
|
return CastMeleeSpellAction::isUseful();
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#define _PLAYERBOT_SHAMANACTIONS_H
|
#define _PLAYERBOT_SHAMANACTIONS_H
|
||||||
|
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@ -426,6 +427,20 @@ public:
|
|||||||
virtual bool isUseful() override { return CastTotemAction::isUseful(); }
|
virtual bool isUseful() override { return CastTotemAction::isUseful(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CastFireElementalTotemMeleeAction : public CastTotemAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CastFireElementalTotemMeleeAction(PlayerbotAI* ai) : CastTotemAction(ai, "fire elemental totem", "", 0.0f) {}
|
||||||
|
virtual std::string const GetTargetName() override { return "self target"; }
|
||||||
|
virtual bool isUseful() override
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target || !bot->IsWithinMeleeRange(target))
|
||||||
|
return false;
|
||||||
|
return CastTotemAction::isUseful();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CastWrathOfAirTotemAction : public CastTotemAction
|
class CastWrathOfAirTotemAction : public CastTotemAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -110,13 +110,17 @@ public:
|
|||||||
creators["no water totem"] = &ShamanATriggerFactoryInternal::no_water_totem;
|
creators["no water totem"] = &ShamanATriggerFactoryInternal::no_water_totem;
|
||||||
creators["no air totem"] = &ShamanATriggerFactoryInternal::no_air_totem;
|
creators["no air totem"] = &ShamanATriggerFactoryInternal::no_air_totem;
|
||||||
creators["earth shield on main tank"] = &ShamanATriggerFactoryInternal::earth_shield_on_main_tank;
|
creators["earth shield on main tank"] = &ShamanATriggerFactoryInternal::earth_shield_on_main_tank;
|
||||||
creators["maelstrom weapon"] = &ShamanATriggerFactoryInternal::maelstrom_weapon;
|
creators["maelstrom weapon 3"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_3;
|
||||||
|
creators["maelstrom weapon 4"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_4;
|
||||||
|
creators["maelstrom weapon 5"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_5;
|
||||||
creators["flame shock"] = &ShamanATriggerFactoryInternal::flame_shock;
|
creators["flame shock"] = &ShamanATriggerFactoryInternal::flame_shock;
|
||||||
creators["wrath of air totem"] = &ShamanATriggerFactoryInternal::wrath_of_air_totem;
|
creators["wrath of air totem"] = &ShamanATriggerFactoryInternal::wrath_of_air_totem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Trigger* maelstrom_weapon(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI); }
|
static Trigger* maelstrom_weapon_3(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 3); }
|
||||||
|
static Trigger* maelstrom_weapon_4(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 4); }
|
||||||
|
static Trigger* maelstrom_weapon_5(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 5); }
|
||||||
static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); }
|
static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); }
|
||||||
static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); }
|
static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); }
|
||||||
static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); }
|
static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); }
|
||||||
@ -234,6 +238,7 @@ public:
|
|||||||
creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst;
|
creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst;
|
||||||
creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank;
|
creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank;
|
||||||
creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem;
|
creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem;
|
||||||
|
creators["fire elemental totem melee"] = &ShamanAiObjectContextInternal::fire_elemental_totem_melee;
|
||||||
creators["totem of wrath"] = &ShamanAiObjectContextInternal::totem_of_wrath;
|
creators["totem of wrath"] = &ShamanAiObjectContextInternal::totem_of_wrath;
|
||||||
creators["wrath of air totem"] = &ShamanAiObjectContextInternal::wrath_of_air_totem;
|
creators["wrath of air totem"] = &ShamanAiObjectContextInternal::wrath_of_air_totem;
|
||||||
creators["shamanistic rage"] = &ShamanAiObjectContextInternal::shamanistic_rage;
|
creators["shamanistic rage"] = &ShamanAiObjectContextInternal::shamanistic_rage;
|
||||||
@ -314,6 +319,7 @@ private:
|
|||||||
static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); }
|
static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); }
|
||||||
static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); }
|
static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); }
|
||||||
static Action* fire_elemental_totem(PlayerbotAI* ai) { return new CastFireElementalTotemAction(ai); }
|
static Action* fire_elemental_totem(PlayerbotAI* ai) { return new CastFireElementalTotemAction(ai); }
|
||||||
|
static Action* fire_elemental_totem_melee(PlayerbotAI* ai) { return new CastFireElementalTotemMeleeAction(ai); }
|
||||||
static Action* wrath_of_air_totem(PlayerbotAI* ai) { return new CastWrathOfAirTotemAction(ai); }
|
static Action* wrath_of_air_totem(PlayerbotAI* ai) { return new CastWrathOfAirTotemAction(ai); }
|
||||||
static Action* shamanistic_rage(PlayerbotAI* ai) { return new CastShamanisticRageAction(ai); }
|
static Action* shamanistic_rage(PlayerbotAI* ai) { return new CastShamanisticRageAction(ai); }
|
||||||
static Action* feral_spirit(PlayerbotAI* ai) { return new CastFeralSpiritAction(ai); }
|
static Action* feral_spirit(PlayerbotAI* ai) { return new CastFeralSpiritAction(ai); }
|
||||||
|
|||||||
@ -241,7 +241,7 @@ public:
|
|||||||
class MaelstromWeaponTrigger : public HasAuraStackTrigger
|
class MaelstromWeaponTrigger : public HasAuraStackTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MaelstromWeaponTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "maelstrom weapon", 5) {}
|
MaelstromWeaponTrigger(PlayerbotAI* botAI, int stack = 5) : HasAuraStackTrigger(botAI, "maelstrom weapon", stack) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindShearInterruptEnemyHealerSpellTrigger : public InterruptEnemyHealerTrigger
|
class WindShearInterruptEnemyHealerSpellTrigger : public InterruptEnemyHealerTrigger
|
||||||
|
|||||||
@ -13,20 +13,13 @@ void TotemsShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
GenericShamanStrategy::InitTriggers(triggers);
|
GenericShamanStrategy::InitTriggers(triggers);
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("fire elemental totem",
|
|
||||||
NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), NULL)));
|
new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no water totem", NextAction::array(0, new NextAction("mana spring totem", 7.0f),
|
new TriggerNode("no water totem", NextAction::array(0, new NextAction("mana spring totem", 7.0f),
|
||||||
new NextAction("healing stream totem", 6.0f), nullptr)));
|
new NextAction("healing stream totem", 6.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f),
|
|
||||||
new NextAction("searing totem", 6.0f), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("strength of earth totem",
|
triggers.push_back(new TriggerNode("strength of earth totem",
|
||||||
NextAction::array(0, new NextAction("strength of earth totem", 6.0f), NULL)));
|
NextAction::array(0, new NextAction("strength of earth totem", 6.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,11 @@
|
|||||||
#include "BattlegroundWS.h"
|
#include "BattlegroundWS.h"
|
||||||
#include "CreatureAI.h"
|
#include "CreatureAI.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "TemporarySummon.h"
|
#include "TemporarySummon.h"
|
||||||
|
#include "ThreatMgr.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
|
|
||||||
bool LowManaTrigger::IsActive()
|
bool LowManaTrigger::IsActive()
|
||||||
@ -64,7 +66,7 @@ bool PetAttackTrigger::IsActive()
|
|||||||
|
|
||||||
bool HighManaTrigger::IsActive()
|
bool HighManaTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < 65;
|
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlmostFullManaTrigger::IsActive()
|
bool AlmostFullManaTrigger::IsActive()
|
||||||
@ -83,6 +85,19 @@ bool EnergyAvailable::IsActive() { return AI_VALUE2(uint8, "energy", "self targe
|
|||||||
|
|
||||||
bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; }
|
bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; }
|
||||||
|
|
||||||
|
bool ComboPointsNotFullTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") < amount; }
|
||||||
|
|
||||||
|
bool TargetWithComboPointsLowerHealTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target || !target->IsAlive() || !target->IsInWorld())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ComboPointsAvailableTrigger::IsActive() &&
|
||||||
|
(target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime;
|
||||||
|
}
|
||||||
|
|
||||||
bool LoseAggroTrigger::IsActive() { return !AI_VALUE2(bool, "has aggro", "current target"); }
|
bool LoseAggroTrigger::IsActive() { return !AI_VALUE2(bool, "has aggro", "current target"); }
|
||||||
|
|
||||||
bool HasAggroTrigger::IsActive() { return AI_VALUE2(bool, "has aggro", "current target"); }
|
bool HasAggroTrigger::IsActive() { return AI_VALUE2(bool, "has aggro", "current target"); }
|
||||||
@ -137,7 +152,16 @@ bool OutNumberedTrigger::IsActive()
|
|||||||
bool BuffTrigger::IsActive()
|
bool BuffTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
return SpellTrigger::IsActive() && !botAI->HasAura(spell, target, false, checkIsOwner);
|
if (!target)
|
||||||
|
return false;
|
||||||
|
if (!SpellTrigger::IsActive())
|
||||||
|
return false;
|
||||||
|
Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration);
|
||||||
|
if (!aura)
|
||||||
|
return true;
|
||||||
|
if (beforeDuration && aura->GetDuration() < beforeDuration)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()
|
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()
|
||||||
@ -171,6 +195,22 @@ bool MyAttackerCountTrigger::IsActive()
|
|||||||
return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount;
|
return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LowTankThreatTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* mt = AI_VALUE(Unit*, "main tank");
|
||||||
|
if (!mt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!current_target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ThreatMgr& mgr = current_target->GetThreatMgr();
|
||||||
|
float threat = mgr.GetThreat(bot);
|
||||||
|
float tankThreat = mgr.GetThreat(mt);
|
||||||
|
return tankThreat == 0.0f || threat > tankThreat * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
bool AoeTrigger::IsActive()
|
bool AoeTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||||
@ -221,7 +261,7 @@ bool DebuffTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime;
|
return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DebuffOnBossTrigger::IsActive()
|
bool DebuffOnBossTrigger::IsActive()
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "HealthTriggers.h"
|
#include "HealthTriggers.h"
|
||||||
#include "RangeTriggers.h"
|
#include "RangeTriggers.h"
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
class Unit;
|
class Unit;
|
||||||
@ -113,6 +114,30 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f)
|
||||||
|
: ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
bool IsActive() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float lifeTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComboPointsNotFullTrigger : public StatAvailable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ComboPointsNotFullTrigger(PlayerbotAI* botAI, int32 amount = 5, std::string const name = "combo points not full")
|
||||||
|
: StatAvailable(botAI, amount, name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class LoseAggroTrigger : public Trigger
|
class LoseAggroTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -224,6 +249,13 @@ public:
|
|||||||
MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {}
|
MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LowTankThreatTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LowTankThreatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "low tank threat") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
class AoeTrigger : public AttackerCountTrigger
|
class AoeTrigger : public AttackerCountTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -276,10 +308,12 @@ public:
|
|||||||
class BuffTrigger : public SpellTrigger
|
class BuffTrigger : public SpellTrigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false)
|
BuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, bool checkDuration = false, uint32 beforeDuration = 0)
|
||||||
: SpellTrigger(botAI, spell, checkInterval)
|
: SpellTrigger(botAI, spell, checkInterval)
|
||||||
{
|
{
|
||||||
this->checkIsOwner = checkIsOwner;
|
this->checkIsOwner = checkIsOwner;
|
||||||
|
this->checkDuration = checkDuration;
|
||||||
|
this->beforeDuration = beforeDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -288,6 +322,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool checkIsOwner;
|
bool checkIsOwner;
|
||||||
|
bool checkDuration;
|
||||||
|
uint32 beforeDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BuffOnPartyTrigger : public BuffTrigger
|
class BuffOnPartyTrigger : public BuffTrigger
|
||||||
@ -347,8 +383,8 @@ class DebuffTrigger : public BuffTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false,
|
DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false,
|
||||||
float needLifeTime = 8.0f)
|
float needLifeTime = 8.0f, uint32 beforeDuration = 0)
|
||||||
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner), needLifeTime(needLifeTime)
|
: BuffTrigger(botAI, spell, checkInterval, checkIsOwner, false, beforeDuration), needLifeTime(needLifeTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,6 +64,7 @@ public:
|
|||||||
creators["light energy available"] = &TriggerContext::LightEnergyAvailable;
|
creators["light energy available"] = &TriggerContext::LightEnergyAvailable;
|
||||||
creators["medium energy available"] = &TriggerContext::MediumEnergyAvailable;
|
creators["medium energy available"] = &TriggerContext::MediumEnergyAvailable;
|
||||||
creators["high energy available"] = &TriggerContext::HighEnergyAvailable;
|
creators["high energy available"] = &TriggerContext::HighEnergyAvailable;
|
||||||
|
creators["almost full energy available"] = &TriggerContext::AlmostFullEnergyAvailable;
|
||||||
|
|
||||||
creators["loot available"] = &TriggerContext::LootAvailable;
|
creators["loot available"] = &TriggerContext::LootAvailable;
|
||||||
creators["no attackers"] = &TriggerContext::NoAttackers;
|
creators["no attackers"] = &TriggerContext::NoAttackers;
|
||||||
@ -96,8 +97,12 @@ public:
|
|||||||
|
|
||||||
creators["combo points available"] = &TriggerContext::ComboPointsAvailable;
|
creators["combo points available"] = &TriggerContext::ComboPointsAvailable;
|
||||||
creators["combo points 3 available"] = &TriggerContext::ComboPoints3Available;
|
creators["combo points 3 available"] = &TriggerContext::ComboPoints3Available;
|
||||||
|
creators["target with combo points almost dead"] = &TriggerContext::target_with_combo_points_almost_dead;
|
||||||
|
creators["combo points not full"] = &TriggerContext::ComboPointsNotFull;
|
||||||
|
creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy;
|
||||||
|
|
||||||
creators["medium threat"] = &TriggerContext::MediumThreat;
|
creators["medium threat"] = &TriggerContext::MediumThreat;
|
||||||
|
creators["low tank threat"] = &TriggerContext::low_tank_threat;
|
||||||
|
|
||||||
creators["dead"] = &TriggerContext::Dead;
|
creators["dead"] = &TriggerContext::Dead;
|
||||||
creators["corpse near"] = &TriggerContext::corpse_near;
|
creators["corpse near"] = &TriggerContext::corpse_near;
|
||||||
@ -279,6 +284,7 @@ private:
|
|||||||
static Trigger* LightEnergyAvailable(PlayerbotAI* botAI) { return new LightEnergyAvailableTrigger(botAI); }
|
static Trigger* LightEnergyAvailable(PlayerbotAI* botAI) { return new LightEnergyAvailableTrigger(botAI); }
|
||||||
static Trigger* MediumEnergyAvailable(PlayerbotAI* botAI) { return new MediumEnergyAvailableTrigger(botAI); }
|
static Trigger* MediumEnergyAvailable(PlayerbotAI* botAI) { return new MediumEnergyAvailableTrigger(botAI); }
|
||||||
static Trigger* HighEnergyAvailable(PlayerbotAI* botAI) { return new HighEnergyAvailableTrigger(botAI); }
|
static Trigger* HighEnergyAvailable(PlayerbotAI* botAI) { return new HighEnergyAvailableTrigger(botAI); }
|
||||||
|
static Trigger* AlmostFullEnergyAvailable(PlayerbotAI* botAI) { return new EnergyAvailable(botAI, 90); }
|
||||||
static Trigger* LootAvailable(PlayerbotAI* botAI) { return new LootAvailableTrigger(botAI); }
|
static Trigger* LootAvailable(PlayerbotAI* botAI) { return new LootAvailableTrigger(botAI); }
|
||||||
static Trigger* NoAttackers(PlayerbotAI* botAI) { return new NoAttackersTrigger(botAI); }
|
static Trigger* NoAttackers(PlayerbotAI* botAI) { return new NoAttackersTrigger(botAI); }
|
||||||
static Trigger* TankAssist(PlayerbotAI* botAI) { return new TankAssistTrigger(botAI); }
|
static Trigger* TankAssist(PlayerbotAI* botAI) { return new TankAssistTrigger(botAI); }
|
||||||
@ -309,7 +315,15 @@ private:
|
|||||||
}
|
}
|
||||||
static Trigger* ComboPointsAvailable(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI); }
|
static Trigger* ComboPointsAvailable(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI); }
|
||||||
static Trigger* ComboPoints3Available(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI, 3); }
|
static Trigger* ComboPoints3Available(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI, 3); }
|
||||||
|
static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai)
|
||||||
|
{
|
||||||
|
return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f);
|
||||||
|
}
|
||||||
|
static Trigger* ComboPointsNotFull(PlayerbotAI* botAI) { return new ComboPointsNotFullTrigger(botAI); }
|
||||||
|
static Trigger* ComboPointsNotFullAndHighEnergy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "combo points not full", "high energy available"); }
|
||||||
static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }
|
static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }
|
||||||
|
static Trigger* low_tank_threat(PlayerbotAI* botAI) { return new LowTankThreatTrigger(botAI); }
|
||||||
|
// static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }
|
||||||
static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); }
|
static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); }
|
||||||
static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); }
|
static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); }
|
||||||
static Trigger* PartyMemberDead(PlayerbotAI* botAI) { return new PartyMemberDeadTrigger(botAI); }
|
static Trigger* PartyMemberDead(PlayerbotAI* botAI) { return new PartyMemberDeadTrigger(botAI); }
|
||||||
|
|||||||
@ -145,6 +145,9 @@ Aura* AreaDebuffValue::Calculate()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// float radius = dynOwner->GetRadius();
|
||||||
|
// if (radius > 12.0f)
|
||||||
|
// continue;
|
||||||
return aura;
|
return aura;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,14 +46,14 @@ WorldLocation ArrowFormation::GetLocationInternal()
|
|||||||
|
|
||||||
float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
||||||
float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
||||||
float z = master->GetPositionZ();
|
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||||
master->GetPositionZ(), x, y, z))
|
master->GetPositionZ(), x, y, z))
|
||||||
{
|
{
|
||||||
x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
||||||
y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
||||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = master->GetMapHeight(x, y, z);
|
master->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
return WorldLocation(master->GetMapId(), x, y, z);
|
return WorldLocation(master->GetMapId(), x, y, z);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,3 +36,36 @@ Unit* AttackerWithoutAuraTargetValue::Calculate()
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Unit* MeleeAttackerWithoutAuraTargetValue::Calculate()
|
||||||
|
{
|
||||||
|
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
||||||
|
// Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||||
|
uint32 max_health = 0;
|
||||||
|
Unit* result = nullptr;
|
||||||
|
for (ObjectGuid const guid : attackers)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
|
if (!unit || !unit->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!bot->IsWithinMeleeRange(unit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (unit->GetHealth() < max_health)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!botAI->HasAura(qualifier, unit, false, true))
|
||||||
|
{
|
||||||
|
max_health = unit->GetHealth();
|
||||||
|
result = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -28,7 +28,9 @@ protected:
|
|||||||
class MeleeAttackerWithoutAuraTargetValue : public AttackerWithoutAuraTargetValue
|
class MeleeAttackerWithoutAuraTargetValue : public AttackerWithoutAuraTargetValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI) : AttackerWithoutAuraTargetValue(botAI, "melee") {}
|
MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI, bool checkArc = true) : AttackerWithoutAuraTargetValue(botAI, "melee"), checkArc(checkArc) {}
|
||||||
|
Unit* Calculate() override;
|
||||||
|
bool checkArc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -50,6 +50,7 @@ public:
|
|||||||
CasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
|
CasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
|
||||||
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
|
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
|
||||||
{
|
{
|
||||||
|
result = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||||
@ -86,13 +87,15 @@ public:
|
|||||||
{
|
{
|
||||||
float new_time = new_unit->GetHealth() / dps_;
|
float new_time = new_unit->GetHealth() / dps_;
|
||||||
float old_time = old_unit->GetHealth() / dps_;
|
float old_time = old_unit->GetHealth() / dps_;
|
||||||
// [5-20] > (5-0] > (20-inf)
|
// [5-30] > (5-0] > (20-inf)
|
||||||
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
|
int new_level = GetIntervalLevel(new_unit);
|
||||||
|
int old_level = GetIntervalLevel(old_unit);
|
||||||
|
if (new_level != old_level)
|
||||||
{
|
{
|
||||||
return true;
|
return new_level > old_level;
|
||||||
}
|
}
|
||||||
int32_t level = GetIntervalLevel(new_unit);
|
int32_t level = new_level;
|
||||||
if (level % 10 == 2 || level % 10 == 1)
|
if (level % 10 == 2 || level % 10 == 0)
|
||||||
{
|
{
|
||||||
return new_time < old_time;
|
return new_time < old_time;
|
||||||
}
|
}
|
||||||
@ -116,16 +119,16 @@ public:
|
|||||||
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
|
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
|
||||||
attackRange += 5.0f;
|
attackRange += 5.0f;
|
||||||
int level = dis < attackRange ? 10 : 0;
|
int level = dis < attackRange ? 10 : 0;
|
||||||
if (time >= 3 && time <= 20)
|
if (time >= 5 && time <= 30)
|
||||||
{
|
{
|
||||||
return level + 2;
|
return level + 2;
|
||||||
}
|
}
|
||||||
if (time > 20)
|
if (time > 30)
|
||||||
{
|
{
|
||||||
return level + 1;
|
|
||||||
}
|
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
return level + 1;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float dps_;
|
float dps_;
|
||||||
@ -176,12 +179,14 @@ public:
|
|||||||
float new_time = new_unit->GetHealth() / dps_;
|
float new_time = new_unit->GetHealth() / dps_;
|
||||||
float old_time = old_unit->GetHealth() / dps_;
|
float old_time = old_unit->GetHealth() / dps_;
|
||||||
// [5-20] > (5-0] > (20-inf)
|
// [5-20] > (5-0] > (20-inf)
|
||||||
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
|
int new_level = GetIntervalLevel(new_unit);
|
||||||
|
int old_level = GetIntervalLevel(old_unit);
|
||||||
|
if (new_level != old_level)
|
||||||
{
|
{
|
||||||
return true;
|
return new_level > old_level;
|
||||||
}
|
}
|
||||||
// attack enemy in range and with lowest health
|
// attack enemy in range and with lowest health
|
||||||
int level = GetIntervalLevel(new_unit);
|
int level = new_level;
|
||||||
if (level == 10)
|
if (level == 10)
|
||||||
{
|
{
|
||||||
return new_time < old_time;
|
return new_time < old_time;
|
||||||
@ -249,12 +254,14 @@ public:
|
|||||||
float new_time = new_unit->GetHealth() / dps_;
|
float new_time = new_unit->GetHealth() / dps_;
|
||||||
float old_time = old_unit->GetHealth() / dps_;
|
float old_time = old_unit->GetHealth() / dps_;
|
||||||
// [5-20] > (5-0] > (20-inf)
|
// [5-20] > (5-0] > (20-inf)
|
||||||
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit))
|
int new_level = GetIntervalLevel(new_unit);
|
||||||
|
int old_level = GetIntervalLevel(old_unit);
|
||||||
|
if (new_level != old_level)
|
||||||
{
|
{
|
||||||
return true;
|
return new_level > old_level;
|
||||||
}
|
}
|
||||||
// attack enemy in range and with lowest health
|
// attack enemy in range and with lowest health
|
||||||
int level = GetIntervalLevel(new_unit);
|
int level = new_level;
|
||||||
Player* bot = botAI->GetBot();
|
Player* bot = botAI->GetBot();
|
||||||
if (level == 10)
|
if (level == 10)
|
||||||
{
|
{
|
||||||
@ -291,7 +298,7 @@ Unit* DpsTargetValue::Calculate()
|
|||||||
return rti;
|
return rti;
|
||||||
|
|
||||||
// FindLeastHpTargetStrategy strategy(botAI);
|
// FindLeastHpTargetStrategy strategy(botAI);
|
||||||
float dps = AI_VALUE(float, "expected group dps");
|
float dps = AI_VALUE(float, "estimated group dps");
|
||||||
if (botAI->IsCaster(bot))
|
if (botAI->IsCaster(bot))
|
||||||
{
|
{
|
||||||
CasterFindTargetSmartStrategy strategy(botAI, dps);
|
CasterFindTargetSmartStrategy strategy(botAI, dps);
|
||||||
|
|||||||
145
src/strategy/values/EstimatedLifetimeValue.cpp
Normal file
145
src/strategy/values/EstimatedLifetimeValue.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include "EstimatedLifetimeValue.h"
|
||||||
|
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "PlayerbotFactory.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
|
||||||
|
float EstimatedLifetimeValue::Calculate()
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||||
|
if (!target || !target->IsAlive())
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
float dps = AI_VALUE(float, "estimated group dps");
|
||||||
|
bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3;
|
||||||
|
if (aoePenalty)
|
||||||
|
dps *= 0.75;
|
||||||
|
float res = target->GetHealth() / dps;
|
||||||
|
// bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
float EstimatedGroupDpsValue::Calculate()
|
||||||
|
{
|
||||||
|
float totalDps;
|
||||||
|
|
||||||
|
std::vector<Player*> groupPlayer = {bot};
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
|
{
|
||||||
|
Player* member = gref->GetSource();
|
||||||
|
if (member == bot) // calculated
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!member || !member->IsInWorld())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->GetMapId() != bot->GetMapId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
groupPlayer.push_back(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Player* player : groupPlayer)
|
||||||
|
{
|
||||||
|
float roleMultiplier;
|
||||||
|
if (botAI->IsTank(player))
|
||||||
|
roleMultiplier = 0.3f;
|
||||||
|
else if (botAI->IsHeal(player))
|
||||||
|
roleMultiplier = 0.1f;
|
||||||
|
else
|
||||||
|
roleMultiplier = 1.0f;
|
||||||
|
float basicDps = GetBasicDps(player->GetLevel());
|
||||||
|
float basicGs = GetBasicGs(player->GetLevel());
|
||||||
|
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(player, true, false, 12);
|
||||||
|
float gs_modifier = (float)mixedGearScore / basicGs;
|
||||||
|
// bonus for wotlk epic gear
|
||||||
|
if (mixedGearScore >= 300)
|
||||||
|
{
|
||||||
|
gs_modifier *= 1 + (mixedGearScore - 300) * 0.01;
|
||||||
|
}
|
||||||
|
if (gs_modifier < 0.75)
|
||||||
|
gs_modifier = 0.75;
|
||||||
|
if (gs_modifier > 4)
|
||||||
|
gs_modifier = 4;
|
||||||
|
totalDps += basicDps * roleMultiplier * gs_modifier;
|
||||||
|
}
|
||||||
|
// Group buff bonus
|
||||||
|
if (groupPlayer.size() >= 25)
|
||||||
|
totalDps *= 1.2;
|
||||||
|
else if (groupPlayer.size() >= 10)
|
||||||
|
totalDps *= 1.1;
|
||||||
|
else if (groupPlayer.size() >= 5)
|
||||||
|
totalDps *= 1.05;
|
||||||
|
return totalDps;
|
||||||
|
}
|
||||||
|
|
||||||
|
float EstimatedGroupDpsValue::GetBasicDps(uint32 level)
|
||||||
|
{
|
||||||
|
float basic_dps;
|
||||||
|
|
||||||
|
if (level <= 15)
|
||||||
|
{
|
||||||
|
basic_dps = 5 + level * 1;
|
||||||
|
}
|
||||||
|
else if (level <= 25)
|
||||||
|
{
|
||||||
|
basic_dps = 20 + (level - 15) * 2;
|
||||||
|
}
|
||||||
|
else if (level <= 45)
|
||||||
|
{
|
||||||
|
basic_dps = 40 + (level - 25) * 3;
|
||||||
|
}
|
||||||
|
else if (level <= 55)
|
||||||
|
{
|
||||||
|
basic_dps = 100 + (level - 45) * 20;
|
||||||
|
}
|
||||||
|
else if (level <= 60)
|
||||||
|
{
|
||||||
|
basic_dps = 300 + (level - 55) * 50;
|
||||||
|
}
|
||||||
|
else if (level <= 70)
|
||||||
|
{
|
||||||
|
basic_dps = 550 + (level - 60) * 65;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
basic_dps = 1200 + (level - 70) * 200;
|
||||||
|
}
|
||||||
|
return basic_dps;
|
||||||
|
}
|
||||||
|
|
||||||
|
float EstimatedGroupDpsValue::GetBasicGs(uint32 level)
|
||||||
|
{
|
||||||
|
float basic_gs;
|
||||||
|
|
||||||
|
if (level <= 8)
|
||||||
|
{
|
||||||
|
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_NORMAL);
|
||||||
|
}
|
||||||
|
else if (level <= 15)
|
||||||
|
{
|
||||||
|
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_UNCOMMON);
|
||||||
|
}
|
||||||
|
else if (level <= 60)
|
||||||
|
{
|
||||||
|
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_RARE);
|
||||||
|
}
|
||||||
|
else if (level <= 70)
|
||||||
|
{
|
||||||
|
basic_gs = PlayerbotFactory::CalcMixedGearScore(85 + (level - 60) * 3, ITEM_QUALITY_RARE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
basic_gs = PlayerbotFactory::CalcMixedGearScore(155 + (level - 70) * 4, ITEM_QUALITY_RARE);
|
||||||
|
}
|
||||||
|
return basic_gs;
|
||||||
|
}
|
||||||
@ -3,8 +3,8 @@
|
|||||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H
|
#ifndef _PLAYERBOT_EstimatedLifetimeValue_H
|
||||||
#define _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H
|
#define _PLAYERBOT_EstimatedLifetimeValue_H
|
||||||
|
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "PossibleTargetsValue.h"
|
#include "PossibleTargetsValue.h"
|
||||||
@ -15,22 +15,26 @@ class PlayerbotAI;
|
|||||||
class Unit;
|
class Unit;
|
||||||
|
|
||||||
// [target health] / [expected group single target dps] = [expected lifetime]
|
// [target health] / [expected group single target dps] = [expected lifetime]
|
||||||
class ExpectedLifetimeValue : public FloatCalculatedValue, public Qualified
|
class EstimatedLifetimeValue : public FloatCalculatedValue, public Qualified
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExpectedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected lifetime") {}
|
EstimatedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated lifetime") {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float Calculate() override;
|
float Calculate() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExpectedGroupDpsValue : public FloatCalculatedValue
|
class EstimatedGroupDpsValue : public FloatCalculatedValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExpectedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected group dps", 20 * 1000) {}
|
EstimatedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated group dps", 20 * 1000) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float Calculate() override;
|
float Calculate() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float GetBasicDps(uint32 level);
|
||||||
|
float GetBasicGs(uint32 level);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,99 +0,0 @@
|
|||||||
#include "ExpectedLifetimeValue.h"
|
|
||||||
|
|
||||||
#include "AiFactory.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "SharedDefines.h"
|
|
||||||
|
|
||||||
float ExpectedLifetimeValue::Calculate()
|
|
||||||
{
|
|
||||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
|
||||||
if (!target || !target->IsAlive())
|
|
||||||
{
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
float dps = AI_VALUE(float, "expected group dps");
|
|
||||||
float res = target->GetHealth() / dps;
|
|
||||||
// bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ExpectedGroupDpsValue::Calculate()
|
|
||||||
{
|
|
||||||
float dps_num;
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
dps_num = 0.7;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dps_num = group->GetMembersCount() * 0.7;
|
|
||||||
}
|
|
||||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, true, false, 12);
|
|
||||||
// efficiency record based on rare gear level, is there better calculation method?
|
|
||||||
// float dps_efficiency = 1;
|
|
||||||
float basic_dps;
|
|
||||||
int32 basic_gs;
|
|
||||||
int32 level = bot->GetLevel();
|
|
||||||
|
|
||||||
if (level <= 15)
|
|
||||||
{
|
|
||||||
basic_dps = 5 + level * 1;
|
|
||||||
}
|
|
||||||
else if (level <= 25)
|
|
||||||
{
|
|
||||||
basic_dps = 20 + (level - 15) * 2;
|
|
||||||
}
|
|
||||||
else if (level <= 40)
|
|
||||||
{
|
|
||||||
basic_dps = 40 + (level - 30) * 4;
|
|
||||||
}
|
|
||||||
else if (level <= 55)
|
|
||||||
{
|
|
||||||
basic_dps = 100 + (level - 45) * 20;
|
|
||||||
}
|
|
||||||
else if (level <= 60)
|
|
||||||
{
|
|
||||||
basic_dps = 300 + (level - 55) * 50;
|
|
||||||
}
|
|
||||||
else if (level <= 70)
|
|
||||||
{
|
|
||||||
basic_dps = 450 + (level - 60) * 40;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
basic_dps = 750 + (level - 70) * 175;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level <= 8)
|
|
||||||
{
|
|
||||||
basic_gs = (level + 5) * 2;
|
|
||||||
}
|
|
||||||
else if (level <= 15)
|
|
||||||
{
|
|
||||||
basic_gs = (level + 5) * 3;
|
|
||||||
}
|
|
||||||
else if (level <= 60)
|
|
||||||
{
|
|
||||||
basic_gs = (level + 5) * 4;
|
|
||||||
}
|
|
||||||
else if (level <= 70)
|
|
||||||
{
|
|
||||||
basic_gs = (85 + (level - 60) * 3) * 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
basic_gs = (155 + (level - 70) * 4) * 4;
|
|
||||||
}
|
|
||||||
float gap = mixedGearScore - basic_gs;
|
|
||||||
float gs_modifier = (float)mixedGearScore / basic_gs - 1;
|
|
||||||
gs_modifier = gs_modifier * 3 + 1;
|
|
||||||
|
|
||||||
if (gs_modifier < 0.75)
|
|
||||||
gs_modifier = 0.75;
|
|
||||||
if (gs_modifier > 4)
|
|
||||||
gs_modifier = 4;
|
|
||||||
|
|
||||||
return dps_num * basic_dps * gs_modifier;
|
|
||||||
}
|
|
||||||
@ -88,7 +88,7 @@ public:
|
|||||||
float angle = GetFollowAngle();
|
float angle = GetFollowAngle();
|
||||||
float x = master->GetPositionX() + cos(angle) * range;
|
float x = master->GetPositionX() + cos(angle) * range;
|
||||||
float y = master->GetPositionY() + sin(angle) * range;
|
float y = master->GetPositionY() + sin(angle) * range;
|
||||||
float z = master->GetPositionZ();
|
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||||
master->GetPositionZ(), x, y, z))
|
master->GetPositionZ(), x, y, z))
|
||||||
{
|
{
|
||||||
@ -138,15 +138,14 @@ public:
|
|||||||
|
|
||||||
float x = master->GetPositionX() + cos(angle) * range + dx;
|
float x = master->GetPositionX() + cos(angle) * range + dx;
|
||||||
float y = master->GetPositionY() + sin(angle) * range + dy;
|
float y = master->GetPositionY() + sin(angle) * range + dy;
|
||||||
float z = master->GetPositionZ();
|
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = bot->GetMapHeight(x, y, z + 5.0f);
|
|
||||||
if (!master->GetMap()->CheckCollisionAndGetValidCoords(
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(
|
||||||
master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z))
|
master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), x, y, z))
|
||||||
{
|
{
|
||||||
x = master->GetPositionX() + cos(angle) * range + dx;
|
x = master->GetPositionX() + cos(angle) * range + dx;
|
||||||
y = master->GetPositionY() + sin(angle) * range + dy;
|
y = master->GetPositionY() + sin(angle) * range + dy;
|
||||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = master->GetMapHeight(x, y, z);
|
master->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
// bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
// bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
// bot->GetPositionZ(), x, y, z);
|
// bot->GetPositionZ(), x, y, z);
|
||||||
@ -155,15 +154,14 @@ public:
|
|||||||
|
|
||||||
float x = master->GetPositionX() + cos(angle) * range + dx;
|
float x = master->GetPositionX() + cos(angle) * range + dx;
|
||||||
float y = master->GetPositionY() + sin(angle) * range + dy;
|
float y = master->GetPositionY() + sin(angle) * range + dy;
|
||||||
float z = master->GetPositionZ();
|
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = bot->GetMapHeight(x, y, z + 5.0f);
|
|
||||||
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||||
master->GetPositionZ(), x, y, z))
|
master->GetPositionZ(), x, y, z))
|
||||||
{
|
{
|
||||||
x = master->GetPositionX() + cos(angle) * range + dx;
|
x = master->GetPositionX() + cos(angle) * range + dx;
|
||||||
y = master->GetPositionY() + sin(angle) * range + dy;
|
y = master->GetPositionY() + sin(angle) * range + dy;
|
||||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = master->GetMapHeight(x, y, z);
|
master->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
return WorldLocation(master->GetMapId(), x, y, z);
|
return WorldLocation(master->GetMapId(), x, y, z);
|
||||||
}
|
}
|
||||||
@ -221,8 +219,8 @@ public:
|
|||||||
{
|
{
|
||||||
x = target->GetPositionX() + cos(angle) * range;
|
x = target->GetPositionX() + cos(angle) * range;
|
||||||
y = target->GetPositionY() + sin(angle) * range;
|
y = target->GetPositionY() + sin(angle) * range;
|
||||||
z = target->GetPositionZ() + target->GetHoverHeight();
|
z = target->GetPositionZ();
|
||||||
z = target->GetMapHeight(x, y, z);
|
target->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
return WorldLocation(bot->GetMapId(), x, y, z);
|
return WorldLocation(bot->GetMapId(), x, y, z);
|
||||||
}
|
}
|
||||||
@ -389,7 +387,7 @@ public:
|
|||||||
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
||||||
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
||||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = master->GetMapHeight(x, y, z);
|
master->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
return WorldLocation(bot->GetMapId(), minX, minY, z);
|
return WorldLocation(bot->GetMapId(), minX, minY, z);
|
||||||
}
|
}
|
||||||
@ -403,7 +401,7 @@ public:
|
|||||||
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
||||||
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
||||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||||
z = master->GetMapHeight(x, y, z);
|
master->UpdateAllowedPositionZ(x, y, z);
|
||||||
}
|
}
|
||||||
return WorldLocation(bot->GetMapId(), x, y, z);
|
return WorldLocation(bot->GetMapId(), x, y, z);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -166,7 +166,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
|
|||||||
return ITEM_USAGE_NONE;
|
return ITEM_USAGE_NONE;
|
||||||
|
|
||||||
uint16 dest;
|
uint16 dest;
|
||||||
InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, false);
|
InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true);
|
||||||
pItem->RemoveFromUpdateQueueOf(bot);
|
pItem->RemoveFromUpdateQueueOf(bot);
|
||||||
delete pItem;
|
delete pItem;
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,7 @@ GuidVector NearestTrapWithDamageValue::Calculate()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
if (spellInfo->IsPositive())
|
if (!spellInfo || spellInfo->IsPositive())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
#include "DuelTargetValue.h"
|
#include "DuelTargetValue.h"
|
||||||
#include "EnemyHealerTargetValue.h"
|
#include "EnemyHealerTargetValue.h"
|
||||||
#include "EnemyPlayerValue.h"
|
#include "EnemyPlayerValue.h"
|
||||||
#include "ExpectedLifetimeValue.h"
|
#include "EstimatedLifetimeValue.h"
|
||||||
#include "Formations.h"
|
#include "Formations.h"
|
||||||
#include "GrindTargetValue.h"
|
#include "GrindTargetValue.h"
|
||||||
#include "GroupValues.h"
|
#include "GroupValues.h"
|
||||||
@ -299,8 +299,8 @@ public:
|
|||||||
creators["boss target"] = &ValueContext::boss_target;
|
creators["boss target"] = &ValueContext::boss_target;
|
||||||
creators["nearest triggers"] = &ValueContext::nearest_triggers;
|
creators["nearest triggers"] = &ValueContext::nearest_triggers;
|
||||||
creators["neglect threat"] = &ValueContext::neglect_threat;
|
creators["neglect threat"] = &ValueContext::neglect_threat;
|
||||||
creators["expected lifetime"] = &ValueContext::expected_lifetime;
|
creators["estimated lifetime"] = &ValueContext::expected_lifetime;
|
||||||
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
creators["estimated group dps"] = &ValueContext::expected_group_dps;
|
||||||
creators["area debuff"] = &ValueContext::area_debuff;
|
creators["area debuff"] = &ValueContext::area_debuff;
|
||||||
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
||||||
creators["disperse distance"] = &ValueContext::disperse_distance;
|
creators["disperse distance"] = &ValueContext::disperse_distance;
|
||||||
@ -538,8 +538,8 @@ private:
|
|||||||
static UntypedValue* boss_target(PlayerbotAI* ai) { return new BossTargetValue(ai); }
|
static UntypedValue* boss_target(PlayerbotAI* ai) { return new BossTargetValue(ai); }
|
||||||
static UntypedValue* nearest_triggers(PlayerbotAI* ai) { return new NearestTriggersValue(ai); }
|
static UntypedValue* nearest_triggers(PlayerbotAI* ai) { return new NearestTriggersValue(ai); }
|
||||||
static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); }
|
static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); }
|
||||||
static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); }
|
static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new EstimatedLifetimeValue(ai); }
|
||||||
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new EstimatedGroupDpsValue(ai); }
|
||||||
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
||||||
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
||||||
static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); }
|
static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user