mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
commit
e87bca128b
@ -651,9 +651,14 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
|
|||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# PROFESSIONS
|
# PROFESSIONS
|
||||||
# Note: Random bots currently do not get professions
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Percentage of randombots in each class bucket that receive a class-matching
|
||||||
|
# weighted profession combination. The remaining randombots use the weighted
|
||||||
|
# random sane-pair profession pool.
|
||||||
|
# Default: 30
|
||||||
|
AiPlayerbot.ClassMatchingProfessionChance = 30
|
||||||
|
|
||||||
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
|
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
|
||||||
# Default: 1 (Enabled)
|
# Default: 1 (Enabled)
|
||||||
AiPlayerbot.EnableFishingWithMaster = 1
|
AiPlayerbot.EnableFishingWithMaster = 1
|
||||||
@ -1319,7 +1324,7 @@ AiPlayerbot.DeleteRandomBotArenaTeams = 0
|
|||||||
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
|
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
|
||||||
|
|
||||||
# PvP Restricted Areas (bots don't pvp)
|
# PvP Restricted Areas (bots don't pvp)
|
||||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
|
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973,4085,4086,4087,4088"
|
||||||
|
|
||||||
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
||||||
AiPlayerbot.FastReactInBG = 1
|
AiPlayerbot.FastReactInBG = 1
|
||||||
@ -1797,10 +1802,10 @@ AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251
|
|||||||
# Requires sending the command "nc +worldbuff" in chat to a bot (or a group of bots) to enable
|
# Requires sending the command "nc +worldbuff" in chat to a bot (or a group of bots) to enable
|
||||||
# Each entry in the matrix should be formatted as follows: Entry:FactionID,ClassID,SpecID,MinimumLevel,MaximumLevel:SpellID1,SpellID2,etc.;
|
# Each entry in the matrix should be formatted as follows: Entry:FactionID,ClassID,SpecID,MinimumLevel,MaximumLevel:SpellID1,SpellID2,etc.;
|
||||||
# FactionID may be set to 0 for the entry to apply buffs to bots of either faction
|
# FactionID may be set to 0 for the entry to apply buffs to bots of either faction
|
||||||
# The default entries create a cross-faction list of level 80 buffs for each implemented pve spec from the "Premade Specs" section
|
# The default entries create a cross-faction level 60-69 Vanilla buffs, level 70-79 TBC buffs, and level 80 buffs for each implemented pve spec from the "Premade Specs" section
|
||||||
# The default entries may be deleted or modified, and new custom entries may be added
|
# The default entries may be deleted or modified, and new custom entries may be added
|
||||||
|
|
||||||
AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358
|
AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358; # WARRIOR ARMS TBC 33:0,1,0,70,79:28520,33256; # WARRIOR FURY TBC 34:0,1,1,70,79:28520,33256; # WARRIOR PROTECTION TBC 35:0,1,2,70,79:28518,33257; # PALADIN HOLY TBC 36:0,2,0,70,79:28491,39627,33263; # PALADIN PROTECTION TBC 37:0,2,1,70,79:28518,33257; # PALADIN RETRIBUTION TBC 38:0,2,2,70,79:28520,33256; # HUNTER BEAST TBC 39:0,3,0,70,79:28520,33261; # HUNTER MARKSMANSHIP TBC 40:0,3,1,70,79:28520,33261; # HUNTER SURVIVAL TBC 41:0,3,2,70,79:28520,33261; # ROGUE ASSASSINATION TBC 42:0,4,0,70,79:28520,33261; # ROGUE COMBAT TBC 43:0,4,1,70,79:28520,33261; # ROGUE SUBTLETY TBC 44:0,4,2,70,79:28520,33261; # PRIEST DISCIPLINE TBC 45:0,5,0,70,79:28491,39627,33263; # PRIEST HOLY TBC 46:0,5,1,70,79:28491,39627,33263; # PRIEST SHADOW TBC 47:0,5,2,70,79:28540,33263; # SHAMAN ELEMENTAL TBC 48:0,7,0,70,79:28521,33263; # SHAMAN ENHANCEMENT TBC 49:0,7,1,70,79:28520,33261; # SHAMAN RESTORATION TBC 50:0,7,2,70,79:28491,39627,33263; # MAGE ARCANE TBC 51:0,8,0,70,79:28521,33263; # MAGE FIRE TBC 52:0,8,1,70,79:28540,33263; # MAGE FROST TBC 53:0,8,2,70,79:28540,33263; # WARLOCK AFFLICTION TBC 54:0,9,0,70,79:28540,33263; # WARLOCK DEMONOLOGY TBC 55:0,9,1,70,79:28540,33263; # WARLOCK DESTRUCTION TBC 56:0,9,2,70,79:28540,33263; # DRUID BALANCE TBC 57:0,11,0,70,79:28521,33263; # DRUID FERAL BEAR TBC 58:0,11,1,70,79:28518,33257; # DRUID RESTORATION TBC 59:0,11,2,70,79:28491,39627,33263; # DRUID FERAL CAT TBC 60:0,11,3,70,79:28520,33261; # WARRIOR ARMS VANILLA 61:0,1,0,60,69:17538,24799; # WARRIOR FURY VANILLA 62:0,1,1,60,69:17538,24799; # WARRIOR PROTECTION VANILLA 63:0,1,2,60,69:17626,25661; # PALADIN HOLY VANILLA 64:0,2,0,60,69:17627,18194; # PALADIN PROTECTION VANILLA 65:0,2,1,60,69:17626,25661; # PALADIN RETRIBUTION VANILLA 66:0,2,2,60,69:17628,24799; # HUNTER BEAST VANILLA 67:0,3,0,60,69:17538,18192; # HUNTER MARKSMANSHIP VANILLA 68:0,3,1,60,69:17538,18192; # HUNTER SURVIVAL VANILLA 69:0,3,2,60,69:17538,18192; # ROGUE ASSASSINATION VANILLA 70:0,4,0,60,69:17538,18192; # ROGUE COMBAT VANILLA 71:0,4,1,60,69:17538,18192; # ROGUE SUBTLETY VANILLA 72:0,4,2,60,69:17538,18192; # PRIEST DISCIPLINE VANILLA 73:0,5,0,60,69:17628,18194; # PRIEST HOLY VANILLA 74:0,5,1,60,69:17627,18194; # PRIEST SHADOW VANILLA 75:0,5,2,60,69:17628,18194; # SHAMAN ELEMENTAL VANILLA 76:0,7,0,60,69:17628,18194; # SHAMAN ENHANCEMENT VANILLA 77:0,7,1,60,69:17538,24799; # SHAMAN RESTORATION VANILLA 78:0,7,2,60,69:17627,18194; # MAGE ARCANE VANILLA 79:0,8,0,60,69:17628,18194; # MAGE FIRE VANILLA 80:0,8,1,60,69:17628,18194; # MAGE FROST VANILLA 81:0,8,2,60,69:17628,18194; # WARLOCK AFFLICTION VANILLA 82:0,9,0,60,69:17628,25661; # WARLOCK DEMONOLOGY VANILLA 83:0,9,1,60,69:17628,25661; # WARLOCK DESTRUCTION VANILLA 84:0,9,2,60,69:17628,25661; # DRUID BALANCE VANILLA 85:0,11,0,60,69:17628,18194; # DRUID FERAL BEAR VANILLA 86:0,11,1,60,69:17626,25661; # DRUID RESTORATION VANILLA 87:0,11,2,60,69:17627,18194; # DRUID FERAL CAT VANILLA 88:0,11,3,60,69:17538,24799
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|||||||
@ -0,0 +1,102 @@
|
|||||||
|
-- #########################################################
|
||||||
|
-- Playerbots - Add pull command texts
|
||||||
|
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
|
||||||
|
-- zhTW, esES, esMX, ruRU)
|
||||||
|
-- #########################################################
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN (
|
||||||
|
'pull_no_target_error',
|
||||||
|
'pull_target_too_far_error',
|
||||||
|
'pull_invalid_target_error',
|
||||||
|
'pull_action_unavailable_error'
|
||||||
|
);
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
|
||||||
|
'pull_no_target_error',
|
||||||
|
'pull_target_too_far_error',
|
||||||
|
'pull_invalid_target_error',
|
||||||
|
'pull_action_unavailable_error'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- pull_no_target_error
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1755,
|
||||||
|
'pull_no_target_error',
|
||||||
|
'You have no target',
|
||||||
|
0, 0,
|
||||||
|
'대상이 없습니다',
|
||||||
|
'Vous n''avez pas de cible',
|
||||||
|
'Du hast kein Ziel',
|
||||||
|
'你没有目标',
|
||||||
|
'你沒有目標',
|
||||||
|
'No tienes objetivo',
|
||||||
|
'No tienes objetivo',
|
||||||
|
'У вас нет цели');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_no_target_error', 100);
|
||||||
|
|
||||||
|
-- pull_target_too_far_error
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1756,
|
||||||
|
'pull_target_too_far_error',
|
||||||
|
'The target is too far away',
|
||||||
|
0, 0,
|
||||||
|
'대상이 너무 멀리 있습니다',
|
||||||
|
'La cible est trop loin',
|
||||||
|
'Das Ziel ist zu weit entfernt',
|
||||||
|
'目标太远了',
|
||||||
|
'目標太遠了',
|
||||||
|
'El objetivo está demasiado lejos',
|
||||||
|
'El objetivo está demasiado lejos',
|
||||||
|
'Цель слишком далеко');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_target_too_far_error', 100);
|
||||||
|
|
||||||
|
-- pull_invalid_target_error
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1757,
|
||||||
|
'pull_invalid_target_error',
|
||||||
|
'The target can''t be pulled',
|
||||||
|
0, 0,
|
||||||
|
'해당 대상은 풀링할 수 없습니다',
|
||||||
|
'La cible ne peut pas être attirée',
|
||||||
|
'Das Ziel kann nicht gepullt werden',
|
||||||
|
'该目标无法被拉怪',
|
||||||
|
'該目標無法被拉怪',
|
||||||
|
'No se puede hacer pull al objetivo',
|
||||||
|
'No se puede hacer pull al objetivo',
|
||||||
|
'Эту цель нельзя пуллить');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_invalid_target_error', 100);
|
||||||
|
|
||||||
|
-- pull_action_unavailable_error: %action_name is replaced with the configured pull action
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1758,
|
||||||
|
'pull_action_unavailable_error',
|
||||||
|
'Can''t perform pull action ''%action_name''',
|
||||||
|
0, 0,
|
||||||
|
'''%action_name'' 풀 액션을 수행할 수 없습니다',
|
||||||
|
'Impossible d''effectuer l''action d''engagement ''%action_name''',
|
||||||
|
'Die Pull-Aktion ''%action_name'' kann nicht ausgeführt werden',
|
||||||
|
'无法执行拉怪动作“%action_name”',
|
||||||
|
'無法執行拉怪動作「%action_name」',
|
||||||
|
'No se puede realizar la acción de pull ''%action_name''',
|
||||||
|
'No se puede realizar la acción de pull ''%action_name''',
|
||||||
|
'Невозможно выполнить действие пула ''%action_name''');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_action_unavailable_error', 100);
|
||||||
@ -45,6 +45,7 @@
|
|||||||
#include "NonCombatActions.h"
|
#include "NonCombatActions.h"
|
||||||
#include "OutfitAction.h"
|
#include "OutfitAction.h"
|
||||||
#include "PositionAction.h"
|
#include "PositionAction.h"
|
||||||
|
#include "PullActions.h"
|
||||||
#include "DropQuestAction.h"
|
#include "DropQuestAction.h"
|
||||||
#include "RandomBotUpdateAction.h"
|
#include "RandomBotUpdateAction.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
@ -105,6 +106,13 @@ public:
|
|||||||
creators["shoot"] = &ActionContext::shoot;
|
creators["shoot"] = &ActionContext::shoot;
|
||||||
creators["lifeblood"] = &ActionContext::lifeblood;
|
creators["lifeblood"] = &ActionContext::lifeblood;
|
||||||
creators["arcane torrent"] = &ActionContext::arcane_torrent;
|
creators["arcane torrent"] = &ActionContext::arcane_torrent;
|
||||||
|
creators["pull my target"] = &ActionContext::pull_my_target;
|
||||||
|
creators["pull rti target"] = &ActionContext::pull_rti_target;
|
||||||
|
creators["pull start"] = &ActionContext::pull_start;
|
||||||
|
creators["pull action"] = &ActionContext::pull_action;
|
||||||
|
creators["pull end"] = &ActionContext::pull_end;
|
||||||
|
creators["return to pull position"] = &ActionContext::return_to_pull_position;
|
||||||
|
creators["reach pull"] = &ActionContext::reach_pull;
|
||||||
creators["end pull"] = &ActionContext::end_pull;
|
creators["end pull"] = &ActionContext::end_pull;
|
||||||
creators["healthstone"] = &ActionContext::healthstone;
|
creators["healthstone"] = &ActionContext::healthstone;
|
||||||
creators["healing potion"] = &ActionContext::healing_potion;
|
creators["healing potion"] = &ActionContext::healing_potion;
|
||||||
@ -313,6 +321,13 @@ private:
|
|||||||
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
||||||
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
||||||
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
||||||
|
static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); }
|
||||||
|
static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); }
|
||||||
|
static Action* pull_start(PlayerbotAI* botAI) { return new PullStartAction(botAI); }
|
||||||
|
static Action* pull_action(PlayerbotAI* botAI) { return new PullAction(botAI); }
|
||||||
|
static Action* pull_end(PlayerbotAI* botAI) { return new PullEndAction(botAI); }
|
||||||
|
static Action* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionAction(botAI); }
|
||||||
|
static Action* reach_pull(PlayerbotAI* botAI) { return new ReachPullAction(botAI); }
|
||||||
static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); }
|
static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); }
|
||||||
static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); }
|
static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); }
|
||||||
static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); }
|
static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); }
|
||||||
|
|||||||
@ -53,22 +53,6 @@ bool AttackMyTargetAction::Execute(Event /*event*/)
|
|||||||
|
|
||||||
bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||||
{
|
{
|
||||||
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
|
||||||
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
|
||||||
|
|
||||||
bool sameTarget = oldTarget == target && bot->GetVictim() == target;
|
|
||||||
bool inCombat = botAI->GetState() == BOT_STATE_COMBAT;
|
|
||||||
bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee;
|
|
||||||
|
|
||||||
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
|
||||||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
botAI->TellError("I cannot attack in flight");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!target)
|
if (!target)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@ -85,6 +69,15 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
||||||
|
bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
botAI->TellError("I cannot attack in flight");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if bot OR target is in prohibited zone/area (skip for duels)
|
// Check if bot OR target is in prohibited zone/area (skip for duels)
|
||||||
if ((target->IsPlayer() || target->IsPet()) &&
|
if ((target->IsPlayer() || target->IsPet()) &&
|
||||||
(!bot->duel || bot->duel->Opponent != target) &&
|
(!bot->duel || bot->duel->Opponent != target) &&
|
||||||
@ -121,6 +114,13 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
||||||
|
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
||||||
|
|
||||||
|
bool sameTarget = oldTarget == target && bot->GetVictim() == target;
|
||||||
|
bool inCombat = botAI->GetState() == BOT_STATE_COMBAT;
|
||||||
|
bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee;
|
||||||
|
|
||||||
if (sameTarget && inCombat && sameAttackMode)
|
if (sameTarget && inCombat && sameAttackMode)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@ -147,7 +147,6 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
bot->SetSelection(target->GetGUID());
|
bot->SetSelection(target->GetGUID());
|
||||||
|
|
||||||
context->GetValue<Unit*>("old target")->Set(oldTarget);
|
context->GetValue<Unit*>("old target")->Set(oldTarget);
|
||||||
|
|
||||||
context->GetValue<Unit*>("current target")->Set(target);
|
context->GetValue<Unit*>("current target")->Set(target);
|
||||||
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
|
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
|
||||||
|
|
||||||
|
|||||||
@ -273,7 +273,7 @@ bool BuffOnPartyAction::Execute(Event /*event*/)
|
|||||||
}
|
}
|
||||||
// End greater buff fix
|
// End greater buff fix
|
||||||
|
|
||||||
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot")
|
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
||||||
{
|
{
|
||||||
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||||
{
|
{
|
||||||
@ -283,17 +283,40 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
|
|||||||
{
|
{
|
||||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||||
spell += " gun";
|
spell += " gun";
|
||||||
|
shootSpellId = 3018;
|
||||||
break;
|
break;
|
||||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||||
spell += " bow";
|
spell += " bow";
|
||||||
|
shootSpellId = 3018;
|
||||||
break;
|
break;
|
||||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||||
spell += " crossbow";
|
spell += " crossbow";
|
||||||
|
shootSpellId = 3018;
|
||||||
|
break;
|
||||||
|
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||||
|
spell = "throw";
|
||||||
|
shootSpellId = 2764;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CastShootAction::isPossible()
|
||||||
|
{
|
||||||
|
if (shootSpellId)
|
||||||
|
return botAI->CanCastSpell(shootSpellId, GetTarget(), false);
|
||||||
|
|
||||||
|
return CastSpellAction::isPossible();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastShootAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
if (shootSpellId)
|
||||||
|
return botAI->CastSpell(shootSpellId, GetTarget());
|
||||||
|
|
||||||
|
return botAI->CastSpell(spell, GetTarget());
|
||||||
|
}
|
||||||
|
|
||||||
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("attacker without aura", spell);
|
return context->GetValue<Unit*>("attacker without aura", spell);
|
||||||
|
|||||||
@ -253,7 +253,12 @@ class CastShootAction : public CastSpellAction
|
|||||||
public:
|
public:
|
||||||
CastShootAction(PlayerbotAI* botAI);
|
CastShootAction(PlayerbotAI* botAI);
|
||||||
|
|
||||||
|
bool isPossible() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
ActionThreatType getThreatType() override { return ActionThreatType::None; }
|
ActionThreatType getThreatType() override { return ActionThreatType::None; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 shootSpellId;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastLifeBloodAction : public CastHealingSpellAction
|
class CastLifeBloodAction : public CastHealingSpellAction
|
||||||
|
|||||||
321
src/Ai/Base/Actions/PullActions.cpp
Normal file
321
src/Ai/Base/Actions/PullActions.cpp
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AttackersValue.h"
|
||||||
|
#include "CreatureAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "PositionValue.h"
|
||||||
|
#include "PullActions.h"
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
float GetPullReachDistance(Player* bot, Unit* target, PullStrategy const* strategy)
|
||||||
|
{
|
||||||
|
if (!bot || !target || !strategy)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
float const combatDistance = bot->GetCombatReach() + target->GetCombatReach();
|
||||||
|
return std::max(0.0f, strategy->GetRange() - combatDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWithinPullRange(Player* bot, Unit* target, PullStrategy const* strategy)
|
||||||
|
{
|
||||||
|
return bot && target && strategy && bot->GetExactDist(target) <= strategy->GetRange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullRequestAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetPullTarget(event);
|
||||||
|
if (!target || !target->IsInWorld())
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pull_no_target_error", "You have no target", {});
|
||||||
|
botAI->TellError(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float const maxPullDistance = sPlayerbotAIConfig.reactDistance * 3.0f;
|
||||||
|
if (target->GetMapId() != bot->GetMapId() || bot->GetDistance(target) > maxPullDistance)
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pull_target_too_far_error", "The target is too far away", {});
|
||||||
|
botAI->TellError(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AttackersValue::IsPossibleTarget(target, bot))
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pull_invalid_target_error", "The target can't be pulled", {});
|
||||||
|
botAI->TellError(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strategy->CanDoPullAction(target))
|
||||||
|
{
|
||||||
|
std::string const actionName = strategy->GetPullActionName();
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"pull_action_unavailable_error",
|
||||||
|
"Can't perform pull action '%action_name'",
|
||||||
|
{{"%action_name", actionName}});
|
||||||
|
botAI->TellError(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo pullPosition = posMap["pull"];
|
||||||
|
pullPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId());
|
||||||
|
posMap["pull"] = pullPosition;
|
||||||
|
|
||||||
|
strategy->RequestPull(target);
|
||||||
|
context->GetValue<Unit*>("current target")->Set(target);
|
||||||
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* PullMyTargetAction::GetPullTarget(Event event)
|
||||||
|
{
|
||||||
|
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
|
||||||
|
if (event.GetSource() == "attack anything")
|
||||||
|
return botAI->GetCreature(event.getObject());
|
||||||
|
|
||||||
|
return requester ? requester->GetSelectedUnit() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* PullRtiTargetAction::GetPullTarget(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
||||||
|
if (rtiTarget)
|
||||||
|
return rtiTarget;
|
||||||
|
|
||||||
|
Group* group = bot->GetGroup();
|
||||||
|
if (!group)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::string const rti = AI_VALUE(std::string, "rti");
|
||||||
|
int32 const index = RtiTargetValue::GetRtiIndex(rti);
|
||||||
|
if (index < 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
ObjectGuid const guid = group->GetTargetIcon(index);
|
||||||
|
return guid.IsEmpty() ? nullptr : botAI->GetUnit(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullStartAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = strategy->GetTarget();
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string const preActionName = strategy->GetPreActionName();
|
||||||
|
if (!preActionName.empty() && !botAI->DoSpecificAction(preActionName, event, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Pet* pet = bot->GetPet())
|
||||||
|
{
|
||||||
|
Creature* creature = pet->ToCreature();
|
||||||
|
if (creature)
|
||||||
|
{
|
||||||
|
strategy->SetPetReactState(creature->GetReactState());
|
||||||
|
creature->SetReactState(REACT_PASSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strategy->OnPullStarted();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PullAction::PullAction(PlayerbotAI* botAI, std::string const name) : CastSpellAction(botAI, name) { InitPullAction(); }
|
||||||
|
|
||||||
|
Unit* PullAction::GetTarget()
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return strategy->GetTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<NextAction> PullAction::getPrerequisites()
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||||
|
if (!strategy || !target)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return IsWithinPullRange(bot, target, strategy) ? std::vector<NextAction>{}
|
||||||
|
: std::vector<NextAction>{ NextAction("reach pull", ACTION_MOVE) };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
InitPullAction();
|
||||||
|
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = strategy->GetTarget();
|
||||||
|
if (!target || !target->IsInWorld())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (target->IsInCombat())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsWithinPullRange(bot, target, strategy))
|
||||||
|
{
|
||||||
|
strategy->RequestPull(target, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->isMoving())
|
||||||
|
{
|
||||||
|
bot->StopMoving();
|
||||||
|
strategy->RequestPull(target, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->GetValue<Unit*>("current target")->Set(target);
|
||||||
|
if (!botAI->DoSpecificAction(strategy->GetPullActionName(), event, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullAction::isPossible()
|
||||||
|
{
|
||||||
|
InitPullAction();
|
||||||
|
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = strategy->GetTarget();
|
||||||
|
std::string const spellName = strategy->GetSpellName();
|
||||||
|
if (!target || !target->IsInWorld() || target->GetMapId() != bot->GetMapId() || spellName.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PullAction::InitPullAction()
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string const spellName = strategy->GetSpellName();
|
||||||
|
if (spellName.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
spell = spellName;
|
||||||
|
|
||||||
|
bool isShoot = (spellName == "shoot" || spellName == "shoot bow" ||
|
||||||
|
spellName == "shoot gun" || spellName == "shoot crossbow" ||
|
||||||
|
spellName == "throw");
|
||||||
|
range = botAI->GetRange(isShoot ? "shoot" : "spell");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullEndAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* pullTarget = strategy->GetTarget();
|
||||||
|
|
||||||
|
if (!strategy->HasPullStarted() && !strategy->IsPullPendingToStart() && !strategy->HasTarget())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Pet* pet = bot->GetPet())
|
||||||
|
{
|
||||||
|
Creature* creature = pet->ToCreature();
|
||||||
|
if (creature)
|
||||||
|
creature->SetReactState(strategy->GetPetReactState());
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
|
||||||
|
PositionInfo pullPosition = posMap["pull"];
|
||||||
|
if (pullPosition.isSet())
|
||||||
|
posMap.erase("pull");
|
||||||
|
|
||||||
|
if (pullTarget && context->GetValue<Unit*>("current target")->Get() == pullTarget)
|
||||||
|
context->GetValue<Unit*>("current target")->Set(nullptr);
|
||||||
|
|
||||||
|
strategy->OnPullEnded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReturnToPullPositionAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||||
|
if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return MoveTo(pullPosition.mapId, pullPosition.x, pullPosition.y, pullPosition.z,
|
||||||
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReturnToPullPositionAction::isUseful()
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||||
|
if (!strategy || !target || !target->IsInCombat())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||||
|
return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() &&
|
||||||
|
bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReachPullAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!target || !strategy)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float const reachDistance = GetPullReachDistance(bot, target, strategy);
|
||||||
|
return ReachCombatTo(target, reachDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReachPullAction::isUseful()
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||||
|
return target && !IsWithinPullRange(bot, target, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* ReachPullAction::GetTarget()
|
||||||
|
{
|
||||||
|
PullStrategy* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return strategy->GetTarget();
|
||||||
|
}
|
||||||
90
src/Ai/Base/Actions/PullActions.h
Normal file
90
src/Ai/Base/Actions/PullActions.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_PULLACTIONS_H
|
||||||
|
#define _PLAYERBOT_PULLACTIONS_H
|
||||||
|
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
|
|
||||||
|
class PullRequestAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullRequestAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Unit* GetPullTarget(Event event) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullMyTargetAction : public PullRequestAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullMyTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull my target") {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Unit* GetPullTarget(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullRtiTargetAction : public PullRequestAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullRtiTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull rti target") {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Unit* GetPullTarget(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullStartAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullStartAction(PlayerbotAI* botAI, std::string const name = "pull start") : Action(botAI, name) {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullAction : public CastSpellAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullAction(PlayerbotAI* botAI, std::string const name = "pull action");
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isPossible() override;
|
||||||
|
std::vector<NextAction> getPrerequisites() override;
|
||||||
|
Unit* GetTarget() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitPullAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullEndAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullEndAction(PlayerbotAI* botAI, std::string const name = "pull end") : Action(botAI, name) {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReachPullAction : public ReachTargetAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReachPullAction(PlayerbotAI* botAI) : ReachTargetAction(botAI, "reach pull", botAI->GetRange("spell")) {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
Unit* GetTarget() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReturnToPullPositionAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReturnToPullPositionAction(PlayerbotAI* botAI) : MovementAction(botAI, "return to pull position") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -43,6 +43,7 @@
|
|||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
#include "PassLeadershipToMasterAction.h"
|
#include "PassLeadershipToMasterAction.h"
|
||||||
#include "PositionAction.h"
|
#include "PositionAction.h"
|
||||||
|
#include "PullActions.h"
|
||||||
#include "QueryItemUsageAction.h"
|
#include "QueryItemUsageAction.h"
|
||||||
#include "QueryQuestAction.h"
|
#include "QueryQuestAction.h"
|
||||||
#include "RangeAction.h"
|
#include "RangeAction.h"
|
||||||
@ -138,6 +139,8 @@ public:
|
|||||||
creators["autogear"] = &ChatActionContext::autogear;
|
creators["autogear"] = &ChatActionContext::autogear;
|
||||||
creators["equip upgrade"] = &ChatActionContext::equip_upgrade;
|
creators["equip upgrade"] = &ChatActionContext::equip_upgrade;
|
||||||
creators["attack my target"] = &ChatActionContext::attack_my_target;
|
creators["attack my target"] = &ChatActionContext::attack_my_target;
|
||||||
|
creators["pull my target"] = &ChatActionContext::pull_my_target;
|
||||||
|
creators["pull rti target"] = &ChatActionContext::pull_rti_target;
|
||||||
creators["chat"] = &ChatActionContext::chat;
|
creators["chat"] = &ChatActionContext::chat;
|
||||||
creators["home"] = &ChatActionContext::home;
|
creators["home"] = &ChatActionContext::home;
|
||||||
creators["destroy"] = &ChatActionContext::destroy;
|
creators["destroy"] = &ChatActionContext::destroy;
|
||||||
@ -250,6 +253,8 @@ private:
|
|||||||
static Action* home(PlayerbotAI* botAI) { return new SetHomeAction(botAI); }
|
static Action* home(PlayerbotAI* botAI) { return new SetHomeAction(botAI); }
|
||||||
static Action* chat(PlayerbotAI* botAI) { return new ChangeChatAction(botAI); }
|
static Action* chat(PlayerbotAI* botAI) { return new ChangeChatAction(botAI); }
|
||||||
static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); }
|
static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); }
|
||||||
|
static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); }
|
||||||
|
static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); }
|
||||||
static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); }
|
static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); }
|
||||||
static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); }
|
static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); }
|
||||||
static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); }
|
static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); }
|
||||||
|
|||||||
@ -66,6 +66,9 @@ public:
|
|||||||
creators["autogear"] = &ChatTriggerContext::autogear;
|
creators["autogear"] = &ChatTriggerContext::autogear;
|
||||||
creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade;
|
creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade;
|
||||||
creators["attack"] = &ChatTriggerContext::attack;
|
creators["attack"] = &ChatTriggerContext::attack;
|
||||||
|
creators["pull"] = &ChatTriggerContext::pull;
|
||||||
|
creators["pull back"] = &ChatTriggerContext::pull_back;
|
||||||
|
creators["pull rti"] = &ChatTriggerContext::pull_rti;
|
||||||
creators["chat"] = &ChatTriggerContext::chat;
|
creators["chat"] = &ChatTriggerContext::chat;
|
||||||
creators["accept"] = &ChatTriggerContext::accept;
|
creators["accept"] = &ChatTriggerContext::accept;
|
||||||
creators["home"] = &ChatTriggerContext::home;
|
creators["home"] = &ChatTriggerContext::home;
|
||||||
@ -209,6 +212,9 @@ private:
|
|||||||
static Trigger* accept(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "accept"); }
|
static Trigger* accept(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "accept"); }
|
||||||
static Trigger* chat(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "chat"); }
|
static Trigger* chat(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "chat"); }
|
||||||
static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); }
|
static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); }
|
||||||
|
static Trigger* pull(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull"); }
|
||||||
|
static Trigger* pull_back(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull back"); }
|
||||||
|
static Trigger* pull_rti(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull rti"); }
|
||||||
static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); }
|
static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); }
|
||||||
static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); }
|
static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); }
|
||||||
static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); }
|
static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); }
|
||||||
|
|||||||
@ -81,6 +81,12 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
|
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("pull", { NextAction("pull my target", relevance) }));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("pull back", { NextAction("pull my target", relevance) }));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("pull rti", { NextAction("pull rti target", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
|
|||||||
@ -5,8 +5,188 @@
|
|||||||
|
|
||||||
#include "PullStrategy.h"
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
#include "PassiveMultiplier.h"
|
#include "PassiveMultiplier.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "SpellMgr.h"
|
||||||
|
|
||||||
|
class PullStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullStrategyActionNodeFactory()
|
||||||
|
{
|
||||||
|
creators["pull start"] = &pull_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ActionNode* pull_start(PlayerbotAI* /*botAI*/)
|
||||||
|
{
|
||||||
|
return new ActionNode("pull start", {}, {}, { NextAction("pull action", ACTION_NORMAL) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PullStrategy::PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction)
|
||||||
|
: Strategy(botAI), action(action), preAction(preAction)
|
||||||
|
{
|
||||||
|
actionNodeFactories.Add(new PullStrategyActionNodeFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
PullStrategy* PullStrategy::Get(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (!botAI)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (PullStrategy* strategy = dynamic_cast<PullStrategy*>(botAI->GetStrategy("pull", BOT_STATE_NON_COMBAT)))
|
||||||
|
{
|
||||||
|
if (strategy->IsPullPendingToStart() || strategy->HasPullStarted() || strategy->HasTarget())
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dynamic_cast<PullStrategy*>(botAI->GetStrategy("pull", BOT_STATE_COMBAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* PullStrategy::GetTarget() const
|
||||||
|
{
|
||||||
|
ObjectGuid const guid = botAI->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Get();
|
||||||
|
if (guid.IsEmpty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Unit* target = botAI->GetUnit(guid);
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
if (!bot || !target || !target->IsAlive() || !target->IsInWorld() ||
|
||||||
|
target->GetMapId() != bot->GetMapId())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullStrategy::HasTarget() const { return GetTarget() != nullptr; }
|
||||||
|
|
||||||
|
void PullStrategy::SetTarget(Unit* target)
|
||||||
|
{
|
||||||
|
botAI->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Set(target ? target->GetGUID() : ObjectGuid::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PullStrategy::GetPullActionName() const
|
||||||
|
{
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PullStrategy::GetSpellName() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
std::string spellName = GetPullActionName();
|
||||||
|
if (!bot || spellName != "shoot")
|
||||||
|
return spellName;
|
||||||
|
|
||||||
|
Item* equippedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||||
|
if (!equippedWeapon)
|
||||||
|
return spellName;
|
||||||
|
|
||||||
|
ItemTemplate const* itemTemplate = equippedWeapon->GetTemplate();
|
||||||
|
if (!itemTemplate)
|
||||||
|
return spellName;
|
||||||
|
|
||||||
|
switch (itemTemplate->SubClass)
|
||||||
|
{
|
||||||
|
case ITEM_SUBCLASS_WEAPON_THROWN:
|
||||||
|
return "throw";
|
||||||
|
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||||
|
return "shoot gun";
|
||||||
|
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||||
|
return "shoot bow";
|
||||||
|
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||||
|
return "shoot crossbow";
|
||||||
|
default:
|
||||||
|
return spellName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float PullStrategy::GetRange() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
std::string const spellName = GetSpellName();
|
||||||
|
if (bot && !spellName.empty())
|
||||||
|
{
|
||||||
|
uint32 const spellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", spellName)->Get();
|
||||||
|
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
|
||||||
|
return bot->GetSpellMaxRangeForTarget(GetTarget(), spellInfo) - CONTACT_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (action == "shoot" ? botAI->GetRange("shoot") : botAI->GetRange("spell")) - CONTACT_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PullStrategy::GetPreActionName() const
|
||||||
|
{
|
||||||
|
return preAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullStrategy::CanDoPullAction(Unit* target)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
if (!bot || !target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!target->IsInWorld() || target->GetMapId() != bot->GetMapId())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->getClass() != CLASS_DRUID && bot->getClass() != CLASS_PALADIN &&
|
||||||
|
GetPullActionName() == "shoot" && !bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const spellName = GetSpellName();
|
||||||
|
if (spellName.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PullStrategy::RequestPull(Unit* target, bool resetTime)
|
||||||
|
{
|
||||||
|
SetTarget(target);
|
||||||
|
pendingToStart = true;
|
||||||
|
if (resetTime)
|
||||||
|
pullStartTime = time(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PullStrategy::OnPullStarted() { pendingToStart = false; }
|
||||||
|
|
||||||
|
void PullStrategy::OnPullEnded()
|
||||||
|
{
|
||||||
|
pullStartTime = 0;
|
||||||
|
pendingToStart = false;
|
||||||
|
SetTarget(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
PullMultiplier::PullMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "pull") {}
|
||||||
|
|
||||||
|
float PullMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy || !strategy->HasTarget() || !action)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!strategy->IsPullPendingToStart() && !strategy->HasPullStarted())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
std::string const actionName = action->getName();
|
||||||
|
if (actionName == "pull my target" ||
|
||||||
|
actionName == "pull rti target" ||
|
||||||
|
actionName == "reach pull" ||
|
||||||
|
actionName == "pull start" ||
|
||||||
|
actionName == "pull action" ||
|
||||||
|
actionName == "return to pull position" ||
|
||||||
|
actionName == "pull end" ||
|
||||||
|
actionName == "follow" ||
|
||||||
|
actionName == "set facing")
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
class MagePullMultiplier : public PassiveMultiplier
|
class MagePullMultiplier : public PassiveMultiplier
|
||||||
{
|
{
|
||||||
@ -24,8 +204,16 @@ float MagePullMultiplier::GetValue(Action* action)
|
|||||||
if (!action)
|
if (!action)
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
|
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||||
|
if (!strategy || !strategy->HasTarget())
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
std::string const name = action->getName();
|
std::string const name = action->getName();
|
||||||
if (actionName == name || name == "reach spell" || name == "change strategy")
|
if (actionName == name || name == "pull action" || name == "pull start" || name == "pull end" ||
|
||||||
|
name == "pull my target" || name == "pull rti target" ||
|
||||||
|
name == "reach spell" || name == "reach pull" ||
|
||||||
|
name == "return to pull position" || name == "follow" ||
|
||||||
|
name == "set facing" || name == "change strategy")
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
return PassiveMultiplier::GetValue(action);
|
return PassiveMultiplier::GetValue(action);
|
||||||
@ -34,18 +222,32 @@ float MagePullMultiplier::GetValue(Action* action)
|
|||||||
std::vector<NextAction> PullStrategy::getDefaultActions()
|
std::vector<NextAction> PullStrategy::getDefaultActions()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
NextAction(action, 105.0f),
|
NextAction("pull action", 105.0f),
|
||||||
NextAction("follow", 104.0f),
|
|
||||||
NextAction("end pull", 103.0f),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PullStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) { CombatStrategy::InitTriggers(triggers); }
|
void PullStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"pull start",
|
||||||
|
{
|
||||||
|
NextAction("pull start", 106.0f),
|
||||||
|
NextAction("pull action", ACTION_MOVE)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"pull end",
|
||||||
|
{
|
||||||
|
NextAction("pull end", 107.0f)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void PullStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void PullStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
{
|
{
|
||||||
|
multipliers.push_back(new PullMultiplier(botAI));
|
||||||
multipliers.push_back(new MagePullMultiplier(botAI, action));
|
multipliers.push_back(new MagePullMultiplier(botAI, action));
|
||||||
CombatStrategy::InitMultipliers(multipliers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PossibleAddsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PossibleAddsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
@ -61,3 +263,15 @@ void PossibleAddsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PullBackStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
Strategy::InitTriggers(triggers);
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"return to pull position",
|
||||||
|
{
|
||||||
|
NextAction("return to pull position", ACTION_MOVE + 5.0f)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|||||||
@ -6,22 +6,65 @@
|
|||||||
#ifndef _PLAYERBOT_PULLSTRATEGY_H
|
#ifndef _PLAYERBOT_PULLSTRATEGY_H
|
||||||
#define _PLAYERBOT_PULLSTRATEGY_H
|
#define _PLAYERBOT_PULLSTRATEGY_H
|
||||||
|
|
||||||
#include "CombatStrategy.h"
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
class Action;
|
||||||
|
class Multiplier;
|
||||||
|
class Unit;
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
class PullStrategy : public CombatStrategy
|
class PullStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PullStrategy(PlayerbotAI* botAI, std::string const action) : CombatStrategy(botAI), action(action) {}
|
PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction = "");
|
||||||
|
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
std::string const getName() override { return "pull"; }
|
std::string const getName() override { return "pull"; }
|
||||||
std::vector<NextAction> getDefaultActions() override;
|
std::vector<NextAction> getDefaultActions() override;
|
||||||
|
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_NONCOMBAT; }
|
||||||
|
|
||||||
|
static PullStrategy* Get(PlayerbotAI* botAI);
|
||||||
|
static uint8 GetMaxPullTime() { return 15; }
|
||||||
|
|
||||||
|
time_t GetPullStartTime() const { return pullStartTime; }
|
||||||
|
bool IsPullPendingToStart() const { return pendingToStart; }
|
||||||
|
bool HasPullStarted() const { return pullStartTime > 0; }
|
||||||
|
|
||||||
|
bool CanDoPullAction(Unit* target);
|
||||||
|
Unit* GetTarget() const;
|
||||||
|
bool HasTarget() const;
|
||||||
|
|
||||||
|
virtual std::string GetPullActionName() const;
|
||||||
|
std::string GetSpellName() const;
|
||||||
|
float GetRange() const;
|
||||||
|
virtual std::string GetPreActionName() const;
|
||||||
|
|
||||||
|
void RequestPull(Unit* target, bool resetTime = true);
|
||||||
|
void OnPullStarted();
|
||||||
|
void OnPullEnded();
|
||||||
|
|
||||||
|
ReactStates GetPetReactState() const { return petReactState; }
|
||||||
|
void SetPetReactState(ReactStates reactState) { petReactState = reactState; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetTarget(Unit* target);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string const action;
|
std::string const action;
|
||||||
|
std::string const preAction;
|
||||||
|
bool pendingToStart = false;
|
||||||
|
time_t pullStartTime = 0;
|
||||||
|
ReactStates petReactState = REACT_DEFENSIVE;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullMultiplier(PlayerbotAI* botAI);
|
||||||
|
|
||||||
|
float GetValue(Action* action) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PossibleAddsStrategy : public Strategy
|
class PossibleAddsStrategy : public Strategy
|
||||||
@ -33,4 +76,13 @@ public:
|
|||||||
std::string const getName() override { return "adds"; }
|
std::string const getName() override { return "adds"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PullBackStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullBackStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
std::string const getName() override { return "pull back"; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -82,6 +82,7 @@ float WaitForAttackMultiplier::GetValue(Action* action)
|
|||||||
actionName != "set facing" &&
|
actionName != "set facing" &&
|
||||||
actionName != "pull my target" &&
|
actionName != "pull my target" &&
|
||||||
actionName != "pull rti target" &&
|
actionName != "pull rti target" &&
|
||||||
|
actionName != "reach pull" &&
|
||||||
actionName != "pull start" &&
|
actionName != "pull start" &&
|
||||||
actionName != "pull action" &&
|
actionName != "pull action" &&
|
||||||
actionName != "pull end")
|
actionName != "pull end")
|
||||||
|
|||||||
@ -95,6 +95,7 @@ public:
|
|||||||
creators["sit"] = &StrategyContext::sit;
|
creators["sit"] = &StrategyContext::sit;
|
||||||
creators["mark rti"] = &StrategyContext::mark_rti;
|
creators["mark rti"] = &StrategyContext::mark_rti;
|
||||||
creators["adds"] = &StrategyContext::possible_adds;
|
creators["adds"] = &StrategyContext::possible_adds;
|
||||||
|
creators["pull back"] = &StrategyContext::pull_back;
|
||||||
creators["close"] = &StrategyContext::close;
|
creators["close"] = &StrategyContext::close;
|
||||||
creators["ranged"] = &StrategyContext::ranged;
|
creators["ranged"] = &StrategyContext::ranged;
|
||||||
creators["behind"] = &StrategyContext::behind;
|
creators["behind"] = &StrategyContext::behind;
|
||||||
@ -171,6 +172,7 @@ private:
|
|||||||
static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); }
|
static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); }
|
||||||
static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); }
|
static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); }
|
||||||
static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); }
|
static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); }
|
||||||
|
static Strategy* pull_back(PlayerbotAI* botAI) { return new PullBackStrategy(botAI); }
|
||||||
static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); }
|
static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); }
|
||||||
static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); }
|
static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); }
|
||||||
static Strategy* battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); }
|
static Strategy* battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); }
|
||||||
|
|||||||
62
src/Ai/Base/Trigger/PullTriggers.cpp
Normal file
62
src/Ai/Base/Trigger/PullTriggers.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PullTriggers.h"
|
||||||
|
|
||||||
|
#include "PositionValue.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
bool PullStartTrigger::IsActive()
|
||||||
|
{
|
||||||
|
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||||
|
return strategy && strategy->IsPullPendingToStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PullEndTrigger::IsActive()
|
||||||
|
{
|
||||||
|
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||||
|
|
||||||
|
if (!strategy || !strategy->HasPullStarted())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = strategy->GetTarget();
|
||||||
|
if (!target || !target->IsInWorld() || !target->IsAlive())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
time_t const secondsSincePullStarted = time(nullptr) - strategy->GetPullStartTime();
|
||||||
|
if (secondsSincePullStarted >= PullStrategy::GetMaxPullTime())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
float distanceToPullTarget = bot->GetDistance(target);
|
||||||
|
if (distanceToPullTarget > ATTACK_DISTANCE && !target->IsNonMeleeSpellCast(false, false, true) &&
|
||||||
|
(!botAI->IsRanged(bot) || distanceToPullTarget > botAI->GetRange("spell")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!botAI->HasStrategy("pull back", BOT_STATE_COMBAT))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||||
|
if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) <= botAI->GetRange("follow");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReturnToPullPositionTrigger::IsActive()
|
||||||
|
{
|
||||||
|
PullStrategy const* strategy = PullStrategy::Get(botAI);
|
||||||
|
|
||||||
|
Unit* target = strategy ? strategy->GetTarget() : nullptr;
|
||||||
|
if (!strategy || !strategy->HasPullStarted() || !target || !target->IsInCombat() ||
|
||||||
|
!botAI->HasStrategy("pull back", BOT_STATE_COMBAT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"];
|
||||||
|
return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() &&
|
||||||
|
bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance;
|
||||||
|
}
|
||||||
35
src/Ai/Base/Trigger/PullTriggers.h
Normal file
35
src/Ai/Base/Trigger/PullTriggers.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_PULLTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_PULLTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
|
||||||
|
class PullStartTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullStartTrigger(PlayerbotAI* botAI, std::string const name = "pull start") : Trigger(botAI, name) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PullEndTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PullEndTrigger(PlayerbotAI* botAI, std::string const name = "pull end") : Trigger(botAI, name) {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReturnToPullPositionTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReturnToPullPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "return to pull position") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "NewRpgTriggers.h"
|
#include "NewRpgTriggers.h"
|
||||||
#include "PvpTriggers.h"
|
#include "PvpTriggers.h"
|
||||||
|
#include "PullTriggers.h"
|
||||||
#include "RpgTriggers.h"
|
#include "RpgTriggers.h"
|
||||||
#include "RtiTriggers.h"
|
#include "RtiTriggers.h"
|
||||||
#include "StuckTriggers.h"
|
#include "StuckTriggers.h"
|
||||||
@ -129,6 +130,9 @@ public:
|
|||||||
creators["has attackers"] = &TriggerContext::has_attackers;
|
creators["has attackers"] = &TriggerContext::has_attackers;
|
||||||
creators["no possible targets"] = &TriggerContext::no_possible_targets;
|
creators["no possible targets"] = &TriggerContext::no_possible_targets;
|
||||||
creators["possible adds"] = &TriggerContext::possible_adds;
|
creators["possible adds"] = &TriggerContext::possible_adds;
|
||||||
|
creators["pull start"] = &TriggerContext::pull_start;
|
||||||
|
creators["pull end"] = &TriggerContext::pull_end;
|
||||||
|
creators["return to pull position"] = &TriggerContext::return_to_pull_position;
|
||||||
|
|
||||||
creators["no drink"] = &TriggerContext::no_drink;
|
creators["no drink"] = &TriggerContext::no_drink;
|
||||||
creators["no food"] = &TriggerContext::no_food;
|
creators["no food"] = &TriggerContext::no_food;
|
||||||
@ -280,6 +284,9 @@ private:
|
|||||||
static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); }
|
static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); }
|
||||||
static Trigger* no_possible_targets(PlayerbotAI* botAI) { return new NoPossibleTargetsTrigger(botAI); }
|
static Trigger* no_possible_targets(PlayerbotAI* botAI) { return new NoPossibleTargetsTrigger(botAI); }
|
||||||
static Trigger* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsTrigger(botAI); }
|
static Trigger* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsTrigger(botAI); }
|
||||||
|
static Trigger* pull_start(PlayerbotAI* botAI) { return new PullStartTrigger(botAI); }
|
||||||
|
static Trigger* pull_end(PlayerbotAI* botAI) { return new PullEndTrigger(botAI); }
|
||||||
|
static Trigger* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionTrigger(botAI); }
|
||||||
static Trigger* can_loot(PlayerbotAI* botAI) { return new CanLootTrigger(botAI); }
|
static Trigger* can_loot(PlayerbotAI* botAI) { return new CanLootTrigger(botAI); }
|
||||||
static Trigger* far_from_loot_target(PlayerbotAI* botAI) { return new FarFromCurrentLootTrigger(botAI); }
|
static Trigger* far_from_loot_target(PlayerbotAI* botAI) { return new FarFromCurrentLootTrigger(botAI); }
|
||||||
static Trigger* far_from_master(PlayerbotAI* botAI) { return new FarFromMasterTrigger(botAI); }
|
static Trigger* far_from_master(PlayerbotAI* botAI) { return new FarFromMasterTrigger(botAI); }
|
||||||
|
|||||||
@ -8,11 +8,11 @@
|
|||||||
#include "BloodDKStrategy.h"
|
#include "BloodDKStrategy.h"
|
||||||
#include "DKActions.h"
|
#include "DKActions.h"
|
||||||
#include "DKTriggers.h"
|
#include "DKTriggers.h"
|
||||||
|
#include "DeathKnightPullStrategy.h"
|
||||||
#include "FrostDKStrategy.h"
|
#include "FrostDKStrategy.h"
|
||||||
#include "GenericDKNonCombatStrategy.h"
|
#include "GenericDKNonCombatStrategy.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PullStrategy.h"
|
|
||||||
#include "UnholyDKStrategy.h"
|
#include "UnholyDKStrategy.h"
|
||||||
|
|
||||||
class DeathKnightStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
class DeathKnightStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||||
@ -28,7 +28,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDKNonCombatStrategy(botAI); }
|
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDKNonCombatStrategy(botAI); }
|
||||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "icy touch"); }
|
static Strategy* pull(PlayerbotAI* botAI) { return new DeathKnightPullStrategy(botAI); }
|
||||||
static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostDKAoeStrategy(botAI); }
|
static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostDKAoeStrategy(botAI); }
|
||||||
static Strategy* unholy_aoe(PlayerbotAI* botAI) { return new UnholyDKAoeStrategy(botAI); }
|
static Strategy* unholy_aoe(PlayerbotAI* botAI) { return new UnholyDKAoeStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|||||||
43
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp
Normal file
43
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DeathKnightPullStrategy.h"
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
std::string DeathKnightPullStrategy::GetPullActionName() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!bot || !target ||
|
||||||
|
(!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT)))
|
||||||
|
{
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const deathGripSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "death grip")->Get();
|
||||||
|
if (deathGripSpellId && bot->HasSpell(deathGripSpellId) &&
|
||||||
|
botAI->CanCastSpell(deathGripSpellId, target))
|
||||||
|
{
|
||||||
|
return "death grip";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const icyTouchSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "icy touch")->Get();
|
||||||
|
if (!icyTouchSpellId || !bot->HasSpell(icyTouchSpellId) ||
|
||||||
|
!botAI->CanCastSpell(icyTouchSpellId, target))
|
||||||
|
{
|
||||||
|
uint32 const darkCommandSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "dark command")->Get();
|
||||||
|
if (darkCommandSpellId && bot->HasSpell(darkCommandSpellId) &&
|
||||||
|
botAI->CanCastSpell(darkCommandSpellId, target))
|
||||||
|
{
|
||||||
|
return "dark command";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
}
|
||||||
19
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h
Normal file
19
src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_DEATH_KNIGHT_PULL_STRATEGY_H
|
||||||
|
#define _PLAYERBOT_DEATH_KNIGHT_PULL_STRATEGY_H
|
||||||
|
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
class DeathKnightPullStrategy : public PullStrategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeathKnightPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "icy touch") {}
|
||||||
|
|
||||||
|
std::string GetPullActionName() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include "MeleeDruidStrategy.h"
|
#include "MeleeDruidStrategy.h"
|
||||||
#include "OffhealDruidCatStrategy.h"
|
#include "OffhealDruidCatStrategy.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "DruidPullStrategy.h"
|
||||||
|
|
||||||
class DruidStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
class DruidStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||||
{
|
{
|
||||||
@ -26,6 +27,7 @@ public:
|
|||||||
DruidStrategyFactoryInternal()
|
DruidStrategyFactoryInternal()
|
||||||
{
|
{
|
||||||
creators["nc"] = &DruidStrategyFactoryInternal::nc;
|
creators["nc"] = &DruidStrategyFactoryInternal::nc;
|
||||||
|
creators["pull"] = &DruidStrategyFactoryInternal::pull;
|
||||||
creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe;
|
creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe;
|
||||||
creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe;
|
creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe;
|
||||||
creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff;
|
creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff;
|
||||||
@ -40,6 +42,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); }
|
static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); }
|
||||||
|
static Strategy* pull(PlayerbotAI* botAI) { return new DruidPullStrategy(botAI); }
|
||||||
static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); }
|
static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); }
|
||||||
static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); }
|
static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); }
|
||||||
static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); }
|
static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); }
|
||||||
|
|||||||
46
src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp
Normal file
46
src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DruidPullStrategy.h"
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
std::string DruidPullStrategy::GetPullActionName() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
std::string actionName = PullStrategy::GetPullActionName();
|
||||||
|
if (!bot)
|
||||||
|
return actionName;
|
||||||
|
|
||||||
|
uint32 const faerieFireFeralId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "faerie fire (feral)")->Get();
|
||||||
|
if (faerieFireFeralId && bot->HasSpell(faerieFireFeralId) &&
|
||||||
|
(botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT)))
|
||||||
|
{
|
||||||
|
actionName = "faerie fire (feral)";
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
uint32 const faerieFireSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", actionName)->Get();
|
||||||
|
if (target && (!faerieFireSpellId || !bot->HasSpell(faerieFireSpellId) ||
|
||||||
|
!botAI->CanCastSpell(faerieFireSpellId, target)))
|
||||||
|
{
|
||||||
|
uint32 const growlSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "growl")->Get();
|
||||||
|
if (growlSpellId && bot->HasSpell(growlSpellId) && botAI->CanCastSpell(growlSpellId, target))
|
||||||
|
return "growl";
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DruidPullStrategy::GetPreActionName() const
|
||||||
|
{
|
||||||
|
if (GetPullActionName() == "faerie fire")
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return PullStrategy::GetPreActionName();
|
||||||
|
}
|
||||||
20
src/Ai/Class/Druid/Strategy/DruidPullStrategy.h
Normal file
20
src/Ai/Class/Druid/Strategy/DruidPullStrategy.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_DRUID_PULL_STRATEGY_H
|
||||||
|
#define _PLAYERBOT_DRUID_PULL_STRATEGY_H
|
||||||
|
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
class DruidPullStrategy : public PullStrategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DruidPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "faerie fire", "dire bear form") {}
|
||||||
|
|
||||||
|
std::string GetPullActionName() const override;
|
||||||
|
std::string GetPreActionName() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -12,6 +12,7 @@
|
|||||||
#include "OffhealRetPaladinStrategy.h"
|
#include "OffhealRetPaladinStrategy.h"
|
||||||
#include "PaladinActions.h"
|
#include "PaladinActions.h"
|
||||||
#include "PaladinBuffStrategies.h"
|
#include "PaladinBuffStrategies.h"
|
||||||
|
#include "PaladinPullStrategy.h"
|
||||||
#include "PaladinTriggers.h"
|
#include "PaladinTriggers.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "TankPaladinStrategy.h"
|
#include "TankPaladinStrategy.h"
|
||||||
@ -22,6 +23,7 @@ public:
|
|||||||
PaladinStrategyFactoryInternal()
|
PaladinStrategyFactoryInternal()
|
||||||
{
|
{
|
||||||
creators["nc"] = &PaladinStrategyFactoryInternal::nc;
|
creators["nc"] = &PaladinStrategyFactoryInternal::nc;
|
||||||
|
creators["pull"] = &PaladinStrategyFactoryInternal::pull;
|
||||||
creators["cure"] = &PaladinStrategyFactoryInternal::cure;
|
creators["cure"] = &PaladinStrategyFactoryInternal::cure;
|
||||||
creators["boost"] = &PaladinStrategyFactoryInternal::boost;
|
creators["boost"] = &PaladinStrategyFactoryInternal::boost;
|
||||||
creators["cc"] = &PaladinStrategyFactoryInternal::cc;
|
creators["cc"] = &PaladinStrategyFactoryInternal::cc;
|
||||||
@ -31,6 +33,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericPaladinNonCombatStrategy(botAI); }
|
static Strategy* nc(PlayerbotAI* botAI) { return new GenericPaladinNonCombatStrategy(botAI); }
|
||||||
|
static Strategy* pull(PlayerbotAI* botAI) { return new PaladinPullStrategy(botAI); }
|
||||||
static Strategy* cure(PlayerbotAI* botAI) { return new PaladinCureStrategy(botAI); }
|
static Strategy* cure(PlayerbotAI* botAI) { return new PaladinCureStrategy(botAI); }
|
||||||
static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); }
|
static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); }
|
||||||
static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); }
|
static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); }
|
||||||
|
|||||||
46
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp
Normal file
46
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PaladinPullStrategy.h"
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
std::string PaladinPullStrategy::GetPullActionName() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!bot || !target ||
|
||||||
|
(!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT)))
|
||||||
|
{
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const avengersShieldSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "avenger's shield")->Get();
|
||||||
|
if (avengersShieldSpellId && bot->HasSpell(avengersShieldSpellId) &&
|
||||||
|
botAI->CanCastSpell(avengersShieldSpellId, target))
|
||||||
|
{
|
||||||
|
return "avenger's shield";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 const handOfReckoningSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "hand of reckoning")->Get();
|
||||||
|
if (handOfReckoningSpellId && bot->HasSpell(handOfReckoningSpellId) &&
|
||||||
|
botAI->CanCastSpell(handOfReckoningSpellId, target))
|
||||||
|
{
|
||||||
|
return "hand of reckoning";
|
||||||
|
}
|
||||||
|
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PaladinPullStrategy::GetPreActionName() const
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("tank", BOT_STATE_COMBAT) || botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return PullStrategy::GetPreActionName();
|
||||||
|
}
|
||||||
20
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h
Normal file
20
src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_PALADIN_PULL_STRATEGY_H
|
||||||
|
#define _PLAYERBOT_PALADIN_PULL_STRATEGY_H
|
||||||
|
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
class PaladinPullStrategy : public PullStrategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PaladinPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "judgement", "seal of righteousness") {}
|
||||||
|
|
||||||
|
std::string GetPullActionName() const override;
|
||||||
|
std::string GetPreActionName() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
27
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp
Normal file
27
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WarriorPullStrategy.h"
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
|
||||||
|
std::string WarriorPullStrategy::GetPullActionName() const
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!bot || !target)
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
|
||||||
|
uint32 const heroicThrowSpellId = botAI->GetAiObjectContext()->GetValue<uint32>("spell id", "heroic throw")->Get();
|
||||||
|
if (heroicThrowSpellId && bot->HasSpell(heroicThrowSpellId) &&
|
||||||
|
botAI->CanCastSpell(heroicThrowSpellId, target))
|
||||||
|
{
|
||||||
|
return "heroic throw";
|
||||||
|
}
|
||||||
|
|
||||||
|
return PullStrategy::GetPullActionName();
|
||||||
|
}
|
||||||
19
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h
Normal file
19
src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_WARRIOR_PULL_STRATEGY_H
|
||||||
|
#define _PLAYERBOT_WARRIOR_PULL_STRATEGY_H
|
||||||
|
|
||||||
|
#include "PullStrategy.h"
|
||||||
|
|
||||||
|
class WarriorPullStrategy : public PullStrategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WarriorPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "shoot") {}
|
||||||
|
|
||||||
|
std::string GetPullActionName() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -10,8 +10,8 @@
|
|||||||
#include "GenericWarriorNonCombatStrategy.h"
|
#include "GenericWarriorNonCombatStrategy.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PullStrategy.h"
|
|
||||||
#include "TankWarriorStrategy.h"
|
#include "TankWarriorStrategy.h"
|
||||||
|
#include "WarriorPullStrategy.h"
|
||||||
#include "WarriorActions.h"
|
#include "WarriorActions.h"
|
||||||
#include "WarriorTriggers.h"
|
#include "WarriorTriggers.h"
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarriorNonCombatStrategy(botAI); }
|
static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarriorNonCombatStrategy(botAI); }
|
||||||
static Strategy* warrior_aoe(PlayerbotAI* botAI) { return new WarrirorAoeStrategy(botAI); }
|
static Strategy* warrior_aoe(PlayerbotAI* botAI) { return new WarrirorAoeStrategy(botAI); }
|
||||||
static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); }
|
static Strategy* pull(PlayerbotAI* botAI) { return new WarriorPullStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class WarriorCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
class WarriorCombatStrategyFactoryInternal : public NamedObjectContext<Strategy>
|
||||||
|
|||||||
117
src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.cpp
Normal file
117
src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.cpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include "Playerbots.h"
|
||||||
|
#include "AiFactory.h"
|
||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
#include "AuchenaiCryptsActions.h"
|
||||||
|
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
|
static const Position SHIRRAK_RANGED_POSITION = { -21.777f, -162.700f, 26.062f };
|
||||||
|
static const Position SHIRRAK_TANK_POSITION = { -65.171f, -162.920f, 26.504f };
|
||||||
|
|
||||||
|
// Tank will position Shirrak at the specified coordinates, further down the corridor past the stairs
|
||||||
|
|
||||||
|
bool ShirrakTankPositionBossAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* shirrak = AI_VALUE2(Unit*, "find target", "shirrak the dead watcher");
|
||||||
|
if (!shirrak)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != shirrak)
|
||||||
|
return Attack(shirrak);
|
||||||
|
|
||||||
|
if (shirrak->GetVictim() == bot && bot->IsWithinMeleeRange(shirrak) &&
|
||||||
|
bot->GetHealthPct()>30.0f)
|
||||||
|
{
|
||||||
|
const Position& position = SHIRRAK_TANK_POSITION;
|
||||||
|
float distToPosition = bot->GetExactDist2d(position.GetPositionX(),
|
||||||
|
position.GetPositionY());
|
||||||
|
if (distToPosition > 6.0f)
|
||||||
|
{
|
||||||
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
|
float moveDist = std::min(2.0f, distToPosition);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flee from Shirrak's Focus Fire
|
||||||
|
|
||||||
|
bool ShirrakFleeFocusFireAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(creatureList, static_cast<uint32>(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f);
|
||||||
|
|
||||||
|
for (Creature* flare : creatureList)
|
||||||
|
{
|
||||||
|
if (flare && flare->IsAlive())
|
||||||
|
{
|
||||||
|
float currentDistance = bot->GetDistance2d(flare);
|
||||||
|
constexpr float safeDistance = 12.0f;
|
||||||
|
constexpr float buffer = 5.0f;
|
||||||
|
|
||||||
|
if (currentDistance < safeDistance)
|
||||||
|
{
|
||||||
|
bot->AttackStop();
|
||||||
|
|
||||||
|
float distanceToMove = safeDistance - currentDistance + buffer;
|
||||||
|
|
||||||
|
return MoveAway(flare, distanceToMove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged should keep distance from Shirrak, staying at the edge of the stairs
|
||||||
|
|
||||||
|
bool ShirrakRangedKeepDistanceAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<Player*> rangedBots;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && botAI->IsRanged(member))
|
||||||
|
rangedBots.push_back(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto findIt = std::find(rangedBots.begin(), rangedBots.end(), bot);
|
||||||
|
size_t botIndex = (findIt != rangedBots.end()) ? std::distance(rangedBots.begin(), findIt) : 0;
|
||||||
|
size_t count = rangedBots.size();
|
||||||
|
|
||||||
|
constexpr float arcSpan = M_PI / 2.0f;
|
||||||
|
float arcCenter = M_PI;
|
||||||
|
float arcStart = arcCenter - (arcSpan / 2.0f);
|
||||||
|
|
||||||
|
float angle = (count <= 1) ? arcCenter : (arcStart + (arcSpan * (float)botIndex / (float)(count - 1)));
|
||||||
|
|
||||||
|
constexpr float spreadRadius = 3.0f;
|
||||||
|
float targetX = SHIRRAK_RANGED_POSITION.GetPositionX() + cos(angle) * spreadRadius;
|
||||||
|
float targetY = SHIRRAK_RANGED_POSITION.GetPositionY() + sin(angle) * spreadRadius;
|
||||||
|
|
||||||
|
float distToSpot = bot->GetExactDist2d(targetX, targetY);
|
||||||
|
|
||||||
|
if (distToSpot > 4.0f)
|
||||||
|
{
|
||||||
|
float dX = targetX - bot->GetPositionX();
|
||||||
|
float dY = targetY - bot->GetPositionY();
|
||||||
|
|
||||||
|
float moveDist = std::min(2.0f, distToSpot);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / distToSpot) * moveDist;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / distToSpot) * moveDist;
|
||||||
|
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
31
src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.h
Normal file
31
src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONS_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONS_H
|
||||||
|
|
||||||
|
#include "AttackAction.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
|
class ShirrakTankPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakTankPositionBossAction(PlayerbotAI* botAI) : AttackAction(botAI, "shirrak tank position boss") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShirrakFleeFocusFireAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakFleeFocusFireAction(PlayerbotAI* botAI) : MovementAction(botAI, "shirrak flee focus fire") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShirrakRangedKeepDistanceAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakRangedKeepDistanceAction(PlayerbotAI* botAI) : MovementAction(botAI, "shirrak ranged keep distance") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
34
src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsActionContext.h
Normal file
34
src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsActionContext.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Action.h"
|
||||||
|
#include "AuchenaiCryptsActions.h"
|
||||||
|
|
||||||
|
class TbcDungeonAuchenaiCryptsActionContext : public NamedObjectContext<Action>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TbcDungeonAuchenaiCryptsActionContext() : NamedObjectContext<Action>(false, true)
|
||||||
|
{
|
||||||
|
creators["shirrak tank position boss"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsActionContext::shirrak_tank_position_boss;
|
||||||
|
|
||||||
|
creators["shirrak flee focus fire"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsActionContext::shirrak_flee_focus_fire;
|
||||||
|
|
||||||
|
creators["shirrak ranged keep distance"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsActionContext::shirrak_ranged_keep_distance;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
static Action* shirrak_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakTankPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* shirrak_flee_focus_fire(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakFleeFocusFireAction(botAI); }
|
||||||
|
|
||||||
|
static Action* shirrak_ranged_keep_distance(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakRangedKeepDistanceAction(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
35
src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsTriggerContext.h
Normal file
35
src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsTriggerContext.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "TriggerContext.h"
|
||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
|
||||||
|
class TbcDungeonAuchenaiCryptsTriggerContext : public NamedObjectContext<Trigger>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
TbcDungeonAuchenaiCryptsTriggerContext()
|
||||||
|
{
|
||||||
|
creators["shirrak tank position boss"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsTriggerContext::shirrak_tank_position_boss;
|
||||||
|
|
||||||
|
creators["shirrak flee focus fire"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsTriggerContext::shirrak_flee_focus_fire;
|
||||||
|
|
||||||
|
creators["shirrak ranged keep distance"] =
|
||||||
|
&TbcDungeonAuchenaiCryptsTriggerContext::shirrak_ranged_keep_distance;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
static Trigger* shirrak_tank_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakTankPositionBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* shirrak_flee_focus_fire(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakFleeFocusFireTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* shirrak_ranged_keep_distance(
|
||||||
|
PlayerbotAI* botAI) { return new ShirrakRangedKeepDistanceTrigger(botAI); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
#include "AuchenaiCryptsMultipliers.h"
|
||||||
|
#include "AuchenaiCryptsActions.h"
|
||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
|
#include "FollowActions.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
|
// Flee from Focus Fire and dont run back in
|
||||||
|
float ShirrakFleeFocusFireMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "shirrak the dead watcher"))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(creatureList, static_cast<uint32>(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f);
|
||||||
|
|
||||||
|
for (Creature* flare : creatureList)
|
||||||
|
{
|
||||||
|
if (flare && flare->IsAlive())
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
float currentDistance = bot->GetDistance2d(flare);
|
||||||
|
constexpr float safeDistance = 12.0f;
|
||||||
|
constexpr float buffer = 5.0f;
|
||||||
|
|
||||||
|
if (currentDistance < safeDistance + buffer && (
|
||||||
|
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<ShirrakRangedKeepDistanceAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<AvoidAoeAction*>(action)))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class ShirrakFleeFocusFireMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakFleeFocusFireMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "shirrak flee focus fire") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
#include "AuchenaiCryptsStrategy.h"
|
||||||
|
#include "AuchenaiCryptsMultipliers.h"
|
||||||
|
|
||||||
|
void TbcDungeonAuchenaiCryptsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
// Shirrak The Dead Watcher
|
||||||
|
triggers.push_back(new TriggerNode("shirrak tank position boss", {
|
||||||
|
NextAction("shirrak tank position boss", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("shirrak flee focus fire", {
|
||||||
|
NextAction("shirrak flee focus fire", ACTION_EMERGENCY + 10) }));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("shirrak ranged keep distance", {
|
||||||
|
NextAction("shirrak ranged keep distance", ACTION_RAID + 1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TbcDungeonAuchenaiCryptsStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new ShirrakFleeFocusFireMultiplier(botAI));
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSSTRATEGY_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSSTRATEGY_H
|
||||||
|
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class TbcDungeonAuchenaiCryptsStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TbcDungeonAuchenaiCryptsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
virtual std::string const getName() override { return "tbc-ac"; }
|
||||||
|
|
||||||
|
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||||
|
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
#include "Playerbots.h"
|
||||||
|
#include "AuchenaiCryptsTriggers.h"
|
||||||
|
#include "AiObject.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
|
||||||
|
// Shirrak the Dead Watcher
|
||||||
|
|
||||||
|
bool ShirrakTankPositionBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsTank(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "shirrak the dead watcher");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShirrakFleeFocusFireTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE2(Unit*, "find target", "shirrak the dead watcher"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::list<Creature*> creatureList;
|
||||||
|
bot->GetCreatureListWithEntryInGrid(creatureList, static_cast<uint32>(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f);
|
||||||
|
|
||||||
|
for (Creature* flare : creatureList)
|
||||||
|
{
|
||||||
|
if (flare && flare->IsAlive())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShirrakRangedKeepDistanceTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return botAI->IsRanged(bot) &&
|
||||||
|
AI_VALUE2(Unit*, "find target", "shirrak the dead watcher");
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERS_H
|
||||||
|
|
||||||
|
#include "Trigger.h"
|
||||||
|
#include "GenericTriggers.h"
|
||||||
|
#include "DungeonStrategyUtils.h"
|
||||||
|
|
||||||
|
enum class AuchenaiCryptsIDs : uint32
|
||||||
|
{
|
||||||
|
// Shirrak The Dead Watcher
|
||||||
|
NPC_FOCUS_FIRE = 18374,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShirrakTankPositionBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakTankPositionBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak tank position boss") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShirrakFleeFocusFireTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakFleeFocusFireTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak flee focus fire") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShirrakRangedKeepDistanceTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShirrakRangedKeepDistanceTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak ranged keep distance") {}
|
||||||
|
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -2,6 +2,7 @@
|
|||||||
#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H
|
#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H
|
||||||
|
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
|
#include "AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.h"
|
||||||
#include "UtgardeKeep/Strategy/UtgardeKeepStrategy.h"
|
#include "UtgardeKeep/Strategy/UtgardeKeepStrategy.h"
|
||||||
#include "Nexus/Strategy/NexusStrategy.h"
|
#include "Nexus/Strategy/NexusStrategy.h"
|
||||||
#include "AzjolNerub/Strategy/AzjolNerubStrategy.h"
|
#include "AzjolNerub/Strategy/AzjolNerubStrategy.h"
|
||||||
@ -44,7 +45,7 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
// Burning Crusade
|
// Burning Crusade
|
||||||
// ...
|
creators["tbc-ac"] = &DungeonStrategyContext::tbc_ac; // Auchindoun: Auchenai Crypts
|
||||||
|
|
||||||
// Wrath of the Lich King
|
// Wrath of the Lich King
|
||||||
creators["wotlk-uk"] = &DungeonStrategyContext::wotlk_uk; // Utgarde Keep
|
creators["wotlk-uk"] = &DungeonStrategyContext::wotlk_uk; // Utgarde Keep
|
||||||
@ -65,6 +66,7 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
|||||||
creators["wotlk-fos"] = &DungeonStrategyContext::wotlk_fos; // The Forge of Souls
|
creators["wotlk-fos"] = &DungeonStrategyContext::wotlk_fos; // The Forge of Souls
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
static Strategy* tbc_ac(PlayerbotAI* botAI) { return new TbcDungeonAuchenaiCryptsStrategy(botAI); }
|
||||||
static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||||
static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); }
|
static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); }
|
||||||
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); }
|
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); }
|
||||||
|
|||||||
6
src/Ai/Dungeon/TbcDungeonActionContext.h
Normal file
6
src/Ai/Dungeon/TbcDungeonActionContext.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONACTIONCONTEXT_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONACTIONCONTEXT_H
|
||||||
|
|
||||||
|
#include "AuchenaiCrypts/AuchenaiCryptsActionContext.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
6
src/Ai/Dungeon/TbcDungeonTriggerContext.h
Normal file
6
src/Ai/Dungeon/TbcDungeonTriggerContext.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _PLAYERBOT_TBCDUNGEONTRIGGERCONTEXT_H
|
||||||
|
#define _PLAYERBOT_TBCDUNGEONTRIGGERCONTEXT_H
|
||||||
|
|
||||||
|
#include "AuchenaiCrypts/AuchenaiCryptsTriggerContext.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||||
|
#include "Ai/Dungeon/TbcDungeonActionContext.h"
|
||||||
#include "Ai/Dungeon/WotlkDungeonActionContext.h"
|
#include "Ai/Dungeon/WotlkDungeonActionContext.h"
|
||||||
|
|
||||||
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
|
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
|
||||||
@ -41,6 +42,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
|||||||
actionContexts.Add(new RaidUlduarActionContext());
|
actionContexts.Add(new RaidUlduarActionContext());
|
||||||
actionContexts.Add(new RaidOnyxiaActionContext());
|
actionContexts.Add(new RaidOnyxiaActionContext());
|
||||||
actionContexts.Add(new RaidIccActionContext());
|
actionContexts.Add(new RaidIccActionContext());
|
||||||
|
actionContexts.Add(new TbcDungeonAuchenaiCryptsActionContext());
|
||||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||||
actionContexts.Add(new WotlkDungeonNexActionContext());
|
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||||
actionContexts.Add(new WotlkDungeonANActionContext());
|
actionContexts.Add(new WotlkDungeonANActionContext());
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||||
#include "Ai/Raid/Icecrown/RaidIccTriggerContext.h"
|
#include "Ai/Raid/Icecrown/RaidIccTriggerContext.h"
|
||||||
|
#include "Ai/Dungeon/TbcDungeonTriggerContext.h"
|
||||||
#include "Ai/Dungeon/WotlkDungeonTriggerContext.h"
|
#include "Ai/Dungeon/WotlkDungeonTriggerContext.h"
|
||||||
|
|
||||||
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
|
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
|
||||||
@ -41,6 +42,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
|||||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||||
triggerContexts.Add(new RaidOnyxiaTriggerContext());
|
triggerContexts.Add(new RaidOnyxiaTriggerContext());
|
||||||
triggerContexts.Add(new RaidIccTriggerContext());
|
triggerContexts.Add(new RaidIccTriggerContext());
|
||||||
|
triggerContexts.Add(new TbcDungeonAuchenaiCryptsTriggerContext());
|
||||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||||
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||||
triggerContexts.Add(new WotlkDungeonANTriggerContext());
|
triggerContexts.Add(new WotlkDungeonANTriggerContext());
|
||||||
|
|||||||
@ -428,6 +428,12 @@ void Engine::toggleStrategy(std::string const name)
|
|||||||
|
|
||||||
bool Engine::HasStrategy(std::string const name) { return strategies.find(name) != strategies.end(); }
|
bool Engine::HasStrategy(std::string const name) { return strategies.find(name) != strategies.end(); }
|
||||||
|
|
||||||
|
Strategy* Engine::GetStrategy(std::string const name)
|
||||||
|
{
|
||||||
|
std::map<std::string, Strategy*>::iterator i = strategies.find(name);
|
||||||
|
return i != strategies.end() ? i->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::ProcessTriggers(bool minimal)
|
void Engine::ProcessTriggers(bool minimal)
|
||||||
{
|
{
|
||||||
std::unordered_map<Trigger*, Event> fires;
|
std::unordered_map<Trigger*, Event> fires;
|
||||||
|
|||||||
@ -70,6 +70,7 @@ public:
|
|||||||
void addStrategiesNoInit(std::string first, ...);
|
void addStrategiesNoInit(std::string first, ...);
|
||||||
bool removeStrategy(std::string const name, bool init = true);
|
bool removeStrategy(std::string const name, bool init = true);
|
||||||
bool HasStrategy(std::string const name);
|
bool HasStrategy(std::string const name);
|
||||||
|
Strategy* GetStrategy(std::string const name);
|
||||||
void removeAllStrategies();
|
void removeAllStrategies();
|
||||||
void toggleStrategy(std::string const name);
|
void toggleStrategy(std::string const name);
|
||||||
std::string const ListStrategies();
|
std::string const ListStrategies();
|
||||||
|
|||||||
@ -315,7 +315,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
break;
|
break;
|
||||||
case CLASS_WARRIOR:
|
case CLASS_WARRIOR:
|
||||||
if (tab == WARRIOR_TAB_PROTECTION)
|
if (tab == WARRIOR_TAB_PROTECTION)
|
||||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
|
engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr);
|
||||||
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
|
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
|
||||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
|
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
|
||||||
else
|
else
|
||||||
@ -333,7 +333,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
break;
|
break;
|
||||||
case CLASS_PALADIN:
|
case CLASS_PALADIN:
|
||||||
if (tab == PALADIN_TAB_PROTECTION)
|
if (tab == PALADIN_TAB_PROTECTION)
|
||||||
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
|
engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "bthreat", "barmor", "cure", nullptr);
|
||||||
else if (tab == PALADIN_TAB_HOLY)
|
else if (tab == PALADIN_TAB_HOLY)
|
||||||
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
|
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
|
||||||
else
|
else
|
||||||
@ -352,7 +352,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
|
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
|
||||||
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
|
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
|
||||||
else
|
else
|
||||||
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
|
engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", nullptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLASS_HUNTER:
|
case CLASS_HUNTER:
|
||||||
@ -383,7 +383,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
break;
|
break;
|
||||||
case CLASS_DEATH_KNIGHT:
|
case CLASS_DEATH_KNIGHT:
|
||||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||||
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
|
engine->addStrategiesNoInit("blood", "tank assist", "pull", "pull back", nullptr);
|
||||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||||
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
|
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
|
||||||
else
|
else
|
||||||
@ -510,7 +510,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
|||||||
case CLASS_PALADIN:
|
case CLASS_PALADIN:
|
||||||
if (tab == PALADIN_TAB_PROTECTION)
|
if (tab == PALADIN_TAB_PROTECTION)
|
||||||
{
|
{
|
||||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
|
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "pull", "barmor", nullptr);
|
||||||
if (player->GetLevel() >= 20)
|
if (player->GetLevel() >= 20)
|
||||||
nonCombatEngine->addStrategy("bhealth", false);
|
nonCombatEngine->addStrategy("bhealth", false);
|
||||||
else
|
else
|
||||||
@ -548,14 +548,14 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
|||||||
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
|
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
|
||||||
nonCombatEngine->addStrategy("dps assist", false);
|
nonCombatEngine->addStrategy("dps assist", false);
|
||||||
else
|
else
|
||||||
nonCombatEngine->addStrategy("tank assist", false);
|
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||||
break;
|
break;
|
||||||
case CLASS_WARRIOR:
|
case CLASS_WARRIOR:
|
||||||
if (tab == WARRIOR_TAB_PROTECTION)
|
if (tab == WARRIOR_TAB_PROTECTION)
|
||||||
nonCombatEngine->addStrategy("tank assist", false);
|
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||||
else
|
else
|
||||||
nonCombatEngine->addStrategy("dps assist", false);
|
nonCombatEngine->addStrategy("dps assist", false);
|
||||||
break;
|
break;
|
||||||
@ -571,7 +571,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
|||||||
break;
|
break;
|
||||||
case CLASS_DEATH_KNIGHT:
|
case CLASS_DEATH_KNIGHT:
|
||||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||||
nonCombatEngine->addStrategy("tank assist", false);
|
nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr);
|
||||||
else
|
else
|
||||||
nonCombatEngine->addStrategy("dps assist", false);
|
nonCombatEngine->addStrategy("dps assist", false);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "PlayerbotFactory.h"
|
#include "PlayerbotFactory.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "AccountMgr.h"
|
#include "AccountMgr.h"
|
||||||
@ -47,10 +48,11 @@ static std::vector<uint32> initSlotsOrder = {EQUIPMENT_SLOT_TRINKET1, EQUIPMENT_
|
|||||||
EQUIPMENT_SLOT_LEGS, EQUIPMENT_SLOT_HANDS, EQUIPMENT_SLOT_NECK, EQUIPMENT_SLOT_BODY, EQUIPMENT_SLOT_WAIST,
|
EQUIPMENT_SLOT_LEGS, EQUIPMENT_SLOT_HANDS, EQUIPMENT_SLOT_NECK, EQUIPMENT_SLOT_BODY, EQUIPMENT_SLOT_WAIST,
|
||||||
EQUIPMENT_SLOT_FEET, EQUIPMENT_SLOT_WRISTS, EQUIPMENT_SLOT_FINGER1, EQUIPMENT_SLOT_FINGER2, EQUIPMENT_SLOT_BACK};
|
EQUIPMENT_SLOT_FEET, EQUIPMENT_SLOT_WRISTS, EQUIPMENT_SLOT_FINGER1, EQUIPMENT_SLOT_FINGER2, EQUIPMENT_SLOT_BACK};
|
||||||
|
|
||||||
uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING,
|
uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING,
|
||||||
SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING,
|
SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING,
|
||||||
SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING,
|
SKILL_HERBALISM, SKILL_INSCRIPTION, SKILL_MINING,
|
||||||
SKILL_JEWELCRAFTING};
|
SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID,
|
||||||
|
SKILL_FISHING, SKILL_JEWELCRAFTING};
|
||||||
|
|
||||||
std::list<uint32> PlayerbotFactory::classQuestIds;
|
std::list<uint32> PlayerbotFactory::classQuestIds;
|
||||||
std::list<uint32> PlayerbotFactory::specialQuestIds;
|
std::list<uint32> PlayerbotFactory::specialQuestIds;
|
||||||
@ -58,6 +60,264 @@ std::vector<uint32> PlayerbotFactory::enchantSpellIdCache;
|
|||||||
std::vector<uint32> PlayerbotFactory::enchantGemIdCache;
|
std::vector<uint32> PlayerbotFactory::enchantGemIdCache;
|
||||||
std::unordered_map<uint32, std::vector<uint32>> PlayerbotFactory::trainerIdCache;
|
std::unordered_map<uint32, std::vector<uint32>> PlayerbotFactory::trainerIdCache;
|
||||||
|
|
||||||
|
bool PlayerbotFactory::IsPrimaryTradeSkill(uint16 skillId)
|
||||||
|
{
|
||||||
|
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillId);
|
||||||
|
return skillLine && skillLine->categoryId == SKILL_CATEGORY_PROFESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::IsGatheringTradeSkill(uint16 skillId)
|
||||||
|
{
|
||||||
|
switch (skillId)
|
||||||
|
{
|
||||||
|
case SKILL_HERBALISM:
|
||||||
|
case SKILL_MINING:
|
||||||
|
case SKILL_SKINNING:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::IsCraftingTradeSkill(uint16 skillId)
|
||||||
|
{
|
||||||
|
return IsPrimaryTradeSkill(skillId) && !IsGatheringTradeSkill(skillId);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 PlayerbotFactory::GetProfessionStarterSpell(uint16 skillId)
|
||||||
|
{
|
||||||
|
static constexpr std::array<std::pair<uint16, uint32>, 14> ProfessionStarterSpells = {{
|
||||||
|
{SKILL_ALCHEMY, 2259},
|
||||||
|
{SKILL_BLACKSMITHING, 2018},
|
||||||
|
{SKILL_COOKING, 2550},
|
||||||
|
{SKILL_ENCHANTING, 7411},
|
||||||
|
{SKILL_ENGINEERING, 4036},
|
||||||
|
{SKILL_FIRST_AID, 3273},
|
||||||
|
{SKILL_FISHING, 7620},
|
||||||
|
{SKILL_HERBALISM, 2366},
|
||||||
|
{SKILL_INSCRIPTION, 45357},
|
||||||
|
{SKILL_JEWELCRAFTING, 25229},
|
||||||
|
{SKILL_LEATHERWORKING, 2108},
|
||||||
|
{SKILL_MINING, 2575},
|
||||||
|
{SKILL_SKINNING, 8613},
|
||||||
|
{SKILL_TAILORING, 3908}
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (auto const& [professionSkill, starterSpell] : ProfessionStarterSpells)
|
||||||
|
{
|
||||||
|
if (professionSkill == skillId)
|
||||||
|
return starterSpell;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PlayerbotFactory::WeightedProfessionPair> PlayerbotFactory::GetClassProfessionPairs(Player* bot)
|
||||||
|
{
|
||||||
|
switch (bot->getClass())
|
||||||
|
{
|
||||||
|
case CLASS_WARRIOR:
|
||||||
|
return {{SKILL_MINING, SKILL_BLACKSMITHING, 45},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 30},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 15},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 10}};
|
||||||
|
case CLASS_PALADIN:
|
||||||
|
return {{SKILL_MINING, SKILL_BLACKSMITHING, 45},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 30},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 15},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 10}};
|
||||||
|
case CLASS_DEATH_KNIGHT:
|
||||||
|
return {{SKILL_MINING, SKILL_BLACKSMITHING, 45},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 35},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 20}};
|
||||||
|
case CLASS_HUNTER:
|
||||||
|
return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 45},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 35},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 10},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 10}};
|
||||||
|
case CLASS_ROGUE:
|
||||||
|
return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 35},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 25},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 25},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 10},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 5}};
|
||||||
|
case CLASS_DRUID:
|
||||||
|
return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 35},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 35},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 20},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 10}};
|
||||||
|
case CLASS_SHAMAN:
|
||||||
|
return {{SKILL_HERBALISM, SKILL_ALCHEMY, 35},
|
||||||
|
{SKILL_SKINNING, SKILL_LEATHERWORKING, 25},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 25},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 15}};
|
||||||
|
case CLASS_PRIEST:
|
||||||
|
return {{SKILL_TAILORING, SKILL_ENCHANTING, 45},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 30},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 25}};
|
||||||
|
case CLASS_MAGE:
|
||||||
|
return {{SKILL_TAILORING, SKILL_ENCHANTING, 50},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 25},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 25}};
|
||||||
|
case CLASS_WARLOCK:
|
||||||
|
default:
|
||||||
|
return {{SKILL_TAILORING, SKILL_ENCHANTING, 50},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 25},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 25}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PlayerbotFactory::WeightedProfessionPair> PlayerbotFactory::GetRandomProfessionPairs()
|
||||||
|
{
|
||||||
|
return {{SKILL_MINING, SKILL_BLACKSMITHING, 20},
|
||||||
|
{SKILL_MINING, SKILL_ENGINEERING, 18},
|
||||||
|
{SKILL_MINING, SKILL_JEWELCRAFTING, 16},
|
||||||
|
{SKILL_SKINNING, SKILL_LEATHERWORKING, 18},
|
||||||
|
{SKILL_HERBALISM, SKILL_ALCHEMY, 18},
|
||||||
|
{SKILL_HERBALISM, SKILL_INSCRIPTION, 14},
|
||||||
|
{SKILL_TAILORING, SKILL_ENCHANTING, 10},
|
||||||
|
{SKILL_HERBALISM, SKILL_MINING, 6},
|
||||||
|
{SKILL_HERBALISM, SKILL_SKINNING, 5},
|
||||||
|
{SKILL_MINING, SKILL_SKINNING, 5}};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<uint16, uint16> PlayerbotFactory::ChooseProfessionPair(
|
||||||
|
std::vector<WeightedProfessionPair> const& professionPairs)
|
||||||
|
{
|
||||||
|
uint32 totalWeight = 0;
|
||||||
|
for (WeightedProfessionPair const& pair : professionPairs)
|
||||||
|
totalWeight += pair.weight;
|
||||||
|
|
||||||
|
if (!totalWeight)
|
||||||
|
return {SKILL_HERBALISM, SKILL_ALCHEMY};
|
||||||
|
|
||||||
|
uint32 roll = urand(1, totalWeight);
|
||||||
|
for (WeightedProfessionPair const& pair : professionPairs)
|
||||||
|
{
|
||||||
|
if (roll <= pair.weight)
|
||||||
|
return {pair.firstSkill, pair.secondSkill};
|
||||||
|
|
||||||
|
roll -= pair.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeightedProfessionPair const& fallback = professionPairs.back();
|
||||||
|
return {fallback.firstSkill, fallback.secondSkill};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::HasProfessionPair(std::vector<WeightedProfessionPair> const& professionPairs,
|
||||||
|
uint16 firstSkill, uint16 secondSkill)
|
||||||
|
{
|
||||||
|
for (WeightedProfessionPair const& pair : professionPairs)
|
||||||
|
{
|
||||||
|
if (pair.firstSkill == firstSkill && pair.secondSkill == secondSkill)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 PlayerbotFactory::ChooseSingleProfession(std::vector<WeightedProfessionPair> const& professionPairs)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint16, uint32>> gatheringSkills;
|
||||||
|
std::vector<std::pair<uint16, uint32>> craftingSkills;
|
||||||
|
|
||||||
|
auto addWeightedSkill = [](std::vector<std::pair<uint16, uint32>>& skills, uint16 skillId, uint32 weight)
|
||||||
|
{
|
||||||
|
for (std::pair<uint16, uint32>& skill : skills)
|
||||||
|
{
|
||||||
|
if (skill.first == skillId)
|
||||||
|
{
|
||||||
|
skill.second += weight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skills.push_back({skillId, weight});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (WeightedProfessionPair const& pair : professionPairs)
|
||||||
|
{
|
||||||
|
if (IsGatheringTradeSkill(pair.firstSkill))
|
||||||
|
addWeightedSkill(gatheringSkills, pair.firstSkill, pair.weight);
|
||||||
|
if (IsCraftingTradeSkill(pair.firstSkill))
|
||||||
|
addWeightedSkill(craftingSkills, pair.firstSkill, pair.weight);
|
||||||
|
|
||||||
|
if (IsGatheringTradeSkill(pair.secondSkill))
|
||||||
|
addWeightedSkill(gatheringSkills, pair.secondSkill, pair.weight);
|
||||||
|
if (IsCraftingTradeSkill(pair.secondSkill))
|
||||||
|
addWeightedSkill(craftingSkills, pair.secondSkill, pair.weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<uint16, uint32>>* selectedPool = nullptr;
|
||||||
|
if (!gatheringSkills.empty() && !craftingSkills.empty())
|
||||||
|
selectedPool = urand(0, 1) == 0 ? &gatheringSkills : &craftingSkills;
|
||||||
|
else if (!gatheringSkills.empty())
|
||||||
|
selectedPool = &gatheringSkills;
|
||||||
|
else if (!craftingSkills.empty())
|
||||||
|
selectedPool = &craftingSkills;
|
||||||
|
|
||||||
|
if (!selectedPool || selectedPool->empty())
|
||||||
|
return SKILL_HERBALISM;
|
||||||
|
|
||||||
|
uint32 totalWeight = 0;
|
||||||
|
for (std::pair<uint16, uint32> const& skill : *selectedPool)
|
||||||
|
totalWeight += skill.second;
|
||||||
|
|
||||||
|
if (!totalWeight)
|
||||||
|
return selectedPool->front().first;
|
||||||
|
|
||||||
|
uint32 roll = urand(1, totalWeight);
|
||||||
|
for (std::pair<uint16, uint32> const& skill : *selectedPool)
|
||||||
|
{
|
||||||
|
if (roll <= skill.second)
|
||||||
|
return skill.first;
|
||||||
|
|
||||||
|
roll -= skill.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedPool->back().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 PlayerbotFactory::GetStoredOrRandomValue(Player* bot,
|
||||||
|
std::string const& key,
|
||||||
|
uint32 minValue,
|
||||||
|
uint32 maxValue)
|
||||||
|
{
|
||||||
|
uint32 value = sRandomPlayerbotMgr.GetValue(bot, key);
|
||||||
|
if (value < minValue || value > maxValue)
|
||||||
|
{
|
||||||
|
value = urand(minValue, maxValue);
|
||||||
|
sRandomPlayerbotMgr.SetValue(bot, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::HasAnySpell(Player* bot, std::vector<uint32> const& spells)
|
||||||
|
{
|
||||||
|
for (uint32 spellId : spells)
|
||||||
|
{
|
||||||
|
if (bot->HasSpell(spellId))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::LearnProfessionSpecialization(Player* bot,
|
||||||
|
ProfessionSpecializationSpell knownSpell,
|
||||||
|
ProfessionSpecializationSpell learnSpell)
|
||||||
|
{
|
||||||
|
uint32 const knownSpellId = static_cast<uint32>(knownSpell);
|
||||||
|
uint32 const learnSpellId = static_cast<uint32>(learnSpell);
|
||||||
|
|
||||||
|
if (bot->HasSpell(knownSpellId) || !sSpellMgr->GetSpellInfo(learnSpellId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bot->CastSpell(bot, learnSpellId, true);
|
||||||
|
return bot->HasSpell(knownSpellId);
|
||||||
|
}
|
||||||
|
|
||||||
PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality, uint32 gearScoreLimit)
|
PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality, uint32 gearScoreLimit)
|
||||||
: level(level), itemQuality(itemQuality), gearScoreLimit(gearScoreLimit), bot(bot)
|
: level(level), itemQuality(itemQuality), gearScoreLimit(gearScoreLimit), bot(bot)
|
||||||
{
|
{
|
||||||
@ -2250,69 +2510,278 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item)
|
|||||||
|
|
||||||
void PlayerbotFactory::InitTradeSkills()
|
void PlayerbotFactory::InitTradeSkills()
|
||||||
{
|
{
|
||||||
|
if (!sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 const maxPrimaryTradeSkills =
|
||||||
|
std::min<uint32>(2, sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
|
||||||
|
|
||||||
uint16 firstSkill = sRandomPlayerbotMgr.GetValue(bot, "firstSkill");
|
uint16 firstSkill = sRandomPlayerbotMgr.GetValue(bot, "firstSkill");
|
||||||
uint16 secondSkill = sRandomPlayerbotMgr.GetValue(bot, "secondSkill");
|
uint16 secondSkill = sRandomPlayerbotMgr.GetValue(bot, "secondSkill");
|
||||||
if (!firstSkill || !secondSkill)
|
ProfessionRollType professionRollType =
|
||||||
{
|
static_cast<ProfessionRollType>(sRandomPlayerbotMgr.GetValue(bot, "professionRollType"));
|
||||||
std::vector<uint32> firstSkills;
|
|
||||||
std::vector<uint32> secondSkills;
|
|
||||||
|
|
||||||
switch (bot->getClass())
|
if (professionRollType != ProfessionRollType::Class && professionRollType != ProfessionRollType::Random)
|
||||||
{
|
{
|
||||||
case CLASS_WARRIOR:
|
professionRollType = urand(1, 100) <= sPlayerbotAIConfig.classMatchingProfessionChance
|
||||||
case CLASS_PALADIN:
|
? ProfessionRollType::Class
|
||||||
case CLASS_DEATH_KNIGHT:
|
: ProfessionRollType::Random;
|
||||||
firstSkills.push_back(SKILL_MINING);
|
sRandomPlayerbotMgr.SetValue(bot, "professionRollType", static_cast<uint32>(professionRollType));
|
||||||
secondSkills.push_back(SKILL_BLACKSMITHING);
|
|
||||||
secondSkills.push_back(SKILL_ENGINEERING);
|
|
||||||
secondSkills.push_back(SKILL_JEWELCRAFTING);
|
|
||||||
break;
|
|
||||||
case CLASS_SHAMAN:
|
|
||||||
case CLASS_DRUID:
|
|
||||||
case CLASS_HUNTER:
|
|
||||||
case CLASS_ROGUE:
|
|
||||||
firstSkills.push_back(SKILL_SKINNING);
|
|
||||||
secondSkills.push_back(SKILL_LEATHERWORKING);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
firstSkills.push_back(SKILL_TAILORING);
|
|
||||||
secondSkills.push_back(SKILL_ENCHANTING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (urand(0, 6))
|
std::vector<WeightedProfessionPair> professionPairs = professionRollType == ProfessionRollType::Class
|
||||||
|
? GetClassProfessionPairs(bot)
|
||||||
|
: GetRandomProfessionPairs();
|
||||||
|
|
||||||
|
bool const hasStoredProfessionPair = firstSkill && secondSkill && firstSkill != secondSkill &&
|
||||||
|
IsPrimaryTradeSkill(firstSkill) && IsPrimaryTradeSkill(secondSkill) &&
|
||||||
|
HasProfessionPair(professionPairs, firstSkill, secondSkill);
|
||||||
|
bool const keepExistingProfessionPair = maxPrimaryTradeSkills < 2 && hasStoredProfessionPair;
|
||||||
|
|
||||||
|
if (maxPrimaryTradeSkills == 1 && !keepExistingProfessionPair)
|
||||||
{
|
{
|
||||||
case 0:
|
if (!IsPrimaryTradeSkill(firstSkill) || secondSkill != 0)
|
||||||
firstSkill = SKILL_HERBALISM;
|
{
|
||||||
secondSkill = SKILL_ALCHEMY;
|
firstSkill = ChooseSingleProfession(professionPairs);
|
||||||
break;
|
secondSkill = 0;
|
||||||
case 1:
|
|
||||||
firstSkill = SKILL_HERBALISM;
|
sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill);
|
||||||
secondSkill = SKILL_MINING;
|
sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
firstSkill = SKILL_MINING;
|
|
||||||
secondSkill = SKILL_SKINNING;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
firstSkill = SKILL_HERBALISM;
|
|
||||||
secondSkill = SKILL_SKINNING;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
firstSkill = firstSkills[urand(0, firstSkills.size() - 1)];
|
|
||||||
secondSkill = secondSkills[urand(0, secondSkills.size() - 1)];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (maxPrimaryTradeSkills == 0 && !keepExistingProfessionPair)
|
||||||
|
{
|
||||||
|
firstSkill = 0;
|
||||||
|
secondSkill = 0;
|
||||||
|
|
||||||
sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill);
|
sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill);
|
||||||
sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill);
|
sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxPrimaryTradeSkills >= 2 &&
|
||||||
|
(!firstSkill || !secondSkill || firstSkill == secondSkill || !IsPrimaryTradeSkill(firstSkill) ||
|
||||||
|
!IsPrimaryTradeSkill(secondSkill) || !HasProfessionPair(professionPairs, firstSkill, secondSkill)))
|
||||||
|
{
|
||||||
|
auto const& professionPair = ChooseProfessionPair(professionPairs);
|
||||||
|
firstSkill = professionPair.first;
|
||||||
|
secondSkill = professionPair.second;
|
||||||
|
|
||||||
|
sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill);
|
||||||
|
sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint16> primarySkills;
|
||||||
|
if (keepExistingProfessionPair)
|
||||||
|
{
|
||||||
|
primarySkills.push_back(firstSkill);
|
||||||
|
primarySkills.push_back(secondSkill);
|
||||||
|
}
|
||||||
|
else if (maxPrimaryTradeSkills > 0)
|
||||||
|
primarySkills.push_back(firstSkill);
|
||||||
|
if (!keepExistingProfessionPair && maxPrimaryTradeSkills > 1)
|
||||||
|
primarySkills.push_back(secondSkill);
|
||||||
|
|
||||||
SetRandomSkill(SKILL_FIRST_AID);
|
SetRandomSkill(SKILL_FIRST_AID);
|
||||||
SetRandomSkill(SKILL_FISHING);
|
SetRandomSkill(SKILL_FISHING);
|
||||||
SetRandomSkill(SKILL_COOKING);
|
SetRandomSkill(SKILL_COOKING);
|
||||||
|
|
||||||
SetRandomSkill(firstSkill);
|
for (uint16 skillId : primarySkills)
|
||||||
SetRandomSkill(secondSkill);
|
SetRandomSkill(skillId);
|
||||||
|
|
||||||
|
std::vector<uint16> skillsToLearn = {SKILL_FIRST_AID, SKILL_FISHING, SKILL_COOKING};
|
||||||
|
skillsToLearn.insert(skillsToLearn.end(), primarySkills.begin(), primarySkills.end());
|
||||||
|
|
||||||
|
for (uint16 skillId : skillsToLearn)
|
||||||
|
{
|
||||||
|
uint32 spellId = GetProfessionStarterSpell(skillId);
|
||||||
|
if (!spellId || bot->HasSpell(spellId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IsPrimaryTradeSkill(skillId) && !bot->GetFreePrimaryProfessionPoints() &&
|
||||||
|
!(keepExistingProfessionPair && bot->HasSkill(skillId)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bot->learnSpell(spellId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitTradeSpecializations();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerbotFactory::InitTradeSpecializations()
|
||||||
|
{
|
||||||
|
InitAlchemySpecialization();
|
||||||
|
InitEngineeringSpecialization();
|
||||||
|
InitLeatherworkingSpecialization();
|
||||||
|
InitTailoringSpecialization();
|
||||||
|
InitBlacksmithingSpecialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::InitAlchemySpecialization()
|
||||||
|
{
|
||||||
|
if (!bot->HasSkill(SKILL_ALCHEMY) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_ALCHEMY) < 325 ||
|
||||||
|
bot->GetLevel() <= 67)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HasAnySpell(bot, {static_cast<uint32>(ProfessionSpecializationSpell::Transmute),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Elixir),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Potion)}))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (GetStoredOrRandomValue(bot, "alchemySpecialization", 1, 3))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Transmute,
|
||||||
|
ProfessionSpecializationSpell::LearnTransmute);
|
||||||
|
case 2:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Elixir,
|
||||||
|
ProfessionSpecializationSpell::LearnElixir);
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Potion,
|
||||||
|
ProfessionSpecializationSpell::LearnPotion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::InitEngineeringSpecialization()
|
||||||
|
{
|
||||||
|
if (!bot->HasSkill(SKILL_ENGINEERING) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_ENGINEERING) < 200 ||
|
||||||
|
bot->GetLevel() < 30)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HasAnySpell(bot, {static_cast<uint32>(ProfessionSpecializationSpell::Goblin),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Gnomish)}))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (GetStoredOrRandomValue(bot, "engineeringSpecialization", 1, 2))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Goblin,
|
||||||
|
ProfessionSpecializationSpell::LearnGoblin);
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Gnomish,
|
||||||
|
ProfessionSpecializationSpell::LearnGnomish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::InitLeatherworkingSpecialization()
|
||||||
|
{
|
||||||
|
if (!bot->HasSkill(SKILL_LEATHERWORKING) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_LEATHERWORKING) < 225 ||
|
||||||
|
bot->GetLevel() <= 40)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HasAnySpell(bot, {static_cast<uint32>(ProfessionSpecializationSpell::Dragon),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Elemental),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Tribal)}))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (GetStoredOrRandomValue(bot, "leatherSpecialization", 1, 3))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Dragon,
|
||||||
|
ProfessionSpecializationSpell::LearnDragon);
|
||||||
|
case 2:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Elemental,
|
||||||
|
ProfessionSpecializationSpell::LearnElemental);
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Tribal,
|
||||||
|
ProfessionSpecializationSpell::LearnTribal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::InitTailoringSpecialization()
|
||||||
|
{
|
||||||
|
if (!bot->HasSkill(SKILL_TAILORING) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_TAILORING) < 350 ||
|
||||||
|
bot->GetLevel() <= 59)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (HasAnySpell(bot, {static_cast<uint32>(ProfessionSpecializationSpell::Spellfire),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Mooncloth),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Shadoweave)}))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (GetStoredOrRandomValue(bot, "tailorSpecialization", 1, 3))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Spellfire,
|
||||||
|
ProfessionSpecializationSpell::LearnSpellfire);
|
||||||
|
case 2:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Mooncloth,
|
||||||
|
ProfessionSpecializationSpell::LearnMooncloth);
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Shadoweave,
|
||||||
|
ProfessionSpecializationSpell::LearnShadoweave);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotFactory::InitBlacksmithingSpecialization()
|
||||||
|
{
|
||||||
|
bool learnedSpecialization = false;
|
||||||
|
|
||||||
|
if (!bot->HasSkill(SKILL_BLACKSMITHING) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_BLACKSMITHING) < 225)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!bot->HasSpell(static_cast<uint32>(ProfessionSpecializationSpell::Armor)) &&
|
||||||
|
!bot->HasSpell(static_cast<uint32>(ProfessionSpecializationSpell::Weapon)))
|
||||||
|
{
|
||||||
|
switch (GetStoredOrRandomValue(bot, "blacksmithSpecialization", 1, 2))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
learnedSpecialization = LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Armor,
|
||||||
|
ProfessionSpecializationSpell::LearnArmor);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
learnedSpecialization = LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Weapon,
|
||||||
|
ProfessionSpecializationSpell::LearnWeapon);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bot->HasSpell(static_cast<uint32>(ProfessionSpecializationSpell::Weapon)) ||
|
||||||
|
bot->GetBaseSkillValue(SKILL_BLACKSMITHING) < 250 ||
|
||||||
|
bot->GetLevel() <= 49 ||
|
||||||
|
HasAnySpell(bot, {static_cast<uint32>(ProfessionSpecializationSpell::Hammer),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Axe),
|
||||||
|
static_cast<uint32>(ProfessionSpecializationSpell::Sword)}))
|
||||||
|
return learnedSpecialization;
|
||||||
|
|
||||||
|
switch (GetStoredOrRandomValue(bot, "blacksmithWeaponSpecialization", 1, 3))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Hammer,
|
||||||
|
ProfessionSpecializationSpell::LearnHammer);
|
||||||
|
case 2:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Axe,
|
||||||
|
ProfessionSpecializationSpell::LearnAxe);
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
return LearnProfessionSpecialization(bot,
|
||||||
|
ProfessionSpecializationSpell::Sword,
|
||||||
|
ProfessionSpecializationSpell::LearnSword);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotFactory::UpdateTradeSkills()
|
void PlayerbotFactory::UpdateTradeSkills()
|
||||||
@ -2456,6 +2925,9 @@ void PlayerbotFactory::InitSkills()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitTradeSkills();
|
||||||
|
InitInventorySkill();
|
||||||
|
|
||||||
// switch (bot->getClass())
|
// switch (bot->getClass())
|
||||||
// {
|
// {
|
||||||
// case CLASS_WARRIOR:
|
// case CLASS_WARRIOR:
|
||||||
@ -3804,31 +4276,22 @@ void PlayerbotFactory::InitInventory()
|
|||||||
|
|
||||||
void PlayerbotFactory::InitInventorySkill()
|
void PlayerbotFactory::InitInventorySkill()
|
||||||
{
|
{
|
||||||
if (bot->HasSkill(SKILL_MINING))
|
if (bot->HasSkill(SKILL_MINING) && !bot->HasItemCount(2901, 1, true))
|
||||||
{
|
|
||||||
StoreItem(2901, 1); // Mining Pick
|
StoreItem(2901, 1); // Mining Pick
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->HasSkill(SKILL_BLACKSMITHING) || bot->HasSkill(SKILL_ENGINEERING))
|
if ((bot->HasSkill(SKILL_BLACKSMITHING) || bot->HasSkill(SKILL_ENGINEERING)) &&
|
||||||
{
|
!bot->HasItemCount(5956, 1, true))
|
||||||
StoreItem(5956, 1); // Blacksmith Hammer
|
StoreItem(5956, 1); // Blacksmith Hammer
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->HasSkill(SKILL_ENGINEERING))
|
if (bot->HasSkill(SKILL_ENGINEERING) && !bot->HasItemCount(6219, 1, true))
|
||||||
{
|
|
||||||
StoreItem(6219, 1); // Arclight Spanner
|
StoreItem(6219, 1); // Arclight Spanner
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->HasSkill(SKILL_ENCHANTING))
|
if (bot->HasSkill(SKILL_ENCHANTING) && !bot->HasItemCount(16207, 1, true))
|
||||||
{
|
|
||||||
StoreItem(16207, 1); // Runed Arcanite Rod
|
StoreItem(16207, 1); // Runed Arcanite Rod
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->HasSkill(SKILL_SKINNING))
|
if (bot->HasSkill(SKILL_SKINNING) && !bot->HasItemCount(7005, 1, true))
|
||||||
{
|
|
||||||
StoreItem(7005, 1); // Skinning Knife
|
StoreItem(7005, 1); // Skinning Knife
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Item* PlayerbotFactory::StoreItem(uint32 itemId, uint32 count)
|
Item* PlayerbotFactory::StoreItem(uint32 itemId, uint32 count)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,9 @@
|
|||||||
#ifndef _PLAYERBOT_PLAYERBOTFACTORY_H
|
#ifndef _PLAYERBOT_PLAYERBOTFACTORY_H
|
||||||
#define _PLAYERBOT_PLAYERBOTFACTORY_H
|
#define _PLAYERBOT_PLAYERBOTFACTORY_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "InventoryAction.h"
|
#include "InventoryAction.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
@ -87,12 +90,91 @@ public:
|
|||||||
void InitAttunementQuests();
|
void InitAttunementQuests();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class ProfessionSpecializationSpell : uint32
|
||||||
|
{
|
||||||
|
Weapon = 9787,
|
||||||
|
Armor = 9788,
|
||||||
|
Hammer = 17040,
|
||||||
|
Axe = 17041,
|
||||||
|
Sword = 17039,
|
||||||
|
|
||||||
|
LearnWeapon = 9789,
|
||||||
|
LearnArmor = 9790,
|
||||||
|
LearnHammer = 39099,
|
||||||
|
LearnAxe = 39098,
|
||||||
|
LearnSword = 39097,
|
||||||
|
|
||||||
|
Dragon = 10656,
|
||||||
|
Elemental = 10658,
|
||||||
|
Tribal = 10660,
|
||||||
|
|
||||||
|
LearnDragon = 10657,
|
||||||
|
LearnElemental = 10659,
|
||||||
|
LearnTribal = 10661,
|
||||||
|
|
||||||
|
Spellfire = 26797,
|
||||||
|
Mooncloth = 26798,
|
||||||
|
Shadoweave = 26801,
|
||||||
|
|
||||||
|
Goblin = 20222,
|
||||||
|
Gnomish = 20219,
|
||||||
|
|
||||||
|
LearnGoblin = 20221,
|
||||||
|
LearnGnomish = 20220,
|
||||||
|
|
||||||
|
LearnSpellfire = 26796,
|
||||||
|
LearnMooncloth = 26799,
|
||||||
|
LearnShadoweave = 26800,
|
||||||
|
|
||||||
|
Transmute = 28672,
|
||||||
|
Elixir = 28677,
|
||||||
|
Potion = 28675,
|
||||||
|
|
||||||
|
LearnTransmute = 28674,
|
||||||
|
LearnElixir = 28678,
|
||||||
|
LearnPotion = 28676
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ProfessionRollType : uint32
|
||||||
|
{
|
||||||
|
Random = 1,
|
||||||
|
Class = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WeightedProfessionPair
|
||||||
|
{
|
||||||
|
uint16 firstSkill;
|
||||||
|
uint16 secondSkill;
|
||||||
|
uint32 weight;
|
||||||
|
};
|
||||||
|
|
||||||
void Prepare();
|
void Prepare();
|
||||||
// void InitSecondEquipmentSet();
|
// void InitSecondEquipmentSet();
|
||||||
// void InitEquipmentNew(bool incremental);
|
// void InitEquipmentNew(bool incremental);
|
||||||
bool CanEquipItem(ItemTemplate const* proto);
|
bool CanEquipItem(ItemTemplate const* proto);
|
||||||
bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item);
|
bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item);
|
||||||
|
static bool IsPrimaryTradeSkill(uint16 skillId);
|
||||||
|
static bool IsGatheringTradeSkill(uint16 skillId);
|
||||||
|
static bool IsCraftingTradeSkill(uint16 skillId);
|
||||||
|
static uint32 GetProfessionStarterSpell(uint16 skillId);
|
||||||
|
static std::vector<WeightedProfessionPair> GetClassProfessionPairs(Player* bot);
|
||||||
|
static std::vector<WeightedProfessionPair> GetRandomProfessionPairs();
|
||||||
|
static std::pair<uint16, uint16> ChooseProfessionPair(std::vector<WeightedProfessionPair> const& professionPairs);
|
||||||
|
static bool HasProfessionPair(std::vector<WeightedProfessionPair> const& professionPairs,
|
||||||
|
uint16 firstSkill, uint16 secondSkill);
|
||||||
|
static uint16 ChooseSingleProfession(std::vector<WeightedProfessionPair> const& professionPairs);
|
||||||
|
static uint32 GetStoredOrRandomValue(Player* bot, std::string const& key, uint32 minValue, uint32 maxValue);
|
||||||
|
static bool HasAnySpell(Player* bot, std::vector<uint32> const& spells);
|
||||||
|
static bool LearnProfessionSpecialization(Player* bot,
|
||||||
|
ProfessionSpecializationSpell knownSpell,
|
||||||
|
ProfessionSpecializationSpell learnSpell);
|
||||||
void InitTradeSkills();
|
void InitTradeSkills();
|
||||||
|
void InitTradeSpecializations();
|
||||||
|
bool InitAlchemySpecialization();
|
||||||
|
bool InitEngineeringSpecialization();
|
||||||
|
bool InitLeatherworkingSpecialization();
|
||||||
|
bool InitTailoringSpecialization();
|
||||||
|
bool InitBlacksmithingSpecialization();
|
||||||
void UpdateTradeSkills();
|
void UpdateTradeSkills();
|
||||||
void SetRandomSkill(uint16 id);
|
void SetRandomSkill(uint16 id);
|
||||||
void ClearSpells();
|
void ClearSpells();
|
||||||
|
|||||||
@ -243,10 +243,22 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
nextAICheckDelay = 0;
|
nextAICheckDelay = 0;
|
||||||
|
|
||||||
// Early return if bot is in invalid state
|
// Early return if bot is in invalid state
|
||||||
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() ||
|
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() || bot->IsDuringRemoveFromWorld())
|
||||||
bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// During timed logout countdown, cancel if bot enters combat (this cancellation is handled client-side for real players).
|
||||||
|
if (bot->GetSession()->isLogingOut())
|
||||||
|
{
|
||||||
|
bool canLogoutInCombat = bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
|
||||||
|
if (bot->IsInCombat() && !canLogoutInCombat)
|
||||||
|
{
|
||||||
|
WorldPackets::Character::LogoutCancel cancelData = WorldPacket(CMSG_LOGOUT_CANCEL);
|
||||||
|
bot->GetSession()->HandleLogoutCancelOpcode(cancelData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle cheat options (set bot health and power if cheats are enabled)
|
// Handle cheat options (set bot health and power if cheats are enabled)
|
||||||
if (bot->IsAlive() &&
|
if (bot->IsAlive() &&
|
||||||
(static_cast<uint32>(GetCheat()) > 0 || static_cast<uint32>(sPlayerbotAIConfig.botCheatMask) > 0))
|
(static_cast<uint32>(GetCheat()) > 0 || static_cast<uint32>(sPlayerbotAIConfig.botCheatMask) > 0))
|
||||||
@ -266,15 +278,26 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
if (!CanUpdateAI())
|
if (!CanUpdateAI())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Handle the current spell
|
// Handle a spell that is still in its preparing phase (including channeled spells).
|
||||||
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
if (!currentSpell)
|
if (!currentSpell)
|
||||||
currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||||
|
|
||||||
if (currentSpell)
|
if (currentSpell)
|
||||||
{
|
{
|
||||||
|
if (currentSpell->getState() == SPELL_STATE_PREPARING)
|
||||||
|
{
|
||||||
|
// Allow external scripts to interrupt a cast in progress
|
||||||
|
if (spellInterruptRequested)
|
||||||
|
{
|
||||||
|
spellInterruptRequested = false;
|
||||||
|
InterruptSpell();
|
||||||
|
YieldThread(bot, GetReactDelay());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const SpellInfo* spellInfo = currentSpell->GetSpellInfo();
|
const SpellInfo* spellInfo = currentSpell->GetSpellInfo();
|
||||||
if (spellInfo && currentSpell->getState() == SPELL_STATE_PREPARING)
|
if (spellInfo)
|
||||||
{
|
{
|
||||||
Unit* spellTarget = currentSpell->m_targets.GetUnitTarget();
|
Unit* spellTarget = currentSpell->m_targets.GetUnitTarget();
|
||||||
// Interrupt if target is dead or spell can't target dead units
|
// Interrupt if target is dead or spell can't target dead units
|
||||||
@ -338,6 +361,22 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spellInterruptRequested)
|
||||||
|
{
|
||||||
|
// At this point the preparing-cast branch above did not consume the request.
|
||||||
|
// Interrupt a current channel if one still exists; otherwise, clear the stale request.
|
||||||
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||||
|
{
|
||||||
|
spellInterruptRequested = false;
|
||||||
|
InterruptSpell();
|
||||||
|
YieldThread(bot, GetReactDelay());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spellInterruptRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle transport check delay
|
// Handle transport check delay
|
||||||
if (nextTransportCheck > elapsed)
|
if (nextTransportCheck > elapsed)
|
||||||
@ -688,30 +727,9 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr
|
|||||||
Reset(true);
|
Reset(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: missing implementation to port
|
// Commented-out logout commands blocks removed from here and implemented in HandleCommand.
|
||||||
/*else if (filtered == "logout")
|
// Remaining is a commented-out action delay command block.
|
||||||
{
|
/*
|
||||||
if (!(bot->IsStunnedByLogout() || bot->GetSession()->isLogingOut()))
|
|
||||||
{
|
|
||||||
if (type == CHAT_MSG_WHISPER)
|
|
||||||
TellPlayer(&fromPlayer, BOT_TEXT("logout_start"));
|
|
||||||
|
|
||||||
if (master && master->GetPlayerbotMgr())
|
|
||||||
SetShouldLogOut(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (filtered == "logout cancel")
|
|
||||||
{
|
|
||||||
if (bot->IsStunnedByLogout() || bot->GetSession()->isLogingOut())
|
|
||||||
{
|
|
||||||
if (type == CHAT_MSG_WHISPER)
|
|
||||||
TellPlayer(&fromPlayer, BOT_TEXT("logout_cancel"));
|
|
||||||
|
|
||||||
WorldPacket p;
|
|
||||||
bot->GetSession()->HandleLogoutCancelOpcode(p);
|
|
||||||
SetShouldLogOut(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((filtered.size() > 5) && (filtered.substr(0, 5) == "wait ") && (filtered.find("wait for attack") ==
|
else if ((filtered.size() > 5) && (filtered.substr(0, 5) == "wait ") && (filtered.find("wait for attack") ==
|
||||||
std::string::npos))
|
std::string::npos))
|
||||||
{
|
{
|
||||||
@ -1057,7 +1075,7 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
|
|||||||
TellMaster(message);
|
TellMaster(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (filtered == "logout cancel")
|
else if (filtered == "cancel logout" || filtered == "logout cancel")
|
||||||
{
|
{
|
||||||
if (!bot->GetSession()->isLogingOut())
|
if (!bot->GetSession()->isLogingOut())
|
||||||
return;
|
return;
|
||||||
@ -1073,10 +1091,8 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
|
|||||||
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
chatCommands.push_back(ChatCommandHolder(filtered, fromPlayer, type));
|
chatCommands.push_back(ChatCommandHolder(filtered, fromPlayer, type));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||||
{
|
{
|
||||||
@ -1558,7 +1574,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
static const std::vector<std::string> allInstanceStrategies =
|
static const std::vector<std::string> allInstanceStrategies =
|
||||||
{
|
{
|
||||||
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore",
|
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore",
|
||||||
"naxx", "onyxia", "ssc", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
"naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
||||||
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
||||||
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
||||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
||||||
@ -1598,7 +1614,10 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
strategyName = "ssc"; // Serpentshrine Cavern
|
strategyName = "ssc"; // Serpentshrine Cavern
|
||||||
break;
|
break;
|
||||||
case 550:
|
case 550:
|
||||||
strategyName = "tempestkeep"; // Tempest Keep
|
strategyName = "tempestkeep"; // Tempest Keep: The Eye
|
||||||
|
break;
|
||||||
|
case 558:
|
||||||
|
strategyName = "tbc-ac"; // Auchindoun: Auchenai Crypts
|
||||||
break;
|
break;
|
||||||
case 565:
|
case 565:
|
||||||
strategyName = "gruulslair"; // Gruul's Lair
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
@ -1776,6 +1795,11 @@ bool PlayerbotAI::ContainsStrategy(StrategyType type)
|
|||||||
|
|
||||||
bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); }
|
bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); }
|
||||||
|
|
||||||
|
Strategy* PlayerbotAI::GetStrategy(std::string const name, BotState type)
|
||||||
|
{
|
||||||
|
return engines[type] ? engines[type]->GetStrategy(name) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerbotAI::ResetStrategies(bool load)
|
void PlayerbotAI::ResetStrategies(bool load)
|
||||||
{
|
{
|
||||||
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
|
for (uint8 i = 0; i < BOT_STATE_MAX; i++)
|
||||||
@ -4189,6 +4213,19 @@ void PlayerbotAI::RemoveAura(std::string const name)
|
|||||||
bot->RemoveAurasDueToSpell(spellid);
|
bot->RemoveAurasDueToSpell(spellid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerbotAI::RequestSpellInterrupt()
|
||||||
|
{
|
||||||
|
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||||
|
if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING)
|
||||||
|
{
|
||||||
|
spellInterruptRequested = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||||
|
spellInterruptRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell)
|
bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell)
|
||||||
{
|
{
|
||||||
if (!IsValidUnit(target))
|
if (!IsValidUnit(target))
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PLAYERBOT_PLAYERbotAI_H
|
#ifndef _PLAYERBOT_PLAYERBOTAI_H
|
||||||
#define _PLAYERBOT_PLAYERbotAI_H
|
#define _PLAYERBOT_PLAYERBOTAI_H
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
@ -405,6 +405,7 @@ public:
|
|||||||
void ChangeStrategy(std::string const name, BotState type);
|
void ChangeStrategy(std::string const name, BotState type);
|
||||||
void ClearStrategies(BotState type);
|
void ClearStrategies(BotState type);
|
||||||
std::vector<std::string> GetStrategies(BotState type);
|
std::vector<std::string> GetStrategies(BotState type);
|
||||||
|
Strategy* GetStrategy(std::string const name, BotState type);
|
||||||
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
|
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
|
||||||
void EvaluateHealerDpsStrategy();
|
void EvaluateHealerDpsStrategy();
|
||||||
bool ContainsStrategy(StrategyType type);
|
bool ContainsStrategy(StrategyType type);
|
||||||
@ -471,6 +472,7 @@ public:
|
|||||||
void SpellInterrupted(uint32 spellid);
|
void SpellInterrupted(uint32 spellid);
|
||||||
int32 CalculateGlobalCooldown(uint32 spellid);
|
int32 CalculateGlobalCooldown(uint32 spellid);
|
||||||
void InterruptSpell();
|
void InterruptSpell();
|
||||||
|
void RequestSpellInterrupt();
|
||||||
void RemoveAura(std::string const name);
|
void RemoveAura(std::string const name);
|
||||||
void RemoveShapeshift();
|
void RemoveShapeshift();
|
||||||
void WaitForSpellCast(Spell* spell);
|
void WaitForSpellCast(Spell* spell);
|
||||||
@ -647,6 +649,7 @@ protected:
|
|||||||
BotCheatMask cheatMask = BotCheatMask::none;
|
BotCheatMask cheatMask = BotCheatMask::none;
|
||||||
Position jumpDestination = Position();
|
Position jumpDestination = Position();
|
||||||
uint32 nextTransportCheck = 0;
|
uint32 nextTransportCheck = 0;
|
||||||
|
bool spellInterruptRequested = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -64,7 +64,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_set<ObjectGuid> BotInitGuard::botsBeingInitialized;
|
std::unordered_set<ObjectGuid> BotInitGuard::botsBeingInitialized;
|
||||||
std::unordered_set<ObjectGuid> PlayerbotHolder::botLoading;
|
std::unordered_map<ObjectGuid, uint32> PlayerbotHolder::botLoading;
|
||||||
|
|
||||||
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
|
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
|
||||||
class PlayerbotLoginQueryHolder : public LoginQueryHolder
|
class PlayerbotLoginQueryHolder : public LoginQueryHolder
|
||||||
@ -121,7 +121,13 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
|||||||
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
|
uint32 loadingForMaster = 0;
|
||||||
|
for (auto const& [guid, acctId] : botLoading)
|
||||||
|
{
|
||||||
|
if (acctId == masterAccountId)
|
||||||
|
++loadingForMaster;
|
||||||
|
}
|
||||||
|
uint32 count = mgr->GetPlayerbotsCount() + loadingForMaster;
|
||||||
if (count >= PlayerbotAIConfig::instance().maxAddedBots)
|
if (count >= PlayerbotAIConfig::instance().maxAddedBots)
|
||||||
{
|
{
|
||||||
allowed = false;
|
allowed = false;
|
||||||
@ -144,7 +150,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
botLoading.insert(playerGuid);
|
botLoading.emplace(playerGuid, masterAccountId);
|
||||||
|
|
||||||
// Always login in with world session to avoid race condition
|
// Always login in with world session to avoid race condition
|
||||||
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||||
@ -293,6 +299,11 @@ void PlayerbotHolder::LogoutAllBots()
|
|||||||
if (!botAI || botAI->IsRealPlayer())
|
if (!botAI || botAI->IsRealPlayer())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// If bot is mid-countdown, cancel the timer so LogoutPlayerBot proceeds immediately.
|
||||||
|
WorldSession* session = bot->GetSession();
|
||||||
|
if (session && session->isLogingOut())
|
||||||
|
session->SetLogoutStartTime(0);
|
||||||
|
|
||||||
LogoutPlayerBot(bot->GetGUID());
|
LogoutPlayerBot(bot->GetGUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,36 +366,50 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
WorldSession* botWorldSessionPtr = bot->GetSession();
|
WorldSession* botWorldSessionPtr = bot->GetSession();
|
||||||
WorldSession* masterWorldSessionPtr = nullptr;
|
WorldSession* masterWorldSessionPtr = nullptr;
|
||||||
|
|
||||||
|
// If already in timed logout countdown, complete it once the 20-second timer expires.
|
||||||
if (botWorldSessionPtr->isLogingOut())
|
if (botWorldSessionPtr->isLogingOut())
|
||||||
|
{
|
||||||
|
if (botWorldSessionPtr->ShouldLogOut(time(nullptr)))
|
||||||
|
{
|
||||||
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"goodbye", "Goodbye!", {});
|
||||||
|
botAI->TellMaster(message);
|
||||||
|
RemoveFromPlayerbotsMap(guid);
|
||||||
|
botWorldSessionPtr->LogoutPlayer(true);
|
||||||
|
delete botWorldSessionPtr;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Player* master = botAI->GetMaster();
|
Player* master = botAI->GetMaster();
|
||||||
if (master)
|
if (master)
|
||||||
masterWorldSessionPtr = master->GetSession();
|
masterWorldSessionPtr = master->GetSession();
|
||||||
|
|
||||||
// TODO: Review whether or not to implement timed logout.
|
// Instant logout checking:
|
||||||
// Unused block. Useful only for timed logout.
|
bool logout =
|
||||||
/*
|
bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ||
|
||||||
// check for instant logout
|
bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||||
bool logout = botWorldSessionPtr->ShouldLogOut(time(nullptr));
|
(masterWorldSessionPtr && !masterWorldSessionPtr->GetPlayer()) ||
|
||||||
|
// Master's socket is already gone (EXIT GAME -> EXIT NOW is the most typical cause).
|
||||||
if (masterWorldSessionPtr && masterWorldSessionPtr->ShouldLogOut(time(nullptr)))
|
// Force instant logout. Without this, the bot restarts its 20-second countdown and fires LogoutPlayer() 20 seconds
|
||||||
logout = true;
|
// after the master's Player object has been deleted, causing the bot's logout to crash on the now deleted master.
|
||||||
|
(masterWorldSessionPtr && masterWorldSessionPtr->IsSocketClosed()) ||
|
||||||
if (masterWorldSessionPtr && !masterWorldSessionPtr->GetPlayer())
|
(masterWorldSessionPtr && masterWorldSessionPtr->ShouldLogOut(time(nullptr))) ||
|
||||||
logout = true;
|
// If the bot's master has security clearance for `InstantLogout` in worldserver.conf, so does the bot.
|
||||||
|
(master &&
|
||||||
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
(master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ||
|
||||||
botWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))
|
master->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||||
logout = true;
|
|
||||||
|
|
||||||
if (master &&
|
|
||||||
(master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || master->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
|
||||||
(masterWorldSessionPtr &&
|
(masterWorldSessionPtr &&
|
||||||
masterWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))))
|
masterWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT))));
|
||||||
logout = true;
|
|
||||||
*/
|
if (!logout)
|
||||||
// Instant logout (the only option right now)
|
{
|
||||||
|
// Start the 20-second logout countdown. CancelLogout() can interrupt this.
|
||||||
|
WorldPackets::Character::LogoutRequest data = WorldPacket(CMSG_LOGOUT_REQUEST);
|
||||||
|
botWorldSessionPtr->HandleLogoutRequestOpcode(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
std::string message = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
"goodbye", "Goodbye!", {});
|
"goodbye", "Goodbye!", {});
|
||||||
@ -1472,6 +1497,15 @@ void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
|||||||
{
|
{
|
||||||
SetNextCheckDelay(sPlayerbotAIConfig.reactDelay);
|
SetNextCheckDelay(sPlayerbotAIConfig.reactDelay);
|
||||||
CheckTellErrors(elapsed);
|
CheckTellErrors(elapsed);
|
||||||
|
|
||||||
|
// Complete timed logouts for added bots once the 20-second countdown has elapsed.
|
||||||
|
std::vector<ObjectGuid> expiredLogouts;
|
||||||
|
for (auto const& [botGuid, bot] : playerBots)
|
||||||
|
if (bot && bot->GetSession() && bot->GetSession()->ShouldLogOut(time(nullptr)))
|
||||||
|
expiredLogouts.push_back(botGuid);
|
||||||
|
|
||||||
|
for (ObjectGuid const& guid : expiredLogouts)
|
||||||
|
LogoutPlayerBot(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
||||||
|
|||||||
@ -57,7 +57,7 @@ protected:
|
|||||||
virtual void OnBotLoginInternal(Player* const bot) = 0;
|
virtual void OnBotLoginInternal(Player* const bot) = 0;
|
||||||
|
|
||||||
PlayerBotMap playerBots;
|
PlayerBotMap playerBots;
|
||||||
static std::unordered_set<ObjectGuid> botLoading;
|
static std::unordered_map<ObjectGuid, uint32> botLoading;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerbotMgr : public PlayerbotHolder
|
class PlayerbotMgr : public PlayerbotHolder
|
||||||
|
|||||||
@ -167,11 +167,13 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
pvpProhibitedZoneIds);
|
pvpProhibitedZoneIds);
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
||||||
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"),
|
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,"
|
||||||
|
"3973,4085,4086,4087,4088"),
|
||||||
pvpProhibitedAreaIds);
|
pvpProhibitedAreaIds);
|
||||||
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,"
|
||||||
|
"13188,13189,24499,24511,24710,24712"),
|
||||||
randomBotQuestIds);
|
randomBotQuestIds);
|
||||||
|
|
||||||
LoadSet<std::set<uint32>>(
|
LoadSet<std::set<uint32>>(
|
||||||
@ -181,7 +183,8 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
"165739,165738,175245,175970,176325,176327,123329,2560"),
|
"165739,165738,175245,175970,176325,176327,123329,2560"),
|
||||||
disallowedGameObjects);
|
disallowedGameObjects);
|
||||||
LoadSet<std::set<uint32>>(
|
LoadSet<std::set<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"),
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,"
|
||||||
|
"10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"),
|
||||||
attunementQuests);
|
attunementQuests);
|
||||||
|
|
||||||
LoadSet<std::set<uint32>>(
|
LoadSet<std::set<uint32>>(
|
||||||
@ -233,6 +236,8 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
||||||
|
|
||||||
//////////////////////////// Professions
|
//////////////////////////// Professions
|
||||||
|
classMatchingProfessionChance =
|
||||||
|
std::min<uint32>(100, sConfigMgr->GetOption<uint32>("AiPlayerbot.ClassMatchingProfessionChance", 30));
|
||||||
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
|
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
|
||||||
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
|
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
|
||||||
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
|
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
|
||||||
|
|||||||
@ -152,6 +152,7 @@ public:
|
|||||||
|
|
||||||
// Professions
|
// Professions
|
||||||
bool enableFishingWithMaster;
|
bool enableFishingWithMaster;
|
||||||
|
uint32 classMatchingProfessionChance;
|
||||||
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
|
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user