mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-14 09:29:09 +00:00
Compare commits
1 Commits
opcode-cra
...
hermensbas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c191edf280 |
26
README.md
26
README.md
@@ -2,8 +2,6 @@
|
|||||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
|
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
|
||||||
|
|
|
|
||||||
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
|
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
|
||||||
|
|
|
||||||
<a href="https://github.com/brighton-chi/mod-playerbots/blob/readme/README_ES.md">Español</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
@@ -18,25 +16,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Playerbots Module
|
# Playerbots Module
|
||||||
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot).
|
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot). Features include:
|
||||||
|
|
||||||
Features include:
|
- Bots that utilize real player data, allowing players to interact with their other characters, form parties, level up, and more;
|
||||||
|
- Random bots that wander through the world and behave like players, simulating the MMO experience;
|
||||||
- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more;
|
- Bots capable of running raids and battlegrounds;
|
||||||
- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience;
|
|
||||||
- Bots capable of running most raids and battlegrounds;
|
|
||||||
- Highly configurable settings to define how bots behave;
|
- Highly configurable settings to define how bots behave;
|
||||||
- Excellent performance, even when running thousands of bots.
|
- Excellent performance, even when running thousands of bots.
|
||||||
|
|
||||||
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/liyunfan1223/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
|
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/liyunfan1223/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
|
||||||
|
|
||||||
`mod-playerbots` has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
|
**Playerbots Module** has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Classic Installation
|
### Classic Installation
|
||||||
|
|
||||||
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
|
`mod-playerbots` requires a custom branch of AzerothCore to work: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
|
||||||
@@ -85,21 +81,21 @@ Use `docker compose up -d --build` to build and run the server. For more informa
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
|
The [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki) contains an extensive overview of addons, commands, and recommended configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
|
||||||
|
|
||||||
## Frequently Asked Questions
|
## Frequently Asked Questions
|
||||||
|
|
||||||
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
|
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
|
||||||
- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support.
|
- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support.
|
||||||
- **Why isn't my source compiling?** Please ensure that you are compiling with the required [custom branch of AzerothCore](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/liyunfan1223/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
|
- **Why isn't my source compiling?** Please [check the build status of our CI](https://github.com/liyunfan1223/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
|
||||||
|
|
||||||
## Addons
|
## Addons
|
||||||
|
|
||||||
Typically, bots are controlled via chat commands. For larger bot groups, this can be unwieldy. As an alternative, community members have developed client Add-Ons to allow controlling bots through the in-game UI. We recommend you check out their projects:
|
Typically, bots are controlled via chat commands. For larger bot groups, this can be unwieldy. As an alternative, community members have developed client Add-Ons to allow controlling bots through the in-game UI. We recommend you check out their projects:
|
||||||
|
|
||||||
- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio), which includes English, Chinese, French, German, Korean, Russian, and Spanish support [note: active development is temporarily continuing on a fork in Macx-Lio's absence (https://github.com/Wishmaster117/MultiBot)]
|
- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio)
|
||||||
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan) [note: no longer under active development]
|
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan)
|
||||||
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision) [note: no longer under active development]
|
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision)
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ AiPlayerbot.SummonWhenGroup = 1
|
|||||||
# Selfbot permission level (0 = disabled, 1 = GM only (default), 2 = all players, 3 = activate on login)
|
# Selfbot permission level (0 = disabled, 1 = GM only (default), 2 = all players, 3 = activate on login)
|
||||||
AiPlayerbot.SelfBotLevel = 1
|
AiPlayerbot.SelfBotLevel = 1
|
||||||
|
|
||||||
|
# Give free food to bots
|
||||||
|
# Default: 1 (enabled)
|
||||||
|
AiPlayerbot.FreeFood = 1
|
||||||
|
|
||||||
# Non-GM player can only use init=auto to initialize bots based on their own level and gearscore
|
# Non-GM player can only use init=auto to initialize bots based on their own level and gearscore
|
||||||
# Default: 0 (non-GM player can use any intialization commands)
|
# Default: 0 (non-GM player can use any intialization commands)
|
||||||
AiPlayerbot.AutoInitOnly = 0
|
AiPlayerbot.AutoInitOnly = 0
|
||||||
@@ -492,7 +496,6 @@ AiPlayerbot.AutoGearQualityLimit = 3
|
|||||||
AiPlayerbot.AutoGearScoreLimit = 0
|
AiPlayerbot.AutoGearScoreLimit = 0
|
||||||
|
|
||||||
# Enable/Disable cheats for bots
|
# Enable/Disable cheats for bots
|
||||||
# "food" (bots eat or drink without using food or drinks from their inventory)
|
|
||||||
# "gold" (bots have infinite gold)
|
# "gold" (bots have infinite gold)
|
||||||
# "health" (bots have infinite health)
|
# "health" (bots have infinite health)
|
||||||
# "mana" (bots have infinite mana)
|
# "mana" (bots have infinite mana)
|
||||||
@@ -501,7 +504,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
|||||||
# "raid" (bots use cheats implemented into raid strategies)
|
# "raid" (bots use cheats implemented into raid strategies)
|
||||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
||||||
# Default: taxi and raid are enabled
|
# Default: taxi and raid are enabled
|
||||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
AiPlayerbot.BotCheats = "taxi,raid"
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -659,7 +662,7 @@ AiPlayerbot.RandomGearScoreLimit = 0
|
|||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.IncrementalGearInit = 1
|
AiPlayerbot.IncrementalGearInit = 1
|
||||||
|
|
||||||
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
|
# Set minimum level of bots that will enchant their equipment (Maxlevel + 1 to disable)
|
||||||
# Default: 60
|
# Default: 60
|
||||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||||
|
|
||||||
@@ -696,16 +699,6 @@ AiPlayerbot.AutoUpgradeEquip = 1
|
|||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.HunterWolfPet = 0
|
AiPlayerbot.HunterWolfPet = 0
|
||||||
|
|
||||||
# Default pet stance when a bot summons a pet
|
|
||||||
# 0 = Passive, 1 = Defensive, 2 = Aggressive
|
|
||||||
# Default: 1 (Defensive)
|
|
||||||
AiPlayerbot.DefaultPetStance = 1
|
|
||||||
|
|
||||||
# Enable/disable debug messages about pet commands
|
|
||||||
# 0 = Disabled, 1 = Enabled
|
|
||||||
# Default = 0 (disabled)
|
|
||||||
AiPlayerbot.PetChatCommandDebug = 0
|
|
||||||
|
|
||||||
# Prohibit hunter bots from creating pets with any family ID listed below in ExcludedHunterPetFamilies
|
# Prohibit hunter bots from creating pets with any family ID listed below in ExcludedHunterPetFamilies
|
||||||
# See the creature_family database table for all pet families by ID (note: ID for spiders is 3)
|
# See the creature_family database table for all pet families by ID (note: ID for spiders is 3)
|
||||||
AiPlayerbot.ExcludedHunterPetFamilies = ""
|
AiPlayerbot.ExcludedHunterPetFamilies = ""
|
||||||
@@ -811,13 +804,12 @@ AiPlayerbot.OpenGoSpell = 6477
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Additional randombot strategies
|
# Additional randombot strategies
|
||||||
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
|
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec-based default strategies. These rules are processed after the defaults.
|
||||||
# Example: "+threat,-potions"
|
AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat"
|
||||||
AiPlayerbot.RandomBotCombatStrategies = ""
|
|
||||||
AiPlayerbot.RandomBotNonCombatStrategies = ""
|
AiPlayerbot.RandomBotNonCombatStrategies = ""
|
||||||
|
|
||||||
# Additional altbot strategies
|
# Additional altbot strategies
|
||||||
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
|
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec-based default strategies. These rules are processed after the defaults.
|
||||||
AiPlayerbot.CombatStrategies = ""
|
AiPlayerbot.CombatStrategies = ""
|
||||||
AiPlayerbot.NonCombatStrategies = ""
|
AiPlayerbot.NonCombatStrategies = ""
|
||||||
|
|
||||||
@@ -1014,20 +1006,6 @@ AiPlayerbot.RandomBotMaps = 0,1,530,571
|
|||||||
# Default: 0.25
|
# Default: 0.25
|
||||||
AiPlayerbot.ProbTeleToBankers = 0.25
|
AiPlayerbot.ProbTeleToBankers = 0.25
|
||||||
|
|
||||||
# Control probability weights for bots teleporting to Capital city bankers
|
|
||||||
# Sum of weights need not be 100. Set to 0 to disable teleporting to the city.
|
|
||||||
AiPlayerbot.EnableWeightTeleToCityBankers = 1
|
|
||||||
AiPlayerbot.TeleToStormwindWeight = 2
|
|
||||||
AiPlayerbot.TeleToIronforgeWeight = 1
|
|
||||||
AiPlayerbot.TeleToDarnassusWeight = 1
|
|
||||||
AiPlayerbot.TeleToExodarWeight = 1
|
|
||||||
AiPlayerbot.TeleToOrgrimmarWeight = 2
|
|
||||||
AiPlayerbot.TeleToUndercityWeight = 1
|
|
||||||
AiPlayerbot.TeleToThunderBluffWeight = 1
|
|
||||||
AiPlayerbot.TeleToSilvermoonCityWeight = 1
|
|
||||||
AiPlayerbot.TeleToShattrathCityWeight = 1
|
|
||||||
AiPlayerbot.TeleToDalaranWeight = 1
|
|
||||||
|
|
||||||
# How far randombots are teleported after death
|
# How far randombots are teleported after death
|
||||||
AiPlayerbot.RandomBotTeleportDistance = 100
|
AiPlayerbot.RandomBotTeleportDistance = 100
|
||||||
|
|
||||||
@@ -1132,7 +1110,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"
|
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080"
|
||||||
|
|
||||||
# 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
|
||||||
@@ -1997,15 +1975,13 @@ AiPlayerbot.AllowedLogFiles = ""
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# A list of gameObject GUID's that are not allowed for bots to interact with.
|
# A list of gameObject GUID's that are not allowed for bots to interact with.
|
||||||
#
|
#
|
||||||
AiPlayerbot.DisallowedGameObjects = 176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,2857,179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,165739,165738,175245,175970,176325,176327,123329
|
AiPlayerbot.DisallowedGameObjects = 176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,17155,2857,179490
|
||||||
#
|
#
|
||||||
# List of GUID's:
|
# List of GUID's:
|
||||||
# QuestItems:
|
# QuestItems:
|
||||||
# 176213 = Blood of Heroes, 17155 = Defias Gunpowder, 2656 = Waterlogged Envelope, 123329 = Baelogs Chest
|
# 176213 = Blood of Heroes, 17155 = Defias Gunpowder, 2656 = Waterlogged Envelope
|
||||||
# Chests:
|
# Chests:
|
||||||
# Large Solid Chest = 74448, Box of Assorted Parts = 19020, Food Crate = 3719, Water Barrel = 3658, Barrel of Milk = 3705, Barrel of sweet Nectar = 3706, Tattered Chest = 105579, Large bettered Chest = 75293, Solid Chest = 2857, Battered Foodlocker = 179490, Witch Doctor's Chest = 141596, Relic Coffer = 160836, Dark Coffer = 160845, Fengus's Chest = 179516, Supply Crate = 176224/181085, Malor's Strongbox = 176112
|
# Large Solid Chest = 74448, Box of Assorted Parts = 19020, Food Crate = 3719, Water Barrel = 3658, Barrel of Milk = 3705, Barrel of sweet Nectar = 3706, Tattered Chest = 105579, Large bettered Chest = 75293, Solid Chest = 2857, Battered Foodlocker = 179490
|
||||||
# Other:
|
|
||||||
# Shallow Grave (Zul'Farrak) = 128308/128403, Grim Guzzler Boar (Blackrock Depths) = 165739, Dark Iron Ale Mug (Blackrock Depths) = 165738, Father Flame (Blackrock Spire) = 175245, Unforged Runic Breastplate (Blackrock Spire) = 175970, Blacksmithing Plans (Stratholme) = 176325/176327
|
|
||||||
# Feel free to edit and help to complete.
|
# Feel free to edit and help to complete.
|
||||||
#
|
#
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
DELETE FROM spell_dbc WHERE ID = 30758;
|
|
||||||
INSERT INTO spell_dbc (`ID`,`Category`,`DispelType`,`Mechanic`,`Attributes`,`AttributesEx`,`AttributesEx2`,`AttributesEx3`,`AttributesEx4`,`AttributesEx5`,`AttributesEx6`,`AttributesEx7`,`ShapeshiftMask`,`unk_320_2`,`ShapeshiftExclude`,`unk_320_3`,`Targets`,`TargetCreatureType`,`RequiresSpellFocus`,`FacingCasterFlags`,`CasterAuraState`,`TargetAuraState`,`ExcludeCasterAuraState`,`ExcludeTargetAuraState`,`CasterAuraSpell`,`TargetAuraSpell`,`ExcludeCasterAuraSpell`,`ExcludeTargetAuraSpell`,`CastingTimeIndex`,`RecoveryTime`,`CategoryRecoveryTime`,`InterruptFlags`,`AuraInterruptFlags`,`ChannelInterruptFlags`,`ProcTypeMask`,`ProcChance`,`ProcCharges`,`MaxLevel`,`BaseLevel`,`SpellLevel`,`DurationIndex`,`PowerType`,`ManaCost`,`ManaCostPerLevel`,`ManaPerSecond`,`ManaPerSecondPerLevel`,`RangeIndex`,`Speed`,`ModalNextSpell`,`CumulativeAura`,`Totem_1`,`Totem_2`,`Reagent_1`,`Reagent_2`,`Reagent_3`,`Reagent_4`,`Reagent_5`,`Reagent_6`,`Reagent_7`,`Reagent_8`,`ReagentCount_1`,`ReagentCount_2`,`ReagentCount_3`,`ReagentCount_4`,`ReagentCount_5`,`ReagentCount_6`,`ReagentCount_7`,`ReagentCount_8`,`EquippedItemClass`,`EquippedItemSubclass`,`EquippedItemInvTypes`,`Effect_1`,`Effect_2`,`Effect_3`,`EffectDieSides_1`,`EffectDieSides_2`,`EffectDieSides_3`,`EffectRealPointsPerLevel_1`,`EffectRealPointsPerLevel_2`,`EffectRealPointsPerLevel_3`,`EffectBasePoints_1`,`EffectBasePoints_2`,`EffectBasePoints_3`,`EffectMechanic_1`,`EffectMechanic_2`,`EffectMechanic_3`,`ImplicitTargetA_1`,`ImplicitTargetA_2`,`ImplicitTargetA_3`,`ImplicitTargetB_1`,`ImplicitTargetB_2`,`ImplicitTargetB_3`,`EffectRadiusIndex_1`,`EffectRadiusIndex_2`,`EffectRadiusIndex_3`,`EffectAura_1`,`EffectAura_2`,`EffectAura_3`,`EffectAuraPeriod_1`,`EffectAuraPeriod_2`,`EffectAuraPeriod_3`,`EffectMultipleValue_1`,`EffectMultipleValue_2`,`EffectMultipleValue_3`,`EffectChainTargets_1`,`EffectChainTargets_2`,`EffectChainTargets_3`,`EffectItemType_1`,`EffectItemType_2`,`EffectItemType_3`,`EffectMiscValue_1`,`EffectMiscValue_2`,`EffectMiscValue_3`,`EffectMiscValueB_1`,`EffectMiscValueB_2`,`EffectMiscValueB_3`,`EffectTriggerSpell_1`,`EffectTriggerSpell_2`,`EffectTriggerSpell_3`,`EffectPointsPerCombo_1`,`EffectPointsPerCombo_2`,`EffectPointsPerCombo_3`,`EffectSpellClassMaskA_1`,`EffectSpellClassMaskA_2`,`EffectSpellClassMaskA_3`,`EffectSpellClassMaskB_1`,`EffectSpellClassMaskB_2`,`EffectSpellClassMaskB_3`,`EffectSpellClassMaskC_1`,`EffectSpellClassMaskC_2`,`EffectSpellClassMaskC_3`,`SpellVisualID_1`,`SpellVisualID_2`,`SpellIconID`,`ActiveIconID`,`SpellPriority`,`Name_Lang_enUS`,`Name_Lang_enGB`,`Name_Lang_koKR`,`Name_Lang_frFR`,`Name_Lang_deDE`,`Name_Lang_enCN`,`Name_Lang_zhCN`,`Name_Lang_enTW`,`Name_Lang_zhTW`,`Name_Lang_esES`,`Name_Lang_esMX`,`Name_Lang_ruRU`,`Name_Lang_ptPT`,`Name_Lang_ptBR`,`Name_Lang_itIT`,`Name_Lang_Unk`,`Name_Lang_Mask`,`NameSubtext_Lang_enUS`,`NameSubtext_Lang_enGB`,`NameSubtext_Lang_koKR`,`NameSubtext_Lang_frFR`,`NameSubtext_Lang_deDE`,`NameSubtext_Lang_enCN`,`NameSubtext_Lang_zhCN`,`NameSubtext_Lang_enTW`,`NameSubtext_Lang_zhTW`,`NameSubtext_Lang_esES`,`NameSubtext_Lang_esMX`,`NameSubtext_Lang_ruRU`,`NameSubtext_Lang_ptPT`,`NameSubtext_Lang_ptBR`,`NameSubtext_Lang_itIT`,`NameSubtext_Lang_Unk`,`NameSubtext_Lang_Mask`,`Description_Lang_enUS`,`Description_Lang_enGB`,`Description_Lang_koKR`,`Description_Lang_frFR`,`Description_Lang_deDE`,`Description_Lang_enCN`,`Description_Lang_zhCN`,`Description_Lang_enTW`,`Description_Lang_zhTW`,`Description_Lang_esES`,`Description_Lang_esMX`,`Description_Lang_ruRU`,`Description_Lang_ptPT`,`Description_Lang_ptBR`,`Description_Lang_itIT`,`Description_Lang_Unk`,`Description_Lang_Mask`,`AuraDescription_Lang_enUS`,`AuraDescription_Lang_enGB`,`AuraDescription_Lang_koKR`,`AuraDescription_Lang_frFR`,`AuraDescription_Lang_deDE`,`AuraDescription_Lang_enCN`,`AuraDescription_Lang_zhCN`,`AuraDescription_Lang_enTW`,`AuraDescription_Lang_zhTW`,`AuraDescription_Lang_esES`,`AuraDescription_Lang_esMX`,`AuraDescription_Lang_ruRU`,`AuraDescription_Lang_ptPT`,`AuraDescription_Lang_ptBR`,`AuraDescription_Lang_itIT`,`AuraDescription_Lang_Unk`,`AuraDescription_Lang_Mask`,`ManaCostPct`,`StartRecoveryCategory`,`StartRecoveryTime`,`MaxTargetLevel`,`SpellClassSet`,`SpellClassMask_1`,`SpellClassMask_2`,`SpellClassMask_3`,`MaxTargets`,`DefenseType`,`PreventionType`,`StanceBarOrder`,`EffectChainAmplitude_1`,`EffectChainAmplitude_2`,`EffectChainAmplitude_3`,`MinFactionID`,`MinReputation`,`RequiredAuraVision`,`RequiredTotemCategoryID_1`,`RequiredTotemCategoryID_2`,`RequiredAreasID`,`SchoolMask`,`RuneCostID`,`SpellMissileID`,`PowerDisplayID`,`EffectBonusMultiplier_1`,`EffectBonusMultiplier_2`,`EffectBonusMultiplier_3`,`SpellDescriptionVariableID`,`SpellDifficultyID`)
|
|
||||||
VALUES (30758,0,0,0,696254720,132128,268976133,269680640,8388736,393224,4100,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,52,0,0,0,0,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,'aedm','','','','','','','','','','','','','','','',16712190,'','','','','','','','','','','','','','','','',16712172,'','','','','','','','','','','','','','','','',16712188,'','','','','','','','','','','','','','','','',16712188,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0);
|
|
||||||
@@ -472,10 +472,6 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sRandomPlayerbotMgr->IsRandomBot(player))
|
|
||||||
{
|
|
||||||
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
|
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ public:
|
|||||||
if (melee && botAI->IsRanged(bot))
|
if (melee && botAI->IsRanged(bot))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
bool rangeddps = message.find("@rangeddps") == 0;
|
|
||||||
if (rangeddps && (!botAI->IsRanged(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
bool meleedps = message.find("@meleedps") == 0;
|
|
||||||
if (meleedps && (!botAI->IsMelee(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
if (tank || dps || heal || ranged || melee)
|
if (tank || dps || heal || ranged || melee)
|
||||||
return ChatFilter::Filter(message);
|
return ChatFilter::Filter(message);
|
||||||
|
|
||||||
@@ -254,41 +246,21 @@ public:
|
|||||||
|
|
||||||
if (message.find("@group") == 0)
|
if (message.find("@group") == 0)
|
||||||
{
|
{
|
||||||
size_t spacePos = message.find(" ");
|
std::string const pnum = message.substr(6, message.find(" "));
|
||||||
if (spacePos == std::string::npos)
|
uint32 from = atoi(pnum.c_str());
|
||||||
return message;
|
uint32 to = from;
|
||||||
|
if (pnum.find("-") != std::string::npos)
|
||||||
std::string pnum = message.substr(6, spacePos - 6);
|
|
||||||
std::string actualMessage = message.substr(spacePos + 1);
|
|
||||||
|
|
||||||
std::set<uint32> targets;
|
|
||||||
std::istringstream ss(pnum);
|
|
||||||
std::string token;
|
|
||||||
|
|
||||||
while (std::getline(ss, token, ','))
|
|
||||||
{
|
{
|
||||||
size_t dashPos = token.find("-");
|
from = atoi(pnum.substr(pnum.find("@") + 1, pnum.find("-")).c_str());
|
||||||
if (dashPos != std::string::npos)
|
to = atoi(pnum.substr(pnum.find("-") + 1, pnum.find(" ")).c_str());
|
||||||
{
|
|
||||||
uint32 from = atoi(token.substr(0, dashPos).c_str());
|
|
||||||
uint32 to = atoi(token.substr(dashPos + 1).c_str());
|
|
||||||
if (from > to) std::swap(from, to);
|
|
||||||
for (uint32 i = from; i <= to; ++i)
|
|
||||||
targets.insert(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint32 index = atoi(token.c_str());
|
|
||||||
targets.insert(index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bot->GetGroup())
|
if (!bot->GetGroup())
|
||||||
return message;
|
return message;
|
||||||
|
|
||||||
uint32 sg = bot->GetSubGroup() + 1;
|
uint32 sg = bot->GetSubGroup() + 1;
|
||||||
if (targets.find(sg) != targets.end())
|
if (sg >= from && sg <= to)
|
||||||
return ChatFilter::Filter(actualMessage);
|
return ChatFilter::Filter(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
|||||||
@@ -299,10 +299,9 @@ bool LootObject::IsLootPossible(Player* bot)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event) or on
|
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
|
||||||
// respawn time
|
|
||||||
GameObject* go = botAI->GetGameObject(guid);
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
if (go && (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE) || !go->isSpawned()))
|
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (skillId == SKILL_NONE)
|
if (skillId == SKILL_NONE)
|
||||||
@@ -408,4 +407,4 @@ LootObject LootObjectStack::GetNearest(float maxDistance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nearest;
|
return nearest;
|
||||||
}
|
}
|
||||||
@@ -722,8 +722,7 @@ void PlayerbotAI::HandleTeleportAck()
|
|||||||
// SetNextCheckDelay(urand(2000, 5000));
|
// SetNextCheckDelay(urand(2000, 5000));
|
||||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||||
if (sPlayerbotAIConfig->restrictHealerDPS)
|
EvaluateHealerDpsStrategy();
|
||||||
EvaluateHealerDpsStrategy();
|
|
||||||
Reset(true);
|
Reset(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1766,7 +1765,7 @@ bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(pla
|
|||||||
|
|
||||||
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -1778,11 +1777,6 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
|||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsHeal(member)) // Check if the member is a healer
|
if (IsHeal(member)) // Check if the member is a healer
|
||||||
{
|
{
|
||||||
bool isAssistant = group->IsAssistant(member->GetGUID());
|
bool isAssistant = group->IsAssistant(member->GetGUID());
|
||||||
@@ -1802,7 +1796,7 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
|
|||||||
|
|
||||||
bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -1814,11 +1808,6 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
|||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsRangedDps(member)) // Check if the member is a ranged DPS
|
if (IsRangedDps(member)) // Check if the member is a ranged DPS
|
||||||
{
|
{
|
||||||
bool isAssistant = group->IsAssistant(member->GetGUID());
|
bool isAssistant = group->IsAssistant(member->GetGUID());
|
||||||
@@ -1851,35 +1840,6 @@ bool PlayerbotAI::HasAggro(Unit* unit)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 PlayerbotAI::GetAssistTankIndex(Player* player)
|
|
||||||
{
|
|
||||||
Group* group = player->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int counter = 0;
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
|
||||||
{
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
if (IsTank(member, true) && group->IsAssistant(member->GetGUID()))
|
|
||||||
{
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
|
int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
@@ -1891,12 +1851,6 @@ int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
if (player == member)
|
||||||
{
|
{
|
||||||
return counter;
|
return counter;
|
||||||
@@ -1921,12 +1875,6 @@ int32 PlayerbotAI::GetRangedIndex(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
if (player == member)
|
||||||
{
|
{
|
||||||
return counter;
|
return counter;
|
||||||
@@ -1954,12 +1902,6 @@ int32 PlayerbotAI::GetClassIndex(Player* player, uint8 cls)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
if (player == member)
|
||||||
{
|
{
|
||||||
return counter;
|
return counter;
|
||||||
@@ -1986,12 +1928,6 @@ int32 PlayerbotAI::GetRangedDpsIndex(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
if (player == member)
|
||||||
{
|
{
|
||||||
return counter;
|
return counter;
|
||||||
@@ -2019,12 +1955,6 @@ int32 PlayerbotAI::GetMeleeIndex(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player == member)
|
if (player == member)
|
||||||
{
|
{
|
||||||
return counter;
|
return counter;
|
||||||
@@ -2196,12 +2126,6 @@ bool PlayerbotAI::IsMainTank(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTank(member) && member->IsAlive())
|
if (IsTank(member) && member->IsAlive())
|
||||||
{
|
{
|
||||||
return player->GetGUID() == member->GetGUID();
|
return player->GetGUID() == member->GetGUID();
|
||||||
@@ -2210,62 +2134,6 @@ bool PlayerbotAI::IsMainTank(Player* player)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsBotMainTank(Player* player)
|
|
||||||
{
|
|
||||||
if (!player->GetSession()->IsBot() || !IsTank(player))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsMainTank(player))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = player->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return true; // If no group, consider the bot as main tank
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 botAssistTankIndex = GetAssistTankIndex(player);
|
|
||||||
|
|
||||||
if (botAssistTankIndex == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* member = gref->GetSource();
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 memberAssistTankIndex = GetAssistTankIndex(member);
|
|
||||||
|
|
||||||
if (memberAssistTankIndex == -1)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memberAssistTankIndex == botAssistTankIndex && player == member)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
@@ -2277,12 +2145,6 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTank(member) && member->IsAlive())
|
if (IsTank(member) && member->IsAlive())
|
||||||
{
|
{
|
||||||
result++;
|
result++;
|
||||||
@@ -2295,7 +2157,7 @@ bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMai
|
|||||||
|
|
||||||
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -2304,12 +2166,6 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (index == counter)
|
||||||
@@ -2323,12 +2179,6 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
|||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (index == counter)
|
||||||
@@ -2536,11 +2386,6 @@ std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
|||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
|
|
||||||
if (!member)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GET_PLAYERBOT_AI(member) && !GET_PLAYERBOT_AI(member)->IsRealPlayer())
|
if (GET_PLAYERBOT_AI(member) && !GET_PLAYERBOT_AI(member)->IsRealPlayer())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -4125,15 +3970,37 @@ bool IsAlliance(uint8 race)
|
|||||||
|
|
||||||
bool PlayerbotAI::HasRealPlayerMaster()
|
bool PlayerbotAI::HasRealPlayerMaster()
|
||||||
{
|
{
|
||||||
if (master)
|
// if (master)
|
||||||
|
// {
|
||||||
|
// PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
|
||||||
|
// return !masterBotAI || masterBotAI->IsRealPlayer();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
// Removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
/* 1) The "master" pointer can be null if the bot was created
|
||||||
|
without a master player or if the master was just removed. */
|
||||||
|
if (!master)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* 2) Is the master player still present in the world?
|
||||||
|
If FindPlayer fails, we invalidate "master" and stop here. */
|
||||||
|
if (!ObjectAccessor::FindPlayer(master->GetGUID()))
|
||||||
{
|
{
|
||||||
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
|
master = nullptr; // avoids repeating the check on the next tick
|
||||||
return !masterBotAI || masterBotAI->IsRealPlayer();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
/* 3) If the master is a bot, we check that it is itself controlled
|
||||||
|
by a real player. Otherwise, it's already a real player → true. */
|
||||||
|
if (PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master))
|
||||||
|
return masterBotAI->IsRealPlayer(); // bot controlled by a player?
|
||||||
|
|
||||||
|
return true; // master = real player
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); }
|
bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); }
|
||||||
|
|
||||||
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
|
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
|
||||||
|
|||||||
@@ -423,15 +423,13 @@ public:
|
|||||||
static bool IsCaster(Player* player, bool bySpec = false);
|
static bool IsCaster(Player* player, bool bySpec = false);
|
||||||
static bool IsRangedDps(Player* player, bool bySpec = false);
|
static bool IsRangedDps(Player* player, bool bySpec = false);
|
||||||
static bool IsCombo(Player* player);
|
static bool IsCombo(Player* player);
|
||||||
static bool IsBotMainTank(Player* player);
|
|
||||||
static bool IsMainTank(Player* player);
|
static bool IsMainTank(Player* player);
|
||||||
static uint32 GetGroupTankNum(Player* player);
|
static uint32 GetGroupTankNum(Player* player);
|
||||||
static bool IsAssistTank(Player* player);
|
bool IsAssistTank(Player* player);
|
||||||
static bool IsAssistTankOfIndex(Player* player, int index);
|
bool IsAssistTankOfIndex(Player* player, int index);
|
||||||
static bool IsHealAssistantOfIndex(Player* player, int index);
|
bool IsHealAssistantOfIndex(Player* player, int index);
|
||||||
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
||||||
bool HasAggro(Unit* unit);
|
bool HasAggro(Unit* unit);
|
||||||
static int32 GetAssistTankIndex(Player* player);
|
|
||||||
int32 GetGroupSlotIndex(Player* player);
|
int32 GetGroupSlotIndex(Player* player);
|
||||||
int32 GetRangedIndex(Player* player);
|
int32 GetRangedIndex(Player* player);
|
||||||
int32 GetClassIndex(Player* player, uint8 cls);
|
int32 GetClassIndex(Player* player, uint8 cls);
|
||||||
|
|||||||
@@ -139,17 +139,6 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
randomBotMapsAsString = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotMaps", "0,1,530,571");
|
randomBotMapsAsString = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotMaps", "0,1,530,571");
|
||||||
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
|
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
|
||||||
probTeleToBankers = sConfigMgr->GetOption<float>("AiPlayerbot.ProbTeleToBankers", 0.25f);
|
probTeleToBankers = sConfigMgr->GetOption<float>("AiPlayerbot.ProbTeleToBankers", 0.25f);
|
||||||
enableWeightTeleToCityBankers = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableWeightTeleToCityBankers", false);
|
|
||||||
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 1);
|
|
||||||
weightTeleToIronforge = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToIronforgeWeight", 1);
|
|
||||||
weightTeleToDarnassus = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDarnassusWeight", 1);
|
|
||||||
weightTeleToExodar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToExodarWeight", 1);
|
|
||||||
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 1);
|
|
||||||
weightTeleToUndercity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToUndercityWeight", 1);
|
|
||||||
weightTeleToThunderBluff = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToThunderBluffWeight", 1);
|
|
||||||
weightTeleToSilvermoonCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToSilvermoonCityWeight", 1);
|
|
||||||
weightTeleToShattrathCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToShattrathCityWeight", 1);
|
|
||||||
weightTeleToDalaran = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDalaranWeight", 1);
|
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestItems",
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestItems",
|
||||||
"6948,5175,5176,5177,5178,16309,12382,13704,11000"),
|
"6948,5175,5176,5177,5178,16309,12382,13704,11000"),
|
||||||
@@ -161,21 +150,15 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
"2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,"
|
"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"),
|
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"),
|
||||||
pvpProhibitedZoneIds);
|
pvpProhibitedZoneIds);
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds", "976,35"),
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
pvpProhibitedAreaIds);
|
||||||
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"),
|
|
||||||
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", "7848,3802,5505,6502,7761"),
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
|
||||||
randomBotQuestIds);
|
randomBotQuestIds);
|
||||||
|
|
||||||
LoadSet<std::set<uint32>>(
|
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects",
|
disallowedGameObjects);
|
||||||
"176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,2857,"
|
|
||||||
"179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,"
|
|
||||||
"165739,165738,175245,175970,176325,176327,123329"),
|
|
||||||
disallowedGameObjects);
|
|
||||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||||
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
||||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
|
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
|
||||||
@@ -215,7 +198,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
"575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,"
|
"575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,"
|
||||||
"531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"),
|
"531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"),
|
||||||
restrictedHealerDPSMaps);
|
restrictedHealerDPSMaps);
|
||||||
|
|
||||||
//////////////////////////// ICC
|
//////////////////////////// ICC
|
||||||
|
|
||||||
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
||||||
@@ -386,7 +369,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
|
|
||||||
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
|
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
|
||||||
|
|
||||||
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "");
|
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "-threat");
|
||||||
randomBotNonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotNonCombatStrategies", "");
|
randomBotNonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotNonCombatStrategies", "");
|
||||||
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "+custom::say");
|
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "+custom::say");
|
||||||
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
|
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
|
||||||
@@ -473,13 +456,11 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
botCheats.clear();
|
botCheats.clear();
|
||||||
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "food,taxi,raid"),
|
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi,raid"),
|
||||||
botCheats);
|
botCheats);
|
||||||
|
|
||||||
botCheatMask = 0;
|
botCheatMask = 0;
|
||||||
|
|
||||||
if (std::find(botCheats.begin(), botCheats.end(), "food") != botCheats.end())
|
|
||||||
botCheatMask |= (uint32)BotCheatMask::food;
|
|
||||||
if (std::find(botCheats.begin(), botCheats.end(), "taxi") != botCheats.end())
|
if (std::find(botCheats.begin(), botCheats.end(), "taxi") != botCheats.end())
|
||||||
botCheatMask |= (uint32)BotCheatMask::taxi;
|
botCheatMask |= (uint32)BotCheatMask::taxi;
|
||||||
if (std::find(botCheats.begin(), botCheats.end(), "gold") != botCheats.end())
|
if (std::find(botCheats.begin(), botCheats.end(), "gold") != botCheats.end())
|
||||||
@@ -594,8 +575,6 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
|
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
|
||||||
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
|
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
|
||||||
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
|
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
|
||||||
defaultPetStance = sConfigMgr->GetOption<int32>("AiPlayerbot.DefaultPetStance", 1);
|
|
||||||
petChatCommandDebug = sConfigMgr->GetOption<bool>("AiPlayerbot.PetChatCommandDebug", 0);
|
|
||||||
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
|
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
|
||||||
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
|
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
|
||||||
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
|
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
|
||||||
@@ -611,6 +590,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
|
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
|
||||||
|
|
||||||
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
|
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
|
||||||
|
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);
|
||||||
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
|
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
|
||||||
|
|
||||||
// arena
|
// arena
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ enum class BotCheatMask : uint32
|
|||||||
mana = 8,
|
mana = 8,
|
||||||
power = 16,
|
power = 16,
|
||||||
raid = 32,
|
raid = 32,
|
||||||
food = 64,
|
maxMask = 64
|
||||||
maxMask = 128
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HealingManaEfficiency : uint8
|
enum class HealingManaEfficiency : uint8
|
||||||
@@ -102,17 +101,6 @@ public:
|
|||||||
bool botAutologin;
|
bool botAutologin;
|
||||||
std::string randomBotMapsAsString;
|
std::string randomBotMapsAsString;
|
||||||
float probTeleToBankers;
|
float probTeleToBankers;
|
||||||
bool enableWeightTeleToCityBankers;
|
|
||||||
int weightTeleToStormwind;
|
|
||||||
int weightTeleToIronforge;
|
|
||||||
int weightTeleToDarnassus;
|
|
||||||
int weightTeleToExodar;
|
|
||||||
int weightTeleToOrgrimmar;
|
|
||||||
int weightTeleToUndercity;
|
|
||||||
int weightTeleToThunderBluff;
|
|
||||||
int weightTeleToSilvermoonCity;
|
|
||||||
int weightTeleToShattrathCity;
|
|
||||||
int weightTeleToDalaran;
|
|
||||||
std::vector<uint32> randomBotMaps;
|
std::vector<uint32> randomBotMaps;
|
||||||
std::vector<uint32> randomBotQuestItems;
|
std::vector<uint32> randomBotQuestItems;
|
||||||
std::vector<uint32> randomBotAccounts;
|
std::vector<uint32> randomBotAccounts;
|
||||||
@@ -345,13 +333,12 @@ public:
|
|||||||
bool autoPickTalents;
|
bool autoPickTalents;
|
||||||
bool autoUpgradeEquip;
|
bool autoUpgradeEquip;
|
||||||
int32 hunterWolfPet;
|
int32 hunterWolfPet;
|
||||||
int32 defaultPetStance;
|
|
||||||
int32 petChatCommandDebug;
|
|
||||||
bool autoLearnTrainerSpells;
|
bool autoLearnTrainerSpells;
|
||||||
bool autoDoQuests;
|
bool autoDoQuests;
|
||||||
bool enableNewRpgStrategy;
|
bool enableNewRpgStrategy;
|
||||||
std::unordered_map<NewRpgStatus, uint32> RpgStatusProbWeight;
|
std::unordered_map<NewRpgStatus, uint32> RpgStatusProbWeight;
|
||||||
bool syncLevelWithPlayers;
|
bool syncLevelWithPlayers;
|
||||||
|
bool freeFood;
|
||||||
bool autoLearnQuestSpells;
|
bool autoLearnQuestSpells;
|
||||||
bool autoTeleportForLevel;
|
bool autoTeleportForLevel;
|
||||||
bool randomBotGroupNearby;
|
bool randomBotGroupNearby;
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
#include "WorldSessionMgr.h"
|
#include "WorldSessionMgr.h"
|
||||||
#include "DatabaseEnv.h" // Added for gender choice
|
#include "DatabaseEnv.h" // Added for gender choice
|
||||||
#include <algorithm> // Added for gender choice
|
#include <algorithm> // Added for gender choice
|
||||||
|
#include "Log.h" // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
|
||||||
class BotInitGuard
|
class BotInitGuard
|
||||||
{
|
{
|
||||||
@@ -1726,23 +1728,72 @@ void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI)
|
|||||||
|
|
||||||
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
|
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
|
||||||
{
|
{
|
||||||
if (!(sPlayerbotAIConfig->enabled) || !player)
|
// if (!(sPlayerbotAIConfig->enabled) || !player)
|
||||||
{
|
// {
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
|
|
||||||
// return nullptr;
|
// return nullptr;
|
||||||
// }
|
// }
|
||||||
auto itr = _playerbotsAIMap.find(player->GetGUID());
|
// // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
|
||||||
if (itr != _playerbotsAIMap.end())
|
// // return nullptr;
|
||||||
{
|
// // }
|
||||||
if (itr->second->IsBotAI())
|
// auto itr = _playerbotsAIMap.find(player->GetGUID());
|
||||||
return reinterpret_cast<PlayerbotAI*>(itr->second);
|
// if (itr != _playerbotsAIMap.end())
|
||||||
|
// {
|
||||||
|
// if (itr->second->IsBotAI())
|
||||||
|
// return reinterpret_cast<PlayerbotAI*>(itr->second);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nullptr;
|
||||||
|
|
||||||
|
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
if (!player || !sPlayerbotAIConfig->enabled)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// First read the GUID into a local variable, but ONLY after the check!
|
||||||
|
ObjectGuid guid = player->GetGUID(); // <-- OK here, we know that player != nullptr
|
||||||
|
{
|
||||||
|
std::shared_lock rlock(_aiMutex);
|
||||||
|
auto it = _playerbotsAIMap.find(guid);
|
||||||
|
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
|
||||||
|
return static_cast<PlayerbotAI*>(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transient state: NEVER break the master ⇄ bots relationship here.
|
||||||
|
if (!ObjectAccessor::FindPlayer(guid))
|
||||||
|
{
|
||||||
|
RemovePlayerbotAI(guid, /*removeMgrEntry=*/false);
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAIByGuid(ObjectGuid guid)
|
||||||
|
{
|
||||||
|
if (!sPlayerbotAIConfig->enabled)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::shared_lock rlock(_aiMutex);
|
||||||
|
auto it = _playerbotsAIMap.find(guid);
|
||||||
|
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
|
||||||
|
return static_cast<PlayerbotAI*>(it->second);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry /*= true*/)
|
||||||
|
{
|
||||||
|
std::unique_lock wlock(_aiMutex);
|
||||||
|
|
||||||
|
if (auto it = _playerbotsAIMap.find(guid); it != _playerbotsAIMap.end())
|
||||||
|
{
|
||||||
|
delete it->second;
|
||||||
|
_playerbotsAIMap.erase(it);
|
||||||
|
LOG_DEBUG("playerbots", "Removed stale AI for GUID {}",
|
||||||
|
static_cast<uint64>(guid.GetRawValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeMgrEntry)
|
||||||
|
_playerbotsMgrMap.erase(guid); // we NO longer touch the relation in a "soft" purge
|
||||||
|
}
|
||||||
|
|
||||||
PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
|
||||||
{
|
{
|
||||||
if (!(sPlayerbotAIConfig->enabled) || !player)
|
if (!(sPlayerbotAIConfig->enabled) || !player)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "PlayerbotAIBase.h"
|
#include "PlayerbotAIBase.h"
|
||||||
#include "QueryHolder.h"
|
#include "QueryHolder.h"
|
||||||
#include "QueryResult.h"
|
#include "QueryResult.h"
|
||||||
|
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
|
||||||
class ChatHandler;
|
class ChatHandler;
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@@ -114,13 +115,38 @@ public:
|
|||||||
void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI);
|
void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI);
|
||||||
|
|
||||||
PlayerbotAI* GetPlayerbotAI(Player* player);
|
PlayerbotAI* GetPlayerbotAI(Player* player);
|
||||||
|
PlayerbotAI* GetPlayerbotAIByGuid(ObjectGuid guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
// void RemovePlayerbotAI(ObjectGuid const& guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
// removeMgrEntry = true => "hard" purge (AI + manager relation), for real logouts
|
||||||
|
// removeMgrEntry = false => "soft" purge (AI only), for detected "stale" cases
|
||||||
|
void RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry = true);
|
||||||
PlayerbotMgr* GetPlayerbotMgr(Player* player);
|
PlayerbotMgr* GetPlayerbotMgr(Player* player);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsAIMap;
|
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsAIMap;
|
||||||
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMgrMap;
|
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMgrMap;
|
||||||
|
mutable std::shared_mutex _aiMutex; // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sPlayerbotsMgr PlayerbotsMgr::instance()
|
#define sPlayerbotsMgr PlayerbotsMgr::instance()
|
||||||
|
|
||||||
|
// Temporary addition If it keeps crashing, we will use them.
|
||||||
|
// Like
|
||||||
|
// BEFORE : PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
|
// AFTER (safe) : PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(bot);
|
||||||
|
// BEFORE : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) { ... }
|
||||||
|
// AFTER (safe) : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(player)) { ... }
|
||||||
|
// --- SAFE helpers (append to PlayerbotMgr.h) ---
|
||||||
|
inline PlayerbotAI* GET_PLAYERBOT_AI_SAFE(Player* p)
|
||||||
|
{
|
||||||
|
// Avoid any dereference during transient states (nullptr, teleport, flight, etc.)
|
||||||
|
return p ? sPlayerbotsMgr->GetPlayerbotAI(p) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PlayerbotMgr* GET_PLAYERBOT_MGR_SAFE(Player* p)
|
||||||
|
{
|
||||||
|
return p ? sPlayerbotsMgr->GetPlayerbotMgr(p) : nullptr;
|
||||||
|
}
|
||||||
|
// --- end SAFE helpers ---
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -377,6 +377,10 @@ public:
|
|||||||
|
|
||||||
void OnPlayerbotLogout(Player* player) override
|
void OnPlayerbotLogout(Player* player) override
|
||||||
{
|
{
|
||||||
|
// immediate purge of the bot's AI upon disconnection
|
||||||
|
if (player && player->GetSession()->IsBot())
|
||||||
|
sPlayerbotsMgr->RemovePlayerbotAI(player->GetGUID()); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
|
|
||||||
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||||
|
|||||||
@@ -60,48 +60,6 @@ struct GuidClassRaceInfo
|
|||||||
uint32 rRace;
|
uint32 rRace;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CityId : uint8 {
|
|
||||||
STORMWIND, IRONFORGE, DARNASSUS, EXODAR,
|
|
||||||
ORGRIMMAR, UNDERCITY, THUNDER_BLUFF, SILVERMOON_CITY,
|
|
||||||
SHATTRATH_CITY, DALARAN
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FactionId : uint8 { ALLIANCE, HORDE, NEUTRAL };
|
|
||||||
|
|
||||||
// Map of banker entry → city + faction
|
|
||||||
static const std::unordered_map<uint16, std::pair<CityId, FactionId>> bankerToCity = {
|
|
||||||
{2455, {CityId::STORMWIND, FactionId::ALLIANCE}}, {2456, {CityId::STORMWIND, FactionId::ALLIANCE}}, {2457, {CityId::STORMWIND, FactionId::ALLIANCE}},
|
|
||||||
{2460, {CityId::IRONFORGE, FactionId::ALLIANCE}}, {2461, {CityId::IRONFORGE, FactionId::ALLIANCE}}, {5099, {CityId::IRONFORGE, FactionId::ALLIANCE}},
|
|
||||||
{4155, {CityId::DARNASSUS, FactionId::ALLIANCE}}, {4208, {CityId::DARNASSUS, FactionId::ALLIANCE}}, {4209, {CityId::DARNASSUS, FactionId::ALLIANCE}},
|
|
||||||
{17773, {CityId::EXODAR, FactionId::ALLIANCE}}, {18350, {CityId::EXODAR, FactionId::ALLIANCE}}, {16710, {CityId::EXODAR, FactionId::ALLIANCE}},
|
|
||||||
{3320, {CityId::ORGRIMMAR, FactionId::HORDE}}, {3309, {CityId::ORGRIMMAR, FactionId::HORDE}}, {3318, {CityId::ORGRIMMAR, FactionId::HORDE}},
|
|
||||||
{4549, {CityId::UNDERCITY, FactionId::HORDE}}, {2459, {CityId::UNDERCITY, FactionId::HORDE}}, {2458, {CityId::UNDERCITY, FactionId::HORDE}}, {4550, {CityId::UNDERCITY, FactionId::HORDE}},
|
|
||||||
{2996, {CityId::THUNDER_BLUFF, FactionId::HORDE}}, {8356, {CityId::THUNDER_BLUFF, FactionId::HORDE}}, {8357, {CityId::THUNDER_BLUFF, FactionId::HORDE}},
|
|
||||||
{17631, {CityId::SILVERMOON_CITY, FactionId::HORDE}}, {17632, {CityId::SILVERMOON_CITY, FactionId::HORDE}}, {17633, {CityId::SILVERMOON_CITY, FactionId::HORDE}},
|
|
||||||
{16615, {CityId::SILVERMOON_CITY, FactionId::HORDE}}, {16616, {CityId::SILVERMOON_CITY, FactionId::HORDE}}, {16617, {CityId::SILVERMOON_CITY, FactionId::HORDE}},
|
|
||||||
{19246, {CityId::SHATTRATH_CITY, FactionId::NEUTRAL}}, {19338, {CityId::SHATTRATH_CITY, FactionId::NEUTRAL}},
|
|
||||||
{19034, {CityId::SHATTRATH_CITY, FactionId::NEUTRAL}}, {19318, {CityId::SHATTRATH_CITY, FactionId::NEUTRAL}},
|
|
||||||
{30604, {CityId::DALARAN, FactionId::NEUTRAL}}, {30605, {CityId::DALARAN, FactionId::NEUTRAL}}, {30607, {CityId::DALARAN, FactionId::NEUTRAL}},
|
|
||||||
{28675, {CityId::DALARAN, FactionId::NEUTRAL}}, {28676, {CityId::DALARAN, FactionId::NEUTRAL}}, {28677, {CityId::DALARAN, FactionId::NEUTRAL}}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Map of city → available banker entries
|
|
||||||
static const std::unordered_map<CityId, std::vector<uint16>> cityToBankers = {
|
|
||||||
{CityId::STORMWIND, {2455, 2456, 2457}},
|
|
||||||
{CityId::IRONFORGE, {2460, 2461, 5099}},
|
|
||||||
{CityId::DARNASSUS, {4155, 4208, 4209}},
|
|
||||||
{CityId::EXODAR, {17773, 18350, 16710}},
|
|
||||||
{CityId::ORGRIMMAR, {3320, 3309, 3318}},
|
|
||||||
{CityId::UNDERCITY, {4549, 2459, 2458, 4550}},
|
|
||||||
{CityId::THUNDER_BLUFF, {2996, 8356, 8357}},
|
|
||||||
{CityId::SILVERMOON_CITY, {17631, 17632, 17633, 16615, 16616, 16617}},
|
|
||||||
{CityId::SHATTRATH_CITY, {19246, 19338, 19034, 19318}},
|
|
||||||
{CityId::DALARAN, {30604, 30605, 30607, 28675, 28676, 28677, 29530}}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Quick lookup map: banker entry → location
|
|
||||||
static std::unordered_map<uint32, WorldLocation> bankerEntryToLocation;
|
|
||||||
|
|
||||||
void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); }
|
void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); }
|
||||||
|
|
||||||
void activatePrintStatsThread()
|
void activatePrintStatsThread()
|
||||||
@@ -1702,6 +1660,11 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tlocs.empty())
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "Cannot teleport bot {} - no locations available", bot->GetName().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
|
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
|
||||||
|
|
||||||
@@ -2058,8 +2021,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
"position_y, "
|
"position_y, "
|
||||||
"position_z, "
|
"position_z, "
|
||||||
"orientation, "
|
"orientation, "
|
||||||
"t.minlevel, "
|
"t.minlevel "
|
||||||
"t.entry "
|
|
||||||
"FROM "
|
"FROM "
|
||||||
"creature c "
|
"creature c "
|
||||||
"INNER JOIN creature_template t on c.id1 = t.entry "
|
"INNER JOIN creature_template t on c.id1 = t.entry "
|
||||||
@@ -2086,30 +2048,27 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
float z = fields[3].Get<float>();
|
float z = fields[3].Get<float>();
|
||||||
float orient = fields[4].Get<float>();
|
float orient = fields[4].Get<float>();
|
||||||
uint32 level = fields[5].Get<uint32>();
|
uint32 level = fields[5].Get<uint32>();
|
||||||
uint32 entry = fields[6].Get<uint32>();
|
WorldLocation loc(mapId, x + cos(orient) * 6.0f, y + sin(orient) * 6.0f, z + 2.0f, orient + M_PI);
|
||||||
BankerLocation bLoc;
|
|
||||||
bLoc.loc = WorldLocation(mapId, x + cos(orient) * 6.0f, y + sin(orient) * 6.0f, z + 2.0f, orient + M_PI);
|
|
||||||
bLoc.entry = entry;
|
|
||||||
collected_locs++;
|
collected_locs++;
|
||||||
for (int32 l = 1; l <= maxLevel; l++)
|
for (int32 l = 1; l <= maxLevel; l++)
|
||||||
{
|
{
|
||||||
// Bots 1-60 go to base game bankers (all have minlevel 30 or 45)
|
if (l <= 60 && level >= 60)
|
||||||
if (l <=60 && level > 45)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Bots 61-70 go to Shattrath bankers (all have minlevel 60 or 70)
|
if (l <= 70 && level >= 70)
|
||||||
if ((l >=61 && l <=70) && (level < 60 || level > 70))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Bots 71+ go to Dalaran bankers (all have minlevel 75)
|
if (l >= 70 && level >= 60 && level <= 70)
|
||||||
if ((l >=71) && level != 75)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bankerLocsPerLevelCache[(uint8)l].push_back(bLoc);
|
if (l >= 30 && level <= 30)
|
||||||
bankerEntryToLocation[bLoc.entry] = bLoc.loc;
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bankerLocsPerLevelCache[(uint8)l].push_back(loc);
|
||||||
}
|
}
|
||||||
} while (results->NextRow());
|
} while (results->NextRow());
|
||||||
}
|
}
|
||||||
@@ -2179,92 +2138,11 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot)
|
|||||||
locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level];
|
locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level];
|
||||||
else
|
else
|
||||||
locs = &locsPerLevelCache[level];
|
locs = &locsPerLevelCache[level];
|
||||||
|
LOG_DEBUG("playerbots", "Random teleporting bot {} for level {} ({} locations available)", bot->GetName().c_str(),
|
||||||
|
bot->GetLevel(), locs->size());
|
||||||
if (level >= 10 && urand(0, 100) < sPlayerbotAIConfig->probTeleToBankers * 100)
|
if (level >= 10 && urand(0, 100) < sPlayerbotAIConfig->probTeleToBankers * 100)
|
||||||
{
|
{
|
||||||
std::vector<WorldLocation> fallbackLocs;
|
RandomTeleport(bot, bankerLocsPerLevelCache[level], true);
|
||||||
for (auto& bLoc : bankerLocsPerLevelCache[level])
|
|
||||||
fallbackLocs.push_back(bLoc.loc);
|
|
||||||
|
|
||||||
if (!sPlayerbotAIConfig->enableWeightTeleToCityBankers)
|
|
||||||
{
|
|
||||||
RandomTeleport(bot, fallbackLocs, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect valid cities based on bot faction.
|
|
||||||
std::unordered_set<CityId> validBankerCities;
|
|
||||||
for (auto& loc : bankerLocsPerLevelCache[level])
|
|
||||||
{
|
|
||||||
auto cityIt = bankerToCity.find(loc.entry);
|
|
||||||
if (cityIt == bankerToCity.end()) continue;
|
|
||||||
|
|
||||||
CityId cityId = cityIt->second.first;
|
|
||||||
FactionId cityFactionId = cityIt->second.second;
|
|
||||||
|
|
||||||
if ((IsAlliance(bot->getRace()) && cityFactionId == FactionId::ALLIANCE) ||
|
|
||||||
(!IsAlliance(bot->getRace()) && cityFactionId == FactionId::HORDE) ||
|
|
||||||
(cityFactionId == FactionId::NEUTRAL))
|
|
||||||
{
|
|
||||||
validBankerCities.insert(cityId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback if no valid cities
|
|
||||||
if (validBankerCities.empty())
|
|
||||||
{
|
|
||||||
RandomTeleport(bot, fallbackLocs, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply weights to valid cities
|
|
||||||
std::vector<CityId> weightedCities;
|
|
||||||
for (CityId city : validBankerCities)
|
|
||||||
{
|
|
||||||
int weight = 0;
|
|
||||||
switch (city)
|
|
||||||
{
|
|
||||||
case CityId::STORMWIND: weight = sPlayerbotAIConfig->weightTeleToStormwind; break;
|
|
||||||
case CityId::IRONFORGE: weight = sPlayerbotAIConfig->weightTeleToIronforge; break;
|
|
||||||
case CityId::DARNASSUS: weight = sPlayerbotAIConfig->weightTeleToDarnassus; break;
|
|
||||||
case CityId::EXODAR: weight = sPlayerbotAIConfig->weightTeleToExodar; break;
|
|
||||||
case CityId::ORGRIMMAR: weight = sPlayerbotAIConfig->weightTeleToOrgrimmar; break;
|
|
||||||
case CityId::UNDERCITY: weight = sPlayerbotAIConfig->weightTeleToUndercity; break;
|
|
||||||
case CityId::THUNDER_BLUFF: weight = sPlayerbotAIConfig->weightTeleToThunderBluff; break;
|
|
||||||
case CityId::SILVERMOON_CITY: weight = sPlayerbotAIConfig->weightTeleToSilvermoonCity; break;
|
|
||||||
case CityId::SHATTRATH_CITY: weight = sPlayerbotAIConfig->weightTeleToShattrathCity; break;
|
|
||||||
case CityId::DALARAN: weight = sPlayerbotAIConfig->weightTeleToDalaran; break;
|
|
||||||
default: weight = 0; break;
|
|
||||||
}
|
|
||||||
if (weight <= 0) continue;
|
|
||||||
|
|
||||||
for (int i = 0; i < weight; ++i)
|
|
||||||
{
|
|
||||||
weightedCities.push_back(city);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback if no valid cities
|
|
||||||
if (weightedCities.empty())
|
|
||||||
{
|
|
||||||
RandomTeleport(bot, fallbackLocs, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick a weighted city randomly, then a random banker in that city
|
|
||||||
// then teleport to that banker
|
|
||||||
CityId selectedCity = weightedCities[urand(0, weightedCities.size() - 1)];
|
|
||||||
const auto& bankers = cityToBankers.at(selectedCity);
|
|
||||||
uint32 selectedBankerEntry = bankers[urand(0, bankers.size() - 1)];
|
|
||||||
auto locIt = bankerEntryToLocation.find(selectedBankerEntry);
|
|
||||||
if (locIt != bankerEntryToLocation.end())
|
|
||||||
{
|
|
||||||
std::vector<WorldLocation> teleportTarget = { locIt->second };
|
|
||||||
RandomTeleport(bot, teleportTarget, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback if something went wrong
|
|
||||||
RandomTeleport(bot, *locs);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -183,11 +183,7 @@ public:
|
|||||||
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
|
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
|
||||||
};
|
};
|
||||||
std::map<uint32, LevelBracket> zone2LevelBracket;
|
std::map<uint32, LevelBracket> zone2LevelBracket;
|
||||||
struct BankerLocation {
|
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
|
||||||
WorldLocation loc;
|
|
||||||
uint32 entry;
|
|
||||||
};
|
|
||||||
std::map<uint8, std::vector<BankerLocation>> bankerLocsPerLevelCache;
|
|
||||||
|
|
||||||
// Account type management
|
// Account type management
|
||||||
void AssignAccountTypes();
|
void AssignAccountTypes();
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
#include "StatsWeightCalculator.h"
|
#include "StatsWeightCalculator.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) |
|
const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) |
|
||||||
(1LL << 33) | (1LL << 24) | (1LL << 34);
|
(1LL << 33) | (1LL << 24) | (1LL << 34);
|
||||||
@@ -124,10 +123,7 @@ void PlayerbotFactory::Init()
|
|||||||
if (id == 47181 || id == 50358 || id == 47242 || id == 52639 || id == 47147 || id == 7218) // Test Enchant
|
if (id == 47181 || id == 50358 || id == 47242 || id == 52639 || id == 47147 || id == 7218) // Test Enchant
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (id == 15463 || id == 15490) // Legendary Arcane Amalgamation
|
if (id == 15463) // Legendary Arcane Amalgamation
|
||||||
continue;
|
|
||||||
|
|
||||||
if (id == 29467 || id == 29475 || id == 29480 || id == 29483) // Naxx40 Sapphiron Shoulder Enchants
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(id);
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(id);
|
||||||
@@ -168,33 +164,22 @@ void PlayerbotFactory::Init()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
|
||||||
if (!proto)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto->ItemLevel < 60)
|
if (proto->ItemLevel < 60)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
|
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sRandomItemMgr->IsTestItem(gemId))
|
if (sRandomItemMgr->IsTestItem(gemId))
|
||||||
{
|
continue;
|
||||||
continue;
|
|
||||||
}
|
if (!proto || !sGemPropertiesStore.LookupEntry(proto->GemProperties))
|
||||||
|
|
||||||
if (!sGemPropertiesStore.LookupEntry(proto->GemProperties))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
|
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
|
||||||
enchantGemIdCache.push_back(gemId);
|
enchantGemIdCache.push_back(gemId);
|
||||||
}
|
}
|
||||||
@@ -1032,18 +1017,15 @@ void PlayerbotFactory::ClearSkills()
|
|||||||
}
|
}
|
||||||
bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0);
|
bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0);
|
||||||
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
|
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
|
||||||
|
|
||||||
// unlearn default race/class skills
|
// unlearn default race/class skills
|
||||||
if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass()))
|
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass());
|
||||||
{
|
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
|
||||||
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
|
{
|
||||||
{
|
uint32 skillId = itr->SkillId;
|
||||||
uint32 skillId = itr->SkillId;
|
if (!bot->HasSkill(skillId))
|
||||||
if (!bot->HasSkill(skillId))
|
continue;
|
||||||
continue;
|
bot->SetSkill(skillId, 0, 0, 0);
|
||||||
bot->SetSkill(skillId, 0, 0, 0);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotFactory::ClearEverything()
|
void PlayerbotFactory::ClearEverything()
|
||||||
@@ -1900,9 +1882,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
|||||||
|
|
||||||
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
||||||
packet << bagIndex << slot << dstBag;
|
packet << bagIndex << slot << dstBag;
|
||||||
WorldPackets::Item::AutoStoreBagItem nicePacket(std::move(packet));
|
bot->GetSession()->HandleAutoStoreBagItemOpcode(packet);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoStoreBagItemOpcode(nicePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||||
@@ -3250,7 +3230,7 @@ std::vector<uint32> PlayerbotFactory::GetCurrentGemsCount()
|
|||||||
|
|
||||||
void PlayerbotFactory::InitFood()
|
void PlayerbotFactory::InitFood()
|
||||||
{
|
{
|
||||||
if (botAI && botAI->HasCheat(BotCheatMask::food))
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4368,10 +4348,10 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// disable next expansion enchantments
|
// disable next expansion enchantments
|
||||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 27899)
|
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 25072)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell >= 44483)
|
if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell > 48557)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
|
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ void Qualified::Qualify(int qual)
|
|||||||
qualifier = out.str();
|
qualifier = out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string const Qualified::MultiQualify(const std::vector<std::string>& qualifiers, const std::string& separator, const std::string_view brackets)
|
std::string const Qualified::MultiQualify(std::vector<std::string> qualifiers, const std::string& separator, const std::string_view brackets)
|
||||||
{
|
{
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
for (uint8 i = 0; i < qualifiers.size(); ++i)
|
for (uint8 i = 0; i < qualifiers.size(); i++)
|
||||||
{
|
{
|
||||||
const std::string& qualifier = qualifiers[i];
|
const std::string& qualifier = qualifiers[i];
|
||||||
if (i == qualifiers.size() - 1)
|
if (i == qualifiers.size() - 1)
|
||||||
@@ -40,13 +40,13 @@ std::string const Qualified::MultiQualify(const std::vector<std::string>& qualif
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Qualified::getMultiQualifiers(const std::string& qualifier1)
|
std::vector<std::string> Qualified::getMultiQualifiers(std::string const qualifier1)
|
||||||
{
|
{
|
||||||
std::istringstream iss(qualifier1);
|
std::istringstream iss(qualifier1);
|
||||||
return {std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
|
return {std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 Qualified::getMultiQualifier(const std::string& qualifier1, uint32 pos)
|
int32 Qualified::getMultiQualifier(std::string const qualifier1, uint32 pos)
|
||||||
{
|
{
|
||||||
return std::stoi(getMultiQualifiers(qualifier1)[pos]);
|
return std::stoi(getMultiQualifiers(qualifier1)[pos]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
@@ -30,10 +29,10 @@ public:
|
|||||||
|
|
||||||
std::string const getQualifier() { return qualifier; }
|
std::string const getQualifier() { return qualifier; }
|
||||||
|
|
||||||
static std::string const MultiQualify(const std::vector<std::string>& qualifiers, const std::string& separator,
|
static std::string const MultiQualify(std::vector<std::string> qualifiers, const std::string& separator,
|
||||||
const std::string_view brackets = "{}");
|
const std::string_view brackets = "{}");
|
||||||
static std::vector<std::string> getMultiQualifiers(const std::string& qualifier1);
|
static std::vector<std::string> getMultiQualifiers(std::string const qualifier1);
|
||||||
static int32 getMultiQualifier(const std::string& qualifier1, uint32 pos);
|
static int32 getMultiQualifier(std::string const qualifier1, uint32 pos);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string qualifier;
|
std::string qualifier;
|
||||||
@@ -43,11 +42,11 @@ template <class T>
|
|||||||
class NamedObjectFactory
|
class NamedObjectFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
|
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
|
||||||
std::unordered_map<std::string, ObjectCreator> creators;
|
std::unordered_map<std::string, ObjectCreator> creators;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual T* create(std::string name, PlayerbotAI* botAI)
|
T* create(std::string name, PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
size_t found = name.find("::");
|
size_t found = name.find("::");
|
||||||
std::string qualifier;
|
std::string qualifier;
|
||||||
@@ -60,9 +59,11 @@ public:
|
|||||||
if (creators.find(name) == creators.end())
|
if (creators.find(name) == creators.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
ObjectCreator& creator = creators[name];
|
ObjectCreator creator = creators[name];
|
||||||
|
if (!creator)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
T* object = creator(botAI);
|
T* object = (*creator)(botAI);
|
||||||
Qualified* q = dynamic_cast<Qualified*>(object);
|
Qualified* q = dynamic_cast<Qualified*>(object);
|
||||||
if (q && found != std::string::npos)
|
if (q && found != std::string::npos)
|
||||||
q->Qualify(qualifier);
|
q->Qualify(qualifier);
|
||||||
@@ -73,7 +74,7 @@ public:
|
|||||||
std::set<std::string> supports()
|
std::set<std::string> supports()
|
||||||
{
|
{
|
||||||
std::set<std::string> keys;
|
std::set<std::string> keys;
|
||||||
for (typename std::unordered_map<std::string, ObjectCreator>::const_iterator it = creators.begin();
|
for (typename std::unordered_map<std::string, ObjectCreator>::iterator it = creators.begin();
|
||||||
it != creators.end(); it++)
|
it != creators.end(); it++)
|
||||||
keys.insert(it->first);
|
keys.insert(it->first);
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ public:
|
|||||||
|
|
||||||
virtual ~NamedObjectContext() { Clear(); }
|
virtual ~NamedObjectContext() { Clear(); }
|
||||||
|
|
||||||
virtual T* create(std::string name, PlayerbotAI* botAI) override
|
T* create(std::string const name, PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
if (created.find(name) == created.end())
|
if (created.find(name) == created.end())
|
||||||
return created[name] = NamedObjectFactory<T>::create(name, botAI);
|
return created[name] = NamedObjectFactory<T>::create(name, botAI);
|
||||||
@@ -102,7 +103,7 @@ public:
|
|||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
|
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
|
||||||
{
|
{
|
||||||
if (i->second)
|
if (i->second)
|
||||||
delete i->second;
|
delete i->second;
|
||||||
@@ -133,13 +134,13 @@ template <class T>
|
|||||||
class SharedNamedObjectContextList
|
class SharedNamedObjectContextList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
|
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
|
||||||
std::unordered_map<std::string, ObjectCreator> creators;
|
std::unordered_map<std::string, ObjectCreator> creators;
|
||||||
std::vector<NamedObjectContext<T>*> contexts;
|
std::vector<NamedObjectContext<T>*> contexts;
|
||||||
|
|
||||||
~SharedNamedObjectContextList()
|
~SharedNamedObjectContextList()
|
||||||
{
|
{
|
||||||
for (typename std::vector<NamedObjectContext<T>*>::const_iterator i = contexts.begin(); i != contexts.end(); i++)
|
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
|
||||||
delete *i;
|
delete *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ template <class T>
|
|||||||
class NamedObjectContextList
|
class NamedObjectContextList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
|
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
|
||||||
const std::unordered_map<std::string, ObjectCreator>& creators;
|
const std::unordered_map<std::string, ObjectCreator>& creators;
|
||||||
const std::vector<NamedObjectContext<T>*>& contexts;
|
const std::vector<NamedObjectContext<T>*>& contexts;
|
||||||
std::unordered_map<std::string, T*> created;
|
std::unordered_map<std::string, T*> created;
|
||||||
@@ -169,7 +170,7 @@ public:
|
|||||||
|
|
||||||
~NamedObjectContextList()
|
~NamedObjectContextList()
|
||||||
{
|
{
|
||||||
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
|
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
|
||||||
{
|
{
|
||||||
if (i->second)
|
if (i->second)
|
||||||
delete i->second;
|
delete i->second;
|
||||||
@@ -191,9 +192,11 @@ public:
|
|||||||
if (creators.find(name) == creators.end())
|
if (creators.find(name) == creators.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const ObjectCreator& creator = creators.at(name);
|
ObjectCreator creator = creators.at(name);
|
||||||
|
if (!creator)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
T* object = creator(botAI);
|
T* object = (*creator)(botAI);
|
||||||
Qualified* q = dynamic_cast<Qualified*>(object);
|
Qualified* q = dynamic_cast<Qualified*>(object);
|
||||||
if (q && found != std::string::npos)
|
if (q && found != std::string::npos)
|
||||||
q->Qualify(qualifier);
|
q->Qualify(qualifier);
|
||||||
@@ -201,7 +204,7 @@ public:
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* GetContextObject(const std::string& name, PlayerbotAI* botAI)
|
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
if (created.find(name) == created.end())
|
if (created.find(name) == created.end())
|
||||||
{
|
{
|
||||||
@@ -211,7 +214,7 @@ public:
|
|||||||
return created[name];
|
return created[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> GetSiblings(const std::string& name)
|
std::set<std::string> GetSiblings(std::string const name)
|
||||||
{
|
{
|
||||||
for (auto i = contexts.begin(); i != contexts.end(); i++)
|
for (auto i = contexts.begin(); i != contexts.end(); i++)
|
||||||
{
|
{
|
||||||
@@ -237,7 +240,7 @@ public:
|
|||||||
{
|
{
|
||||||
std::set<std::string> supported = (*i)->supports();
|
std::set<std::string> supported = (*i)->supports();
|
||||||
|
|
||||||
for (std::set<std::string>::const_iterator j = supported.begin(); j != supported.end(); ++j)
|
for (std::set<std::string>::iterator j = supported.begin(); j != supported.end(); j++)
|
||||||
result.insert(*j);
|
result.insert(*j);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +250,7 @@ public:
|
|||||||
std::set<std::string> GetCreated()
|
std::set<std::string> GetCreated()
|
||||||
{
|
{
|
||||||
std::set<std::string> result;
|
std::set<std::string> result;
|
||||||
for (typename std::unordered_map<std::string, T*>::const_iterator i = created.begin(); i != created.end(); i++)
|
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
|
||||||
{
|
{
|
||||||
result.insert(i->first);
|
result.insert(i->first);
|
||||||
}
|
}
|
||||||
@@ -260,13 +263,13 @@ template <class T>
|
|||||||
class NamedObjectFactoryList
|
class NamedObjectFactoryList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ObjectCreator = std::function<T*(PlayerbotAI* ai)>;
|
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
|
||||||
std::vector<NamedObjectFactory<T>*> factories;
|
std::vector<NamedObjectFactory<T>*> factories;
|
||||||
std::unordered_map<std::string, ObjectCreator> creators;
|
std::unordered_map<std::string, ObjectCreator> creators;
|
||||||
|
|
||||||
virtual ~NamedObjectFactoryList()
|
virtual ~NamedObjectFactoryList()
|
||||||
{
|
{
|
||||||
for (typename std::vector<NamedObjectFactory<T>*>::const_iterator i = factories.begin(); i != factories.end(); i++)
|
for (typename std::vector<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
|
||||||
delete *i;
|
delete *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,9 +286,11 @@ public:
|
|||||||
if (creators.find(name) == creators.end())
|
if (creators.find(name) == creators.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const ObjectCreator& creator = creators[name];
|
ObjectCreator creator = creators[name];
|
||||||
|
if (!creator)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
T* object = creator(botAI);
|
T* object = (*creator)(botAI);
|
||||||
Qualified* q = dynamic_cast<Qualified*>(object);
|
Qualified* q = dynamic_cast<Qualified*>(object);
|
||||||
if (q && found != std::string::npos)
|
if (q && found != std::string::npos)
|
||||||
q->Qualify(qualifier);
|
q->Qualify(qualifier);
|
||||||
@@ -302,7 +307,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T* GetContextObject(const std::string& name, PlayerbotAI* botAI)
|
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
if (T* object = create(name, botAI))
|
if (T* object = create(name, botAI))
|
||||||
return object;
|
return object;
|
||||||
|
|||||||
@@ -246,8 +246,7 @@ public:
|
|||||||
creators["rpg mount anim"] = &ActionContext::rpg_mount_anim;
|
creators["rpg mount anim"] = &ActionContext::rpg_mount_anim;
|
||||||
|
|
||||||
creators["toggle pet spell"] = &ActionContext::toggle_pet_spell;
|
creators["toggle pet spell"] = &ActionContext::toggle_pet_spell;
|
||||||
creators["pet attack"] = &ActionContext::pet_attack;
|
creators["pet attack"] = &ActionContext::pet_attack;
|
||||||
creators["set pet stance"] = &ActionContext::set_pet_stance;
|
|
||||||
|
|
||||||
creators["new rpg status update"] = &ActionContext::new_rpg_status_update;
|
creators["new rpg status update"] = &ActionContext::new_rpg_status_update;
|
||||||
creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind;
|
creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind;
|
||||||
@@ -435,7 +434,6 @@ private:
|
|||||||
|
|
||||||
static Action* toggle_pet_spell(PlayerbotAI* ai) { return new TogglePetSpellAutoCastAction(ai); }
|
static Action* toggle_pet_spell(PlayerbotAI* ai) { return new TogglePetSpellAutoCastAction(ai); }
|
||||||
static Action* pet_attack(PlayerbotAI* ai) { return new PetAttackAction(ai); }
|
static Action* pet_attack(PlayerbotAI* ai) { return new PetAttackAction(ai); }
|
||||||
static Action* set_pet_stance(PlayerbotAI* ai) { return new SetPetStanceAction(ai); }
|
|
||||||
|
|
||||||
static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); }
|
static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); }
|
||||||
static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); }
|
static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); }
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ bool BossFireResistanceAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
PaladinFireResistanceStrategy paladinFireResistanceStrategy(botAI);
|
PaladinFireResistanceStrategy paladinFireResistanceStrategy(botAI);
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFireResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFireResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
||||||
botAI->DoSpecificAction("fire resistance aura", Event(), true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +35,6 @@ bool BossFrostResistanceAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
PaladinFrostResistanceStrategy paladinFrostResistanceStrategy(botAI);
|
PaladinFrostResistanceStrategy paladinFrostResistanceStrategy(botAI);
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFrostResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFrostResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
||||||
botAI->DoSpecificAction("frost resistance aura", Event(), true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +48,6 @@ bool BossNatureResistanceAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI);
|
HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI);
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
||||||
botAI->DoSpecificAction("aspect of the wild", Event(), true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +61,5 @@ bool BossShadowResistanceAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI);
|
PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI);
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
||||||
botAI->DoSpecificAction("shadow resistance aura", Event(), true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,10 +79,9 @@
|
|||||||
#include "OpenItemAction.h"
|
#include "OpenItemAction.h"
|
||||||
#include "UnlockItemAction.h"
|
#include "UnlockItemAction.h"
|
||||||
#include "UnlockTradedItemAction.h"
|
#include "UnlockTradedItemAction.h"
|
||||||
#include "TameAction.h"
|
#include "PetAction.h"
|
||||||
#include "TellGlyphsAction.h"
|
#include "TellGlyphsAction.h"
|
||||||
#include "EquipGlyphsAction.h"
|
#include "EquipGlyphsAction.h"
|
||||||
#include "PetAction.h"
|
|
||||||
|
|
||||||
class ChatActionContext : public NamedObjectContext<Action>
|
class ChatActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@@ -192,11 +191,9 @@ public:
|
|||||||
creators["lfg"] = &ChatActionContext::lfg;
|
creators["lfg"] = &ChatActionContext::lfg;
|
||||||
creators["calc"] = &ChatActionContext::calc;
|
creators["calc"] = &ChatActionContext::calc;
|
||||||
creators["wipe"] = &ChatActionContext::wipe;
|
creators["wipe"] = &ChatActionContext::wipe;
|
||||||
creators["tame"] = &ChatActionContext::tame;
|
creators["pet"] = &ChatActionContext::pet;
|
||||||
creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs
|
creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs
|
||||||
creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs
|
creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs
|
||||||
creators["pet"] = &ChatActionContext::pet;
|
|
||||||
creators["pet attack"] = &ChatActionContext::pet_attack;
|
|
||||||
creators["roll"] = &ChatActionContext::roll_action;
|
creators["roll"] = &ChatActionContext::roll_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,11 +301,9 @@ private:
|
|||||||
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
||||||
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
|
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
|
||||||
static Action* wipe(PlayerbotAI* ai) { return new WipeAction(ai); }
|
static Action* wipe(PlayerbotAI* ai) { return new WipeAction(ai); }
|
||||||
static Action* tame(PlayerbotAI* botAI) { return new TameAction(botAI); }
|
static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); }
|
||||||
static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs
|
static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs
|
||||||
static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs
|
static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs
|
||||||
static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); }
|
|
||||||
static Action* pet_attack(PlayerbotAI* botAI) { return new PetAction(botAI, "attack"); }
|
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EquipAction.h"
|
#include "EquipAction.h"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemCountValue.h"
|
#include "ItemCountValue.h"
|
||||||
@@ -12,7 +11,6 @@
|
|||||||
#include "ItemVisitors.h"
|
#include "ItemVisitors.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "StatsWeightCalculator.h"
|
#include "StatsWeightCalculator.h"
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
bool EquipAction::Execute(Event event)
|
bool EquipAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@@ -106,10 +104,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid itemguid = item->GetGUID();
|
ObjectGuid itemguid = item->GetGUID();
|
||||||
packet << itemguid << uint8(EQUIPMENT_SLOT_RANGED);
|
packet << itemguid << uint8(EQUIPMENT_SLOT_RANGED);
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(packet);
|
||||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(packet));
|
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "Equipping " << chat->FormatItem(itemProto) << " in ranged slot";
|
out << "Equipping " << chat->FormatItem(itemProto) << " in ranged slot";
|
||||||
@@ -204,9 +199,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid newItemGuid = item->GetGUID();
|
ObjectGuid newItemGuid = item->GetGUID();
|
||||||
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_MAINHAND);
|
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_MAINHAND);
|
||||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try moving old main hand weapon to offhand if beneficial
|
// Try moving old main hand weapon to offhand if beneficial
|
||||||
@@ -217,9 +210,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid oldMHGuid = mainHandItem->GetGUID();
|
ObjectGuid oldMHGuid = mainHandItem->GetGUID();
|
||||||
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
||||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(offhandPacket));
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
|
||||||
|
|
||||||
std::ostringstream moveMsg;
|
std::ostringstream moveMsg;
|
||||||
moveMsg << "Main hand upgrade found. Moving " << chat->FormatItem(oldMHProto) << " to offhand";
|
moveMsg << "Main hand upgrade found. Moving " << chat->FormatItem(oldMHProto) << " to offhand";
|
||||||
@@ -239,9 +230,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid newItemGuid = item->GetGUID();
|
ObjectGuid newItemGuid = item->GetGUID();
|
||||||
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
||||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "Equipping " << chat->FormatItem(itemProto) << " in offhand";
|
out << "Equipping " << chat->FormatItem(itemProto) << " in offhand";
|
||||||
@@ -298,9 +287,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid itemguid = item->GetGUID();
|
ObjectGuid itemguid = item->GetGUID();
|
||||||
packet << itemguid << dstSlot;
|
packet << itemguid << dstSlot;
|
||||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(packet));
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(packet);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,10 @@ bool FollowAction::Execute(Event event)
|
|||||||
true, priority, true);
|
true, priority, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This section has been commented out because it was forcing the pet to
|
if (Pet* pet = bot->GetPet())
|
||||||
// follow the bot on every "follow" action tick, overriding any attack or
|
{
|
||||||
// stay commands that might have been issued by the player.
|
botAI->PetFollow();
|
||||||
// if (Pet* pet = bot->GetPet())
|
}
|
||||||
// {
|
|
||||||
// botAI->PetFollow();
|
|
||||||
// }
|
|
||||||
// if (moved)
|
// if (moved)
|
||||||
// botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
|
// botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GenericActions.h"
|
#include "GenericActions.h"
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "Player.h"
|
|
||||||
#include "Pet.h"
|
|
||||||
#include "PlayerbotAIConfig.h"
|
|
||||||
#include "CreatureAI.h"
|
#include "CreatureAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "CharmInfo.h"
|
|
||||||
#include "SharedDefines.h"
|
|
||||||
#include "ObjectGuid.h"
|
|
||||||
#include "SpellMgr.h"
|
|
||||||
#include "SpellInfo.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
enum PetSpells
|
enum PetSpells
|
||||||
{
|
{
|
||||||
@@ -33,8 +23,7 @@ enum PetSpells
|
|||||||
PET_DEVOUR_MAGIC_4 = 19736,
|
PET_DEVOUR_MAGIC_4 = 19736,
|
||||||
PET_DEVOUR_MAGIC_5 = 27276,
|
PET_DEVOUR_MAGIC_5 = 27276,
|
||||||
PET_DEVOUR_MAGIC_6 = 27277,
|
PET_DEVOUR_MAGIC_6 = 27277,
|
||||||
PET_DEVOUR_MAGIC_7 = 48011,
|
PET_DEVOUR_MAGIC_7 = 48011
|
||||||
PET_SPIRIT_WOLF_LEAP = 58867
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<uint32> disabledPetSpells = {
|
static std::vector<uint32> disabledPetSpells = {
|
||||||
@@ -42,7 +31,7 @@ static std::vector<uint32> disabledPetSpells = {
|
|||||||
PET_COWER, PET_LEAP,
|
PET_COWER, PET_LEAP,
|
||||||
PET_SPELL_LOCK_1, PET_SPELL_LOCK_2,
|
PET_SPELL_LOCK_1, PET_SPELL_LOCK_2,
|
||||||
PET_DEVOUR_MAGIC_1, PET_DEVOUR_MAGIC_2, PET_DEVOUR_MAGIC_3,
|
PET_DEVOUR_MAGIC_1, PET_DEVOUR_MAGIC_2, PET_DEVOUR_MAGIC_3,
|
||||||
PET_DEVOUR_MAGIC_4, PET_DEVOUR_MAGIC_5, PET_DEVOUR_MAGIC_6, PET_DEVOUR_MAGIC_7, PET_SPIRIT_WOLF_LEAP
|
PET_DEVOUR_MAGIC_4, PET_DEVOUR_MAGIC_5, PET_DEVOUR_MAGIC_6, PET_DEVOUR_MAGIC_7
|
||||||
};
|
};
|
||||||
|
|
||||||
bool MeleeAction::isUseful()
|
bool MeleeAction::isUseful()
|
||||||
@@ -111,11 +100,6 @@ bool TogglePetSpellAutoCastAction::Execute(Event event)
|
|||||||
toggled = true;
|
toggled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug message if pet spells have been toggled and debug is enabled
|
|
||||||
if (toggled && sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
||||||
botAI->TellMaster("Pet autocast spells have been toggled.");
|
|
||||||
|
|
||||||
return toggled;
|
return toggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,23 +107,22 @@ bool PetAttackAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
Guardian* pet = bot->GetGuardianPet();
|
Guardian* pet = bot->GetGuardianPet();
|
||||||
if (!pet)
|
if (!pet)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
// Do not attack if the pet's stance is set to "passive".
|
|
||||||
if (pet->GetReactState() == REACT_PASSIVE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (!target)
|
if (!target)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!bot->IsValidAttackTarget(target))
|
if (!bot->IsValidAttackTarget(target))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// This section has been commented because it was overriding the
|
pet->SetReactState(REACT_PASSIVE);
|
||||||
// pet's stance to "passive" every time the attack action was executed.
|
|
||||||
// pet->SetReactState(REACT_PASSIVE);
|
|
||||||
|
|
||||||
pet->ClearUnitState(UNIT_STATE_FOLLOW);
|
pet->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||||
pet->AttackStop();
|
pet->AttackStop();
|
||||||
pet->SetTarget(target->GetGUID());
|
pet->SetTarget(target->GetGUID());
|
||||||
@@ -153,76 +136,3 @@ bool PetAttackAction::Execute(Event event)
|
|||||||
pet->ToCreature()->AI()->AttackStart(target);
|
pet->ToCreature()->AI()->AttackStart(target);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetPetStanceAction::Execute(Event /*event*/)
|
|
||||||
{
|
|
||||||
// Prepare a list to hold all controlled pet and guardian creatures
|
|
||||||
std::vector<Creature*> targets;
|
|
||||||
|
|
||||||
// Add the bot's main pet (if it exists) to the target list
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
if (pet)
|
|
||||||
targets.push_back(pet);
|
|
||||||
|
|
||||||
// Loop through all units controlled by the bot (could be pets, guardians, etc.)
|
|
||||||
for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); itr != bot->m_Controlled.end(); ++itr)
|
|
||||||
{
|
|
||||||
// Only add creatures (skip players, vehicles, etc.)
|
|
||||||
Creature* creature = dynamic_cast<Creature*>(*itr);
|
|
||||||
if (!creature)
|
|
||||||
continue;
|
|
||||||
// Avoid adding the main pet twice
|
|
||||||
if (pet && creature == pet)
|
|
||||||
continue;
|
|
||||||
targets.push_back(creature);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no controlled pets or guardians, notify the player and exit
|
|
||||||
if (targets.empty())
|
|
||||||
{
|
|
||||||
botAI->TellError("You have no pet or guardian pet.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the default pet stance from the configuration
|
|
||||||
int32 stance = sPlayerbotAIConfig->defaultPetStance;
|
|
||||||
ReactStates react = REACT_DEFENSIVE;
|
|
||||||
std::string stanceText = "defensive (from config, fallback)";
|
|
||||||
|
|
||||||
// Map the config stance integer to a ReactStates value and a message
|
|
||||||
switch (stance)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
react = REACT_PASSIVE;
|
|
||||||
stanceText = "passive (from config)";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
react = REACT_DEFENSIVE;
|
|
||||||
stanceText = "defensive (from config)";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
react = REACT_AGGRESSIVE;
|
|
||||||
stanceText = "aggressive (from config)";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
react = REACT_DEFENSIVE;
|
|
||||||
stanceText = "defensive (from config, fallback)";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the stance to all target creatures (pets/guardians)
|
|
||||||
for (Creature* target : targets)
|
|
||||||
{
|
|
||||||
target->SetReactState(react);
|
|
||||||
CharmInfo* charmInfo = target->GetCharmInfo();
|
|
||||||
// If the creature has a CharmInfo, set the player-visible stance as well
|
|
||||||
if (charmInfo)
|
|
||||||
charmInfo->SetPlayerReactState(react);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If debug is enabled in config, inform the master of the new stance
|
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
||||||
botAI->TellMaster("Pet stance set to " + stanceText + " (applied to all pets/guardians).");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#define _PLAYERBOT_GENERICACTIONS_H
|
#define _PLAYERBOT_GENERICACTIONS_H
|
||||||
|
|
||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "Action.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@@ -35,12 +33,4 @@ public:
|
|||||||
virtual bool Execute(Event event) override;
|
virtual bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetPetStanceAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SetPetStanceAction(PlayerbotAI* botAI) : Action(botAI, "set pet stance") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ bool GiveFoodAction::isUseful()
|
|||||||
|
|
||||||
bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget());
|
bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget());
|
||||||
|
|
||||||
return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food));
|
return !isRandomBot || (isRandomBot && !sPlayerbotAIConfig->freeFood);
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* GiveWaterAction::GetTarget() { return AI_VALUE(Unit*, "party member without water"); }
|
Unit* GiveWaterAction::GetTarget() { return AI_VALUE(Unit*, "party member without water"); }
|
||||||
@@ -88,5 +88,5 @@ bool GiveWaterAction::isUseful()
|
|||||||
|
|
||||||
bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget());
|
bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget());
|
||||||
|
|
||||||
return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food));
|
return !isRandomBot || (isRandomBot && !sPlayerbotAIConfig->freeFood);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2767,177 +2767,3 @@ bool MoveFromGroupAction::Execute(Event event)
|
|||||||
distance = 20.0f; // flee distance from config is too small for this
|
distance = 20.0f; // flee distance from config is too small for this
|
||||||
return MoveFromGroup(distance);
|
return MoveFromGroup(distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveAwayFromCreatureAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive);
|
|
||||||
|
|
||||||
// Find all creatures with the specified Id
|
|
||||||
std::vector<Unit*> creatures;
|
|
||||||
for (const auto& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (unit && (alive && unit->IsAlive()) && unit->GetEntry() == creatureId)
|
|
||||||
{
|
|
||||||
creatures.push_back(unit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (creatures.empty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for a safe position
|
|
||||||
const int directions = 8;
|
|
||||||
const float increment = 3.0f;
|
|
||||||
float bestX = bot->GetPositionX();
|
|
||||||
float bestY = bot->GetPositionY();
|
|
||||||
float bestZ = bot->GetPositionZ();
|
|
||||||
float maxSafetyScore = -1.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < directions; ++i)
|
|
||||||
{
|
|
||||||
float angle = (i * 2 * M_PI) / directions;
|
|
||||||
for (float distance = increment; distance <= 30.0f; distance += increment)
|
|
||||||
{
|
|
||||||
float moveX = bot->GetPositionX() + distance * cos(angle);
|
|
||||||
float moveY = bot->GetPositionY() + distance * sin(angle);
|
|
||||||
float moveZ = bot->GetPositionZ();
|
|
||||||
|
|
||||||
// Check creature distance constraints
|
|
||||||
bool isSafeFromCreatures = true;
|
|
||||||
float minCreatureDist = std::numeric_limits<float>::max();
|
|
||||||
for (Unit* creature : creatures)
|
|
||||||
{
|
|
||||||
float dist = creature->GetExactDist2d(moveX, moveY);
|
|
||||||
if (dist < range)
|
|
||||||
{
|
|
||||||
isSafeFromCreatures = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dist < minCreatureDist)
|
|
||||||
{
|
|
||||||
minCreatureDist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSafeFromCreatures && bot->IsWithinLOS(moveX, moveY, moveZ))
|
|
||||||
{
|
|
||||||
// A simple safety score: the minimum distance to any creature. Higher is better.
|
|
||||||
if (minCreatureDist > maxSafetyScore)
|
|
||||||
{
|
|
||||||
maxSafetyScore = minCreatureDist;
|
|
||||||
bestX = moveX;
|
|
||||||
bestY = moveY;
|
|
||||||
bestZ = moveZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the best position found
|
|
||||||
if (maxSafetyScore > 0.0f)
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MoveAwayFromCreatureAction::isPossible()
|
|
||||||
{
|
|
||||||
return bot->CanFreeMove();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Player* closestPlayer = nullptr;
|
|
||||||
float minDistance = 0.0f;
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Player*> debuffedPlayers;
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* player = gref->GetSource();
|
|
||||||
if (player && player->IsAlive() && player->HasAura(spellId))
|
|
||||||
{
|
|
||||||
debuffedPlayers.push_back(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debuffedPlayers.empty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for a safe position
|
|
||||||
const int directions = 8;
|
|
||||||
const float increment = 3.0f;
|
|
||||||
float bestX = bot->GetPositionX();
|
|
||||||
float bestY = bot->GetPositionY();
|
|
||||||
float bestZ = bot->GetPositionZ();
|
|
||||||
float maxSafetyScore = -1.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < directions; ++i)
|
|
||||||
{
|
|
||||||
float angle = (i * 2 * M_PI) / directions;
|
|
||||||
for (float distance = increment; distance <= (range + 5.0f); distance += increment)
|
|
||||||
{
|
|
||||||
float moveX = bot->GetPositionX() + distance * cos(angle);
|
|
||||||
float moveY = bot->GetPositionY() + distance * sin(angle);
|
|
||||||
float moveZ = bot->GetPositionZ();
|
|
||||||
|
|
||||||
// Check creature distance constraints
|
|
||||||
bool isSafeFromDebuffedPlayer = true;
|
|
||||||
float minDebuffedPlayerDistance = std::numeric_limits<float>::max();
|
|
||||||
for (Unit* debuffedPlayer : debuffedPlayers)
|
|
||||||
{
|
|
||||||
float dist = debuffedPlayer->GetExactDist2d(moveX, moveY);
|
|
||||||
if (dist < range)
|
|
||||||
{
|
|
||||||
isSafeFromDebuffedPlayer = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dist < minDebuffedPlayerDistance)
|
|
||||||
{
|
|
||||||
minDebuffedPlayerDistance = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSafeFromDebuffedPlayer && bot->IsWithinLOS(moveX, moveY, moveZ))
|
|
||||||
{
|
|
||||||
// A simple safety score: the minimum distance to any debuffed player. Higher is better.
|
|
||||||
if (minDebuffedPlayerDistance > maxSafetyScore)
|
|
||||||
{
|
|
||||||
maxSafetyScore = minDebuffedPlayerDistance;
|
|
||||||
bestX = moveX;
|
|
||||||
bestY = moveY;
|
|
||||||
bestZ = moveZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the best position found
|
|
||||||
if (maxSafetyScore > 0.0f)
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MoveAwayFromPlayerWithDebuffAction::isPossible()
|
|
||||||
{
|
|
||||||
return bot->CanFreeMove();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -294,34 +294,4 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MoveAwayFromCreatureAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveAwayFromCreatureAction(PlayerbotAI* botAI, std::string name, uint32 creatureId, float range, bool alive = true)
|
|
||||||
: MovementAction(botAI, name), creatureId(creatureId), range(range), alive(alive) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isPossible() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32 creatureId;
|
|
||||||
float range;
|
|
||||||
bool alive;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveAwayFromPlayerWithDebuffAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveAwayFromPlayerWithDebuffAction(PlayerbotAI* botAI, std::string name, uint32 spellId, float range)
|
|
||||||
: MovementAction(botAI, name), spellId(spellId), range(range) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isPossible() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32 spellId;
|
|
||||||
float range;
|
|
||||||
bool alive;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ bool DrinkAction::Execute(Event event)
|
|||||||
if (!hasMana)
|
if (!hasMana)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::food))
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
{
|
{
|
||||||
// if (bot->IsNonMeleeSpellCast(true))
|
// if (bot->IsNonMeleeSpellCast(true))
|
||||||
// return false;
|
// return false;
|
||||||
@@ -54,11 +54,11 @@ bool DrinkAction::Execute(Event event)
|
|||||||
return UseItemAction::Execute(event);
|
return UseItemAction::Execute(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrinkAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8, "mana", "self target") < 100; }
|
bool DrinkAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8, "mana", "self target") < 85; }
|
||||||
|
|
||||||
bool DrinkAction::isPossible()
|
bool DrinkAction::isPossible()
|
||||||
{
|
{
|
||||||
return !bot->IsInCombat() && (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible());
|
return !bot->IsInCombat() && (sPlayerbotAIConfig->freeFood || UseItemAction::isPossible());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EatAction::Execute(Event event)
|
bool EatAction::Execute(Event event)
|
||||||
@@ -66,7 +66,7 @@ bool EatAction::Execute(Event event)
|
|||||||
if (bot->IsInCombat())
|
if (bot->IsInCombat())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::food))
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
{
|
{
|
||||||
// if (bot->IsNonMeleeSpellCast(true))
|
// if (bot->IsNonMeleeSpellCast(true))
|
||||||
// return false;
|
// return false;
|
||||||
@@ -107,5 +107,5 @@ bool EatAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8
|
|||||||
|
|
||||||
bool EatAction::isPossible()
|
bool EatAction::isPossible()
|
||||||
{
|
{
|
||||||
return !bot->IsInCombat() && (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible());
|
return !bot->IsInCombat() && (sPlayerbotAIConfig->freeFood || UseItemAction::isPossible());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemVisitors.h"
|
#include "ItemVisitors.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
bool OutfitAction::Execute(Event event)
|
bool OutfitAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@@ -71,9 +70,7 @@ bool OutfitAction::Execute(Event event)
|
|||||||
|
|
||||||
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
||||||
packet << bagIndex << slot << dstBag;
|
packet << bagIndex << slot << dstBag;
|
||||||
WorldPackets::Item::AutoStoreBagItem nicePacket(std::move(packet));
|
bot->GetSession()->HandleAutoStoreBagItemOpcode(packet);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoStoreBagItemOpcode(nicePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EquipItems(outfit);
|
EquipItems(outfit);
|
||||||
|
|||||||
@@ -4,254 +4,373 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PetAction.h"
|
#include "PetAction.h"
|
||||||
|
#include <algorithm>
|
||||||
#include "CharmInfo.h"
|
#include <iomanip>
|
||||||
#include "Creature.h"
|
#include <sstream>
|
||||||
#include "CreatureAI.h"
|
|
||||||
#include "Pet.h"
|
#include "Pet.h"
|
||||||
|
#include "SpellMgr.h"
|
||||||
|
#include "DBCStructure.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "SharedDefines.h"
|
#include "PlayerbotFactory.h"
|
||||||
|
#include <random>
|
||||||
|
#include <cctype>
|
||||||
|
#include "WorldSession.h"
|
||||||
|
|
||||||
|
bool IsExoticPet(const CreatureTemplate* creature)
|
||||||
|
{
|
||||||
|
// Use the IsExotic() method from CreatureTemplate
|
||||||
|
return creature && creature->IsExotic();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasBeastMastery(Player* bot)
|
||||||
|
{
|
||||||
|
// Beast Mastery talent aura ID for WotLK is 53270
|
||||||
|
return bot->HasAura(53270);
|
||||||
|
}
|
||||||
|
|
||||||
bool PetAction::Execute(Event event)
|
bool PetAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
// Extract the command parameter from the event (e.g., "aggressive", "defensive", "attack", etc.)
|
|
||||||
std::string param = event.getParam();
|
std::string param = event.getParam();
|
||||||
if (param.empty() && !defaultCmd.empty())
|
std::istringstream iss(param);
|
||||||
{
|
std::string mode, value;
|
||||||
param = defaultCmd;
|
iss >> mode;
|
||||||
}
|
std::getline(iss, value);
|
||||||
|
value.erase(0, value.find_first_not_of(" ")); // trim leading spaces
|
||||||
|
|
||||||
if (param.empty())
|
bool found = false;
|
||||||
{
|
|
||||||
// If no parameter is provided, show usage instructions and return.
|
|
||||||
botAI->TellError("Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player* bot = botAI->GetBot();
|
// Reset lastPetName/Id each time
|
||||||
|
lastPetName = "";
|
||||||
|
lastPetId = 0;
|
||||||
|
|
||||||
// Collect all controlled pets and guardians, except totems, into the targets vector.
|
if (mode == "name" && !value.empty())
|
||||||
std::vector<Creature*> targets;
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
if (pet)
|
|
||||||
targets.push_back(pet);
|
|
||||||
|
|
||||||
for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); itr != bot->m_Controlled.end(); ++itr)
|
|
||||||
{
|
{
|
||||||
Creature* creature = dynamic_cast<Creature*>(*itr);
|
found = SetPetByName(value);
|
||||||
if (!creature)
|
|
||||||
continue;
|
|
||||||
if (pet && creature == pet)
|
|
||||||
continue;
|
|
||||||
if (creature->IsTotem())
|
|
||||||
continue;
|
|
||||||
targets.push_back(creature);
|
|
||||||
}
|
}
|
||||||
|
else if (mode == "id" && !value.empty())
|
||||||
// If no pets or guardians are found, notify and return.
|
|
||||||
if (targets.empty())
|
|
||||||
{
|
{
|
||||||
botAI->TellError("You have no pet or guardian pet.");
|
try
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactStates react;
|
|
||||||
std::string stanceText;
|
|
||||||
|
|
||||||
// Handle stance commands: aggressive, defensive, or passive.
|
|
||||||
if (param == "aggressive")
|
|
||||||
{
|
|
||||||
react = REACT_AGGRESSIVE;
|
|
||||||
stanceText = "aggressive";
|
|
||||||
}
|
|
||||||
else if (param == "defensive")
|
|
||||||
{
|
|
||||||
react = REACT_DEFENSIVE;
|
|
||||||
stanceText = "defensive";
|
|
||||||
}
|
|
||||||
else if (param == "passive")
|
|
||||||
{
|
|
||||||
react = REACT_PASSIVE;
|
|
||||||
stanceText = "passive";
|
|
||||||
}
|
|
||||||
// The "stance" command simply reports the current stance of each pet/guardian.
|
|
||||||
else if (param == "stance")
|
|
||||||
{
|
|
||||||
for (Creature* target : targets)
|
|
||||||
{
|
{
|
||||||
std::string type = target->IsPet() ? "pet" : "guardian";
|
uint32 id = std::stoul(value);
|
||||||
std::string name = target->GetName();
|
found = SetPetById(id);
|
||||||
std::string stance;
|
}
|
||||||
switch (target->GetReactState())
|
catch (...)
|
||||||
{
|
{
|
||||||
case REACT_AGGRESSIVE:
|
botAI->TellError("Invalid pet id.");
|
||||||
stance = "aggressive";
|
|
||||||
break;
|
|
||||||
case REACT_DEFENSIVE:
|
|
||||||
stance = "defensive";
|
|
||||||
break;
|
|
||||||
case REACT_PASSIVE:
|
|
||||||
stance = "passive";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
stance = "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
botAI->TellMaster("Current stance of " + type + " \"" + name + "\": " + stance + ".");
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
// The "attack" command forces pets/guardians to attack the master's selected target.
|
else if (mode == "family" && !value.empty())
|
||||||
else if (param == "attack")
|
|
||||||
{
|
{
|
||||||
// Try to get the master's selected target.
|
found = SetPetByFamily(value);
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
Unit* targetUnit = nullptr;
|
|
||||||
|
|
||||||
if (master)
|
|
||||||
{
|
|
||||||
ObjectGuid masterTargetGuid = master->GetTarget();
|
|
||||||
if (!masterTargetGuid.IsEmpty())
|
|
||||||
{
|
|
||||||
targetUnit = botAI->GetUnit(masterTargetGuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no valid target is selected, show an error and return.
|
|
||||||
if (!targetUnit)
|
|
||||||
{
|
|
||||||
botAI->TellError("No valid target selected by master.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!targetUnit->IsAlive())
|
|
||||||
{
|
|
||||||
botAI->TellError("Target is not alive.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!bot->IsValidAttackTarget(targetUnit))
|
|
||||||
{
|
|
||||||
botAI->TellError("Target is not a valid attack target for the bot.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool didAttack = false;
|
|
||||||
// For each controlled pet/guardian, command them to attack the selected target.
|
|
||||||
for (Creature* petCreature : targets)
|
|
||||||
{
|
|
||||||
CharmInfo* charmInfo = petCreature->GetCharmInfo();
|
|
||||||
if (!charmInfo)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
petCreature->ClearUnitState(UNIT_STATE_FOLLOW);
|
|
||||||
// Only command attack if not already attacking the target, or if not currently under command attack.
|
|
||||||
if (petCreature->GetVictim() != targetUnit ||
|
|
||||||
(petCreature->GetVictim() == targetUnit && !charmInfo->IsCommandAttack()))
|
|
||||||
{
|
|
||||||
if (petCreature->GetVictim())
|
|
||||||
petCreature->AttackStop();
|
|
||||||
|
|
||||||
if (!petCreature->IsPlayer() && petCreature->ToCreature()->IsAIEnabled)
|
|
||||||
{
|
|
||||||
// For AI-enabled creatures (NPC pets/guardians): issue attack command and set flags.
|
|
||||||
charmInfo->SetIsCommandAttack(true);
|
|
||||||
charmInfo->SetIsAtStay(false);
|
|
||||||
charmInfo->SetIsFollowing(false);
|
|
||||||
charmInfo->SetIsCommandFollow(false);
|
|
||||||
charmInfo->SetIsReturning(false);
|
|
||||||
|
|
||||||
petCreature->ToCreature()->AI()->AttackStart(targetUnit);
|
|
||||||
|
|
||||||
didAttack = true;
|
|
||||||
}
|
|
||||||
else // For charmed player pets/guardians
|
|
||||||
{
|
|
||||||
if (petCreature->GetVictim() && petCreature->GetVictim() != targetUnit)
|
|
||||||
petCreature->AttackStop();
|
|
||||||
|
|
||||||
charmInfo->SetIsCommandAttack(true);
|
|
||||||
charmInfo->SetIsAtStay(false);
|
|
||||||
charmInfo->SetIsFollowing(false);
|
|
||||||
charmInfo->SetIsCommandFollow(false);
|
|
||||||
charmInfo->SetIsReturning(false);
|
|
||||||
|
|
||||||
petCreature->Attack(targetUnit, true);
|
|
||||||
didAttack = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Inform the master if the command succeeded or failed.
|
|
||||||
if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
||||||
botAI->TellMaster("Pet commanded to attack your target.");
|
|
||||||
else if (!didAttack)
|
|
||||||
botAI->TellError("Pet did not attack. (Already attacking or unable to attack target)");
|
|
||||||
return didAttack;
|
|
||||||
}
|
}
|
||||||
// The "follow" command makes all pets/guardians follow the bot.
|
else if (mode == "rename" && !value.empty())
|
||||||
else if (param == "follow")
|
|
||||||
{
|
{
|
||||||
botAI->PetFollow();
|
found = RenamePet(value);
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
||||||
botAI->TellMaster("Pet commanded to follow.");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
// The "stay" command causes all pets/guardians to stop and stay in place.
|
|
||||||
else if (param == "stay")
|
|
||||||
{
|
|
||||||
for (Creature* target : targets)
|
|
||||||
{
|
|
||||||
// If not already in controlled motion, stop movement and set to idle.
|
|
||||||
bool controlledMotion =
|
|
||||||
target->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE;
|
|
||||||
if (!controlledMotion)
|
|
||||||
{
|
|
||||||
target->StopMovingOnCurrentPos();
|
|
||||||
target->GetMotionMaster()->Clear(false);
|
|
||||||
target->GetMotionMaster()->MoveIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
CharmInfo* charmInfo = target->GetCharmInfo();
|
|
||||||
if (charmInfo)
|
|
||||||
{
|
|
||||||
// Set charm/pet state flags for "stay".
|
|
||||||
charmInfo->SetCommandState(COMMAND_STAY);
|
|
||||||
charmInfo->SetIsCommandAttack(false);
|
|
||||||
charmInfo->SetIsCommandFollow(false);
|
|
||||||
charmInfo->SetIsFollowing(false);
|
|
||||||
charmInfo->SetIsReturning(false);
|
|
||||||
charmInfo->SetIsAtStay(!controlledMotion);
|
|
||||||
charmInfo->SaveStayPosition(controlledMotion);
|
|
||||||
if (target->ToPet())
|
|
||||||
target->ToPet()->ClearCastWhenWillAvailable();
|
|
||||||
|
|
||||||
charmInfo->SetForcedSpell(0);
|
|
||||||
charmInfo->SetForcedTargetGUID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
||||||
botAI->TellMaster("Pet commanded to stay.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Unknown command: show usage instructions and return.
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
botAI->TellError("Unknown pet command: " + param +
|
botAI->TellError("Usage: pet name <name> | pet id <id> | pet family <family> | pet rename <new name> ");
|
||||||
". Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For stance commands, apply the chosen stance to all targets.
|
if (!found)
|
||||||
for (Creature* target : targets)
|
return false;
|
||||||
{
|
|
||||||
target->SetReactState(react);
|
|
||||||
CharmInfo* charmInfo = target->GetCharmInfo();
|
|
||||||
if (charmInfo)
|
|
||||||
charmInfo->SetPlayerReactState(react);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inform the master of the new stance if debug is enabled.
|
// For non-rename commands, initialize pet and give feedback
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
if (mode != "rename")
|
||||||
botAI->TellMaster("Pet stance set to " + stanceText + ".");
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||||
|
factory.InitPet();
|
||||||
|
factory.InitPetTalents();
|
||||||
|
|
||||||
|
if (!lastPetName.empty() && lastPetId != 0)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << ".";
|
||||||
|
botAI->TellMaster(oss.str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
botAI->TellMaster("Pet changed and initialized!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PetAction::SetPetByName(const std::string& name)
|
||||||
|
{
|
||||||
|
// Convert the input to lowercase for case-insensitive comparison
|
||||||
|
std::string lowerName = name;
|
||||||
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||||
|
|
||||||
|
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||||
|
{
|
||||||
|
const CreatureTemplate& creature = itr->second;
|
||||||
|
std::string creatureName = creature.Name;
|
||||||
|
std::transform(creatureName.begin(), creatureName.end(), creatureName.begin(), ::tolower);
|
||||||
|
|
||||||
|
// Only match if names match (case-insensitive)
|
||||||
|
if (creatureName == lowerName)
|
||||||
|
{
|
||||||
|
// Check if the pet is tameable at all
|
||||||
|
if (!creature.IsTameable(true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Exotic pet check with talent requirement
|
||||||
|
if (IsExoticPet(&creature) && !HasBeastMastery(bot))
|
||||||
|
{
|
||||||
|
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final tameable check based on hunter's actual ability
|
||||||
|
if (!creature.IsTameable(bot->CanTameExoticPets()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lastPetName = creature.Name;
|
||||||
|
lastPetId = creature.Entry;
|
||||||
|
return CreateAndSetPet(creature.Entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
botAI->TellError("No tameable pet found with name: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PetAction::SetPetById(uint32 id)
|
||||||
|
{
|
||||||
|
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(id);
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
if (creature)
|
||||||
|
{
|
||||||
|
// Check if the pet is tameable at all
|
||||||
|
if (!creature->IsTameable(true))
|
||||||
|
{
|
||||||
|
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exotic pet check with talent requirement
|
||||||
|
if (IsExoticPet(creature) && !HasBeastMastery(bot))
|
||||||
|
{
|
||||||
|
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final tameable check based on hunter's actual ability
|
||||||
|
if (!creature->IsTameable(bot->CanTameExoticPets()))
|
||||||
|
{
|
||||||
|
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPetName = creature->Name;
|
||||||
|
lastPetId = creature->Entry;
|
||||||
|
return CreateAndSetPet(creature->Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PetAction::SetPetByFamily(const std::string& family)
|
||||||
|
{
|
||||||
|
std::string lowerFamily = family;
|
||||||
|
std::transform(lowerFamily.begin(), lowerFamily.end(), lowerFamily.begin(), ::tolower);
|
||||||
|
|
||||||
|
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
|
||||||
|
std::vector<const CreatureTemplate*> candidates;
|
||||||
|
bool foundExotic = false;
|
||||||
|
|
||||||
|
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||||
|
{
|
||||||
|
const CreatureTemplate& creature = itr->second;
|
||||||
|
|
||||||
|
if (!creature.IsTameable(true)) // allow exotics for search
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CreatureFamilyEntry const* familyEntry = sCreatureFamilyStore.LookupEntry(creature.family);
|
||||||
|
if (!familyEntry)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string familyName = familyEntry->Name[0];
|
||||||
|
std::transform(familyName.begin(), familyName.end(), familyName.begin(), ::tolower);
|
||||||
|
|
||||||
|
if (familyName != lowerFamily)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Exotic/BM check
|
||||||
|
if (IsExoticPet(&creature))
|
||||||
|
{
|
||||||
|
foundExotic = true;
|
||||||
|
if (!HasBeastMastery(bot))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!creature.IsTameable(bot->CanTameExoticPets()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidates.push_back(&creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.empty())
|
||||||
|
{
|
||||||
|
if (foundExotic && !HasBeastMastery(bot))
|
||||||
|
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
||||||
|
else
|
||||||
|
botAI->TellError("No tameable pet found with family: " + family);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomly select one from candidates
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 gen(rd());
|
||||||
|
std::uniform_int_distribution<> dis(0, candidates.size() - 1);
|
||||||
|
|
||||||
|
const CreatureTemplate* selected = candidates[dis(gen)];
|
||||||
|
|
||||||
|
lastPetName = selected->Name;
|
||||||
|
lastPetId = selected->Entry;
|
||||||
|
return CreateAndSetPet(selected->Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PetAction::RenamePet(const std::string& newName)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Pet* pet = bot->GetPet();
|
||||||
|
if (!pet)
|
||||||
|
{
|
||||||
|
botAI->TellError("You have no pet to rename.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length check (WoW max pet name is 12 characters)
|
||||||
|
if (newName.empty() || newName.length() > 12)
|
||||||
|
{
|
||||||
|
botAI->TellError("Pet name must be between 1 and 12 alphabetic characters.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alphabetic character check
|
||||||
|
for (char c : newName)
|
||||||
|
{
|
||||||
|
if (!std::isalpha(static_cast<unsigned char>(c)))
|
||||||
|
{
|
||||||
|
botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z).");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize case: capitalize first letter, lower the rest
|
||||||
|
std::string normalized = newName;
|
||||||
|
normalized[0] = std::toupper(normalized[0]);
|
||||||
|
for (size_t i = 1; i < normalized.size(); ++i)
|
||||||
|
normalized[i] = std::tolower(normalized[i]);
|
||||||
|
|
||||||
|
// Forbidden name check
|
||||||
|
if (sObjectMgr->IsReservedName(normalized))
|
||||||
|
{
|
||||||
|
botAI->TellError("That pet name is forbidden. Please choose another name.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the pet's name, save to DB, and send instant client update
|
||||||
|
pet->SetName(normalized);
|
||||||
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
|
bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry());
|
||||||
|
|
||||||
|
botAI->TellMaster("Your pet has been renamed to " + normalized + "!");
|
||||||
|
botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet.");
|
||||||
|
|
||||||
|
// Dismiss pet
|
||||||
|
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true);
|
||||||
|
// Recall pet using Hunter's Call Pet spell (spellId 883)
|
||||||
|
if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(883))
|
||||||
|
{
|
||||||
|
bot->CastSpell(bot, 883, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PetAction::CreateAndSetPet(uint32 creatureEntry)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
|
||||||
|
{
|
||||||
|
botAI->TellError("Only level 10+ hunters can have pets.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry);
|
||||||
|
if (!creature)
|
||||||
|
{
|
||||||
|
botAI->TellError("Creature template not found.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove current pet(s)
|
||||||
|
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
|
||||||
|
{
|
||||||
|
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
|
||||||
|
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
||||||
|
}
|
||||||
|
if (bot->GetPetStable() && bot->GetPetStable()->GetUnslottedHunterPet())
|
||||||
|
{
|
||||||
|
bot->GetPetStable()->UnslottedPets.clear();
|
||||||
|
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
|
||||||
|
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually create the new pet
|
||||||
|
Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0);
|
||||||
|
if (!pet)
|
||||||
|
{
|
||||||
|
botAI->TellError("Failed to create pet.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pet level and add to world
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel() - 1);
|
||||||
|
pet->GetMap()->AddToMap(pet->ToCreature());
|
||||||
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel());
|
||||||
|
bot->SetMinion(pet, true);
|
||||||
|
pet->InitTalentForLevel();
|
||||||
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
||||||
|
bot->PetSpellInitialize();
|
||||||
|
|
||||||
|
// Set stats
|
||||||
|
pet->InitStatsForLevel(bot->GetLevel());
|
||||||
|
pet->SetLevel(bot->GetLevel());
|
||||||
|
pet->SetPower(POWER_HAPPINESS, pet->GetMaxPower(Powers(POWER_HAPPINESS)));
|
||||||
|
pet->SetHealth(pet->GetMaxHealth());
|
||||||
|
|
||||||
|
// Enable autocast for active spells
|
||||||
|
for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
|
||||||
|
{
|
||||||
|
if (itr->second.state == PETSPELL_REMOVED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
|
||||||
|
if (!spellInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (spellInfo->IsPassive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pet->ToggleAutocast(spellInfo, true);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,20 +10,25 @@
|
|||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
#include "PlayerbotFactory.h"
|
#include "PlayerbotFactory.h"
|
||||||
#include "Unit.h"
|
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
class PetAction : public Action
|
class PetAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PetAction(PlayerbotAI* botAI, const std::string& defaultCmd = "") : Action(botAI, "pet"), defaultCmd(defaultCmd) {}
|
PetAction(PlayerbotAI* botAI) : Action(botAI, "pet") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool warningEnabled = true;
|
bool SetPetByName(const std::string& name);
|
||||||
std::string defaultCmd;
|
bool SetPetById(uint32 id);
|
||||||
};
|
bool SetPetByFamily(const std::string& family);
|
||||||
|
bool RenamePet(const std::string& newName);
|
||||||
|
|
||||||
|
bool CreateAndSetPet(uint32 creatureEntry);
|
||||||
|
|
||||||
|
std::string lastPetName;
|
||||||
|
uint32 lastPetId = 0;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "ItemUsageValue.h"
|
#include "ItemUsageValue.h"
|
||||||
#include "ItemVisitors.h"
|
#include "ItemVisitors.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
class SellItemsVisitor : public IterateItemsVisitor
|
class SellItemsVisitor : public IterateItemsVisitor
|
||||||
{
|
{
|
||||||
@@ -115,12 +114,9 @@ void SellAction::Sell(Item* item)
|
|||||||
|
|
||||||
uint32 botMoney = bot->GetMoney();
|
uint32 botMoney = bot->GetMoney();
|
||||||
|
|
||||||
WorldPacket p(CMSG_SELL_ITEM);
|
WorldPacket p;
|
||||||
p << vendorguid << itemguid << count;
|
p << vendorguid << itemguid << count;
|
||||||
|
bot->GetSession()->HandleSellItemOpcode(p);
|
||||||
WorldPackets::Item::SellItem nicePacket(std::move(p));
|
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleSellItemOpcode(nicePacket);
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::gold))
|
if (botAI->HasCheat(BotCheatMask::gold))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,500 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
|
||||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "TameAction.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <random>
|
|
||||||
#include <set>
|
|
||||||
#include <sstream>
|
|
||||||
#include "DBCStructure.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "ObjectMgr.h"
|
|
||||||
#include "Pet.h"
|
|
||||||
#include "Player.h"
|
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "PlayerbotFactory.h"
|
|
||||||
#include "SpellMgr.h"
|
|
||||||
#include "WorldSession.h"
|
|
||||||
|
|
||||||
bool IsExoticPet(const CreatureTemplate* creature)
|
|
||||||
{
|
|
||||||
// Use the IsExotic() method from CreatureTemplate
|
|
||||||
return creature && creature->IsExotic();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasBeastMastery(Player* bot)
|
|
||||||
{
|
|
||||||
// Beast Mastery talent aura ID for WotLK is 53270
|
|
||||||
return bot->HasAura(53270);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
// Parse the user's input command into mode and value (e.g. "name wolf", "id 1234", etc.)
|
|
||||||
std::string param = event.getParam();
|
|
||||||
std::istringstream iss(param);
|
|
||||||
std::string mode, value;
|
|
||||||
iss >> mode;
|
|
||||||
std::getline(iss, value);
|
|
||||||
value.erase(0, value.find_first_not_of(" ")); // Remove leading spaces from value
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
// Reset any previous pet name/id state
|
|
||||||
lastPetName = "";
|
|
||||||
lastPetId = 0;
|
|
||||||
|
|
||||||
// If the command is "family" with no value, list all available pet families
|
|
||||||
if (mode == "family" && value.empty())
|
|
||||||
{
|
|
||||||
std::set<std::string> normalFamilies;
|
|
||||||
std::set<std::string> exoticFamilies;
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
|
|
||||||
// Loop over all creature templates and collect tameable families
|
|
||||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
|
||||||
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
|
|
||||||
{
|
|
||||||
const CreatureTemplate& creature = itr->second;
|
|
||||||
if (!creature.IsTameable(true))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CreatureFamilyEntry const* familyEntry = sCreatureFamilyStore.LookupEntry(creature.family);
|
|
||||||
if (!familyEntry)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string familyName = familyEntry->Name[0];
|
|
||||||
if (familyName.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (creature.IsExotic())
|
|
||||||
exoticFamilies.insert(familyName);
|
|
||||||
else
|
|
||||||
normalFamilies.insert(familyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the output message for the user
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Available pet families: ";
|
|
||||||
size_t count = 0;
|
|
||||||
for (const auto& name : normalFamilies)
|
|
||||||
{
|
|
||||||
if (count++ != 0)
|
|
||||||
oss << ", ";
|
|
||||||
oss << name;
|
|
||||||
}
|
|
||||||
if (!exoticFamilies.empty())
|
|
||||||
{
|
|
||||||
if (!normalFamilies.empty())
|
|
||||||
oss << " | ";
|
|
||||||
oss << "Exotic: ";
|
|
||||||
count = 0;
|
|
||||||
for (const auto& name : exoticFamilies)
|
|
||||||
{
|
|
||||||
if (count++ != 0)
|
|
||||||
oss << ", ";
|
|
||||||
oss << name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
botAI->TellError(oss.str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle "tame abandon" command to give up your current pet
|
|
||||||
if (mode == "abandon")
|
|
||||||
{
|
|
||||||
return AbandonPet();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to process the command based on mode and value
|
|
||||||
if (mode == "name" && !value.empty())
|
|
||||||
{
|
|
||||||
found = SetPetByName(value);
|
|
||||||
}
|
|
||||||
else if (mode == "id" && !value.empty())
|
|
||||||
{
|
|
||||||
// Try to convert value to an integer and set pet by ID
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint32 id = std::stoul(value);
|
|
||||||
found = SetPetById(id);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
botAI->TellError("Invalid tame id.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mode == "family" && !value.empty())
|
|
||||||
{
|
|
||||||
found = SetPetByFamily(value);
|
|
||||||
}
|
|
||||||
else if (mode == "rename" && !value.empty())
|
|
||||||
{
|
|
||||||
found = RenamePet(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Unrecognized command or missing argument; show usage
|
|
||||||
botAI->TellError(
|
|
||||||
"Usage: tame name <name> | tame id <id> | tame family <family> | tame rename <new name> | tame abandon");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the requested tame/rename failed, return failure
|
|
||||||
if (!found)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// For all non-rename commands, initialize the new pet and talents, then notify the master
|
|
||||||
if (mode != "rename")
|
|
||||||
{
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
|
||||||
factory.InitPet();
|
|
||||||
factory.InitPetTalents();
|
|
||||||
|
|
||||||
if (!lastPetName.empty() && lastPetId != 0)
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << ".";
|
|
||||||
botAI->TellMaster(oss.str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
botAI->TellMaster("Pet changed and initialized!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::SetPetByName(const std::string& name)
|
|
||||||
{
|
|
||||||
// Make a lowercase copy of the input name for case-insensitive comparison
|
|
||||||
std::string lowerName = name;
|
|
||||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
|
||||||
|
|
||||||
// Get the full list of creature templates from the object manager
|
|
||||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
|
|
||||||
// Iterate through all creature templates
|
|
||||||
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
|
|
||||||
{
|
|
||||||
const CreatureTemplate& creature = itr->second;
|
|
||||||
std::string creatureName = creature.Name;
|
|
||||||
// Convert creature's name to lowercase for comparison
|
|
||||||
std::transform(creatureName.begin(), creatureName.end(), creatureName.begin(), ::tolower);
|
|
||||||
|
|
||||||
// If the input name matches this creature's name
|
|
||||||
if (creatureName == lowerName)
|
|
||||||
{
|
|
||||||
// Skip if the creature isn't tameable at all
|
|
||||||
if (!creature.IsTameable(true))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If the creature is exotic and the bot doesn't have Beast Mastery, show error and fail
|
|
||||||
if (IsExoticPet(&creature) && !HasBeastMastery(bot))
|
|
||||||
{
|
|
||||||
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if the creature isn't tameable by this bot (respecting exotic pet rules)
|
|
||||||
if (!creature.IsTameable(bot->CanTameExoticPets()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Store the found pet's name and entry ID for later use/feedback
|
|
||||||
lastPetName = creature.Name;
|
|
||||||
lastPetId = creature.Entry;
|
|
||||||
// Create and set this pet for the bot
|
|
||||||
return CreateAndSetPet(creature.Entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no suitable pet found, show an error and return failure
|
|
||||||
botAI->TellError("No tameable pet found with name: " + name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::SetPetById(uint32 id)
|
|
||||||
{
|
|
||||||
// Look up the creature template by its numeric entry/id
|
|
||||||
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(id);
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
|
|
||||||
// Proceed only if a valid creature was found
|
|
||||||
if (creature)
|
|
||||||
{
|
|
||||||
// Check if this creature is ever tameable (ignore bot's own restrictions for now)
|
|
||||||
if (!creature->IsTameable(true))
|
|
||||||
{
|
|
||||||
// If not tameable at all, show an error and fail
|
|
||||||
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's an exotic pet, make sure the bot has the Beast Mastery talent
|
|
||||||
if (IsExoticPet(creature) && !HasBeastMastery(bot))
|
|
||||||
{
|
|
||||||
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the bot is actually allowed to tame this pet (honoring exotic pet rules)
|
|
||||||
if (!creature->IsTameable(bot->CanTameExoticPets()))
|
|
||||||
{
|
|
||||||
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember this pet's name and id for later feedback
|
|
||||||
lastPetName = creature->Name;
|
|
||||||
lastPetId = creature->Entry;
|
|
||||||
// Set and create the pet for the bot
|
|
||||||
return CreateAndSetPet(creature->Entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no valid creature was found by id, show an error
|
|
||||||
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::SetPetByFamily(const std::string& family)
|
|
||||||
{
|
|
||||||
// Convert the input family name to lowercase for case-insensitive comparison
|
|
||||||
std::string lowerFamily = family;
|
|
||||||
std::transform(lowerFamily.begin(), lowerFamily.end(), lowerFamily.begin(), ::tolower);
|
|
||||||
|
|
||||||
// Get all creature templates from the object manager
|
|
||||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
|
|
||||||
// Prepare a list of candidate creatures and track if any exotic pet is found
|
|
||||||
std::vector<const CreatureTemplate*> candidates;
|
|
||||||
bool foundExotic = false;
|
|
||||||
|
|
||||||
// Iterate through all creature templates
|
|
||||||
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
|
|
||||||
{
|
|
||||||
const CreatureTemplate& creature = itr->second;
|
|
||||||
|
|
||||||
// Skip if this creature is never tameable
|
|
||||||
if (!creature.IsTameable(true))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Look up the family entry for this creature
|
|
||||||
CreatureFamilyEntry const* familyEntry = sCreatureFamilyStore.LookupEntry(creature.family);
|
|
||||||
if (!familyEntry)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Compare the family name in a case-insensitive way
|
|
||||||
std::string familyName = familyEntry->Name[0];
|
|
||||||
std::transform(familyName.begin(), familyName.end(), familyName.begin(), ::tolower);
|
|
||||||
|
|
||||||
if (familyName != lowerFamily)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If the creature is exotic, check Beast Mastery talent requirements
|
|
||||||
if (IsExoticPet(&creature))
|
|
||||||
{
|
|
||||||
foundExotic = true;
|
|
||||||
if (!HasBeastMastery(bot))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add as candidate if this bot is allowed to tame it (including exotic rules)
|
|
||||||
if (!creature.IsTameable(bot->CanTameExoticPets()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
candidates.push_back(&creature);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no candidates found, inform the user of the reason and return false
|
|
||||||
if (candidates.empty())
|
|
||||||
{
|
|
||||||
if (foundExotic && !HasBeastMastery(bot))
|
|
||||||
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
|
|
||||||
else
|
|
||||||
botAI->TellError("No tameable pet found with family: " + family);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomly select one candidate from the list to tame
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 gen(rd());
|
|
||||||
std::uniform_int_distribution<> dis(0, candidates.size() - 1);
|
|
||||||
|
|
||||||
const CreatureTemplate* selected = candidates[dis(gen)];
|
|
||||||
|
|
||||||
// Save the selected pet's name and id for feedback
|
|
||||||
lastPetName = selected->Name;
|
|
||||||
lastPetId = selected->Entry;
|
|
||||||
// Attempt to create and set the new pet for the bot
|
|
||||||
return CreateAndSetPet(selected->Entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::RenamePet(const std::string& newName)
|
|
||||||
{
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
// Check if the bot currently has a pet
|
|
||||||
if (!pet)
|
|
||||||
{
|
|
||||||
botAI->TellError("You have no pet to rename.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the new name: must not be empty and max 12 characters
|
|
||||||
if (newName.empty() || newName.length() > 12)
|
|
||||||
{
|
|
||||||
botAI->TellError("Pet name must be between 1 and 12 alphabetic characters.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure all characters in the new name are alphabetic
|
|
||||||
for (char c : newName)
|
|
||||||
{
|
|
||||||
if (!std::isalpha(static_cast<unsigned char>(c)))
|
|
||||||
{
|
|
||||||
botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z).");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the name: capitalize the first letter, lowercase the rest
|
|
||||||
std::string normalized = newName;
|
|
||||||
normalized[0] = std::toupper(normalized[0]);
|
|
||||||
for (size_t i = 1; i < normalized.size(); ++i)
|
|
||||||
normalized[i] = std::tolower(normalized[i]);
|
|
||||||
|
|
||||||
// Check if the new name is reserved or forbidden
|
|
||||||
if (sObjectMgr->IsReservedName(normalized))
|
|
||||||
{
|
|
||||||
botAI->TellError("That pet name is forbidden. Please choose another name.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the pet's name and save it to the database
|
|
||||||
pet->SetName(normalized);
|
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
||||||
bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry());
|
|
||||||
|
|
||||||
// Notify the master about the rename and give a tip to update the client name display
|
|
||||||
botAI->TellMaster("Your pet has been renamed to " + normalized + "!");
|
|
||||||
botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet.");
|
|
||||||
|
|
||||||
// Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter
|
|
||||||
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true);
|
|
||||||
if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(883))
|
|
||||||
{
|
|
||||||
bot->CastSpell(bot, 883, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::CreateAndSetPet(uint32 creatureEntry)
|
|
||||||
{
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
// Ensure the player is a hunter and at least level 10 (required for pets)
|
|
||||||
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
|
|
||||||
{
|
|
||||||
botAI->TellError("Only level 10+ hunters can have pets.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the creature template for the given entry (pet species info)
|
|
||||||
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry);
|
|
||||||
if (!creature)
|
|
||||||
{
|
|
||||||
botAI->TellError("Creature template not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the bot already has a current pet or an unslotted pet, remove them to avoid conflicts
|
|
||||||
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
|
|
||||||
{
|
|
||||||
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
|
|
||||||
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
|
||||||
}
|
|
||||||
if (bot->GetPetStable() && bot->GetPetStable()->GetUnslottedHunterPet())
|
|
||||||
{
|
|
||||||
bot->GetPetStable()->UnslottedPets.clear();
|
|
||||||
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
|
|
||||||
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new tamed pet from the specified creature entry
|
|
||||||
Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0);
|
|
||||||
if (!pet)
|
|
||||||
{
|
|
||||||
botAI->TellError("Failed to create pet.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the pet's level to one below the bot's current level, then add to the map and set to full level
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel() - 1);
|
|
||||||
pet->GetMap()->AddToMap(pet->ToCreature());
|
|
||||||
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel());
|
|
||||||
// Set the pet as the bot's active minion
|
|
||||||
bot->SetMinion(pet, true);
|
|
||||||
// Initialize talents appropriate for the pet's level
|
|
||||||
pet->InitTalentForLevel();
|
|
||||||
// Save pet to the database as the current pet
|
|
||||||
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
||||||
// Initialize available pet spells
|
|
||||||
bot->PetSpellInitialize();
|
|
||||||
|
|
||||||
// Further initialize pet stats to match the bot's level
|
|
||||||
pet->InitStatsForLevel(bot->GetLevel());
|
|
||||||
pet->SetLevel(bot->GetLevel());
|
|
||||||
// Set happiness and health of the pet to maximum values
|
|
||||||
pet->SetPower(POWER_HAPPINESS, pet->GetMaxPower(Powers(POWER_HAPPINESS)));
|
|
||||||
pet->SetHealth(pet->GetMaxHealth());
|
|
||||||
|
|
||||||
// Enable autocast for all active (not removed) non-passive spells the pet knows
|
|
||||||
for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
|
|
||||||
{
|
|
||||||
if (itr->second.state == PETSPELL_REMOVED)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
|
|
||||||
if (!spellInfo)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (spellInfo->IsPassive())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
pet->ToggleAutocast(spellInfo, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TameAction::AbandonPet()
|
|
||||||
{
|
|
||||||
// Get the bot player and its current pet (if any)
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
|
|
||||||
// Check if the bot has a pet and that it is a hunter pet
|
|
||||||
if (pet && pet->getPetType() == HUNTER_PET)
|
|
||||||
{
|
|
||||||
// Remove the pet from the bot and mark it as deleted in the database
|
|
||||||
bot->RemovePet(pet, PET_SAVE_AS_DELETED);
|
|
||||||
// Inform the bot's master/player that the pet was abandoned
|
|
||||||
botAI->TellMaster("Your pet has been abandoned.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If there is no hunter pet, show an error message
|
|
||||||
botAI->TellError("You have no hunter pet to abandon.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
|
||||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _PLAYERBOT_TAMEACTION_H
|
|
||||||
#define _PLAYERBOT_TAMEACTION_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "Action.h"
|
|
||||||
#include "PlayerbotFactory.h"
|
|
||||||
|
|
||||||
class PlayerbotAI;
|
|
||||||
|
|
||||||
class TameAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TameAction(PlayerbotAI* botAI) : Action(botAI, "tame") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool SetPetByName(const std::string& name);
|
|
||||||
bool SetPetById(uint32 id);
|
|
||||||
bool SetPetByFamily(const std::string& family);
|
|
||||||
bool RenamePet(const std::string& newName);
|
|
||||||
bool CreateAndSetPet(uint32 creatureEntry);
|
|
||||||
bool AbandonPet();
|
|
||||||
|
|
||||||
std::string lastPetName;
|
|
||||||
uint32 lastPetId = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemCountValue.h"
|
#include "ItemCountValue.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "WorldSession.h"
|
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
std::vector<std::string> split(std::string const s, char delim);
|
std::vector<std::string> split(std::string const s, char delim);
|
||||||
|
|
||||||
@@ -72,9 +70,7 @@ void UnequipAction::UnequipItem(Item* item)
|
|||||||
|
|
||||||
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
||||||
packet << bagIndex << slot << dstBag;
|
packet << bagIndex << slot << dstBag;
|
||||||
WorldPackets::Item::AutoStoreBagItem nicePacket(std::move(packet));
|
bot->GetSession()->HandleAutoStoreBagItemOpcode(packet);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleAutoStoreBagItemOpcode(nicePacket);
|
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << chat->FormatItem(item->GetTemplate()) << " unequipped";
|
out << chat->FormatItem(item->GetTemplate()) << " unequipped";
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemUsageValue.h"
|
#include "ItemUsageValue.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ItemPackets.h"
|
|
||||||
|
|
||||||
bool UseItemAction::Execute(Event event)
|
bool UseItemAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@@ -325,8 +324,8 @@ void UseItemAction::TellConsumableUse(Item* item, std::string const action, floa
|
|||||||
|
|
||||||
bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
||||||
{
|
{
|
||||||
WorldPacket packet(CMSG_SOCKET_GEMS);
|
WorldPacket* const packet = new WorldPacket(CMSG_SOCKET_GEMS);
|
||||||
packet << item->GetGUID();
|
*packet << item->GetGUID();
|
||||||
|
|
||||||
bool fits = false;
|
bool fits = false;
|
||||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS;
|
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS;
|
||||||
@@ -338,14 +337,14 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
|||||||
{
|
{
|
||||||
if (fits)
|
if (fits)
|
||||||
{
|
{
|
||||||
packet << ObjectGuid::Empty;
|
*packet << ObjectGuid::Empty;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(enchant_slot));
|
uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(enchant_slot));
|
||||||
if (!enchant_id)
|
if (!enchant_id)
|
||||||
{
|
{
|
||||||
packet << gem->GetGUID();
|
*packet << gem->GetGUID();
|
||||||
fits = true;
|
fits = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -353,20 +352,20 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
|||||||
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
|
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
|
||||||
if (!enchantEntry || !enchantEntry->GemID)
|
if (!enchantEntry || !enchantEntry->GemID)
|
||||||
{
|
{
|
||||||
packet << gem->GetGUID();
|
*packet << gem->GetGUID();
|
||||||
fits = true;
|
fits = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replace && enchantEntry->GemID != gem->GetTemplate()->ItemId)
|
if (replace && enchantEntry->GemID != gem->GetTemplate()->ItemId)
|
||||||
{
|
{
|
||||||
packet << gem->GetGUID();
|
*packet << gem->GetGUID();
|
||||||
fits = true;
|
fits = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packet << ObjectGuid::Empty;
|
*packet << ObjectGuid::Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fits)
|
if (fits)
|
||||||
@@ -376,9 +375,7 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
|||||||
out << " with " << chat->FormatItem(gem->GetTemplate());
|
out << " with " << chat->FormatItem(gem->GetTemplate());
|
||||||
botAI->TellMaster(out);
|
botAI->TellMaster(out);
|
||||||
|
|
||||||
WorldPackets::Item::SocketGems nicePacket(std::move(packet));
|
bot->GetSession()->HandleSocketOpcode(*packet);
|
||||||
nicePacket.Read();
|
|
||||||
bot->GetSession()->HandleSocketOpcode(nicePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fits;
|
return fits;
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
#include "UseMeetingStoneAction.h"
|
#include "UseMeetingStoneAction.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "ReleaseSpiritAction.h"
|
#include "ReleaseSpiritAction.h"
|
||||||
#include "PetAction.h"
|
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@@ -71,7 +70,6 @@ public:
|
|||||||
creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended;
|
creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended;
|
||||||
creators["store loot"] = &WorldPacketActionContext::store_loot;
|
creators["store loot"] = &WorldPacketActionContext::store_loot;
|
||||||
creators["self resurrect"] = &WorldPacketActionContext::self_resurrect;
|
creators["self resurrect"] = &WorldPacketActionContext::self_resurrect;
|
||||||
creators["pet"] = &WorldPacketActionContext::pet;
|
|
||||||
|
|
||||||
// quest
|
// quest
|
||||||
creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest;
|
creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest;
|
||||||
@@ -141,7 +139,6 @@ private:
|
|||||||
static Action* tell_not_enough_reputation(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough reputation"); }
|
static Action* tell_not_enough_reputation(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough reputation"); }
|
||||||
static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); }
|
static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); }
|
||||||
static Action* self_resurrect(PlayerbotAI* botAI) { return new SelfResurrectAction(botAI); }
|
static Action* self_resurrect(PlayerbotAI* botAI) { return new SelfResurrectAction(botAI); }
|
||||||
static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); }
|
|
||||||
|
|
||||||
// quest
|
// quest
|
||||||
static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); }
|
static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); }
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", 21.0f), nullptr)));
|
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", 21.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), NULL)));
|
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 11.0f), NULL)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), NULL)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DKBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void DKBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
|||||||
@@ -171,10 +171,6 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
// NextAction::array(0, new NextAction("anti magic zone", ACTION_EMERGENCY + 1), nullptr)));
|
// NextAction::array(0, new NextAction("anti magic zone", ACTION_EMERGENCY + 1), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 5), nullptr)));
|
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 5), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("mind freeze", NextAction::array(0, new NextAction("mind freeze", ACTION_HIGH + 1), nullptr)));
|
new TriggerNode("mind freeze", NextAction::array(0, new NextAction("mind freeze", ACTION_HIGH + 1), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
|
|||||||
@@ -119,42 +119,6 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
|
|||||||
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY
|
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY
|
||||||
// + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic
|
// + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic
|
||||||
// form", 1.0f), nullptr)));
|
// form", 1.0f), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("party member critical health",
|
|
||||||
NextAction::array(0,
|
|
||||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
|
||||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
|
||||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
|
|
||||||
nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("party member low health",
|
|
||||||
NextAction::array(0,
|
|
||||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
|
|
||||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
|
|
||||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
|
|
||||||
nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("party member medium health",
|
|
||||||
NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
|
|
||||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
|
|
||||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
|
|
||||||
nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("party member almost full health",
|
|
||||||
NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL)));
|
|
||||||
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("party member remove curse",
|
|
||||||
NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0,
|
triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0,
|
||||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
||||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
||||||
@@ -183,7 +147,6 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
|
|||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
||||||
if (specTab == 1) // Feral
|
if (specTab == 1) // Feral
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
||||||
|
|||||||
@@ -122,8 +122,7 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode("combat party member dead",
|
triggers.push_back(new TriggerNode("combat party member dead",
|
||||||
NextAction::array(0, new NextAction("rebirth", ACTION_HIGH + 9), NULL)));
|
NextAction::array(0, new NextAction("rebirth", ACTION_HIGH + 9), NULL)));
|
||||||
triggers.push_back(new TriggerNode("being attacked",
|
triggers.push_back(new TriggerNode("being attacked",
|
||||||
NextAction::array(0, new NextAction("nature's grasp", ACTION_HIGH + 1), nullptr)));
|
NextAction::array(0, new NextAction("nature's grasp", ACTION_HIGH + 1), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ bool MountDrakeAction::Execute(Event event)
|
|||||||
for (auto& member : members)
|
for (auto& member : members)
|
||||||
{
|
{
|
||||||
Player* player = botAI->GetPlayer(member);
|
Player* player = botAI->GetPlayer(member);
|
||||||
if (!player->GetSession()->IsBot()) { continue; }
|
if (!player) { continue; }
|
||||||
|
|
||||||
for (int i = 0; i < composition.size(); i++)
|
for (int i = 0; i < composition.size(); i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -102,11 +102,9 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
|
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr)));
|
new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("tame", NextAction::array(0, new NextAction("tame", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs
|
triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs
|
||||||
triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs
|
triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs
|
||||||
triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", relevance), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,9 +185,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("qi");
|
supported.push_back("qi");
|
||||||
supported.push_back("unlock items");
|
supported.push_back("unlock items");
|
||||||
supported.push_back("unlock traded item");
|
supported.push_back("unlock traded item");
|
||||||
supported.push_back("tame");
|
supported.push_back("pet");
|
||||||
supported.push_back("glyphs"); // Added for custom Glyphs
|
supported.push_back("glyphs"); // Added for custom Glyphs
|
||||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||||
supported.push_back("pet");
|
|
||||||
supported.push_back("pet attack");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode("combat stuck", NextAction::array(0, new NextAction("reset", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("combat stuck", NextAction::array(0, new NextAction("reset", 1.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("not facing target",
|
triggers.push_back(new TriggerNode("not facing target",
|
||||||
NextAction::array(0, new NextAction("set facing", ACTION_MOVE + 7), nullptr)));
|
NextAction::array(0, new NextAction("set facing", ACTION_MOVE + 7), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", 40.0f), nullptr)));
|
triggers.push_back(
|
||||||
// The pet-attack trigger is commented out because it was forcing the bot's pet to attack, overriding stay and follow commands.
|
new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", 40.0f), nullptr)));
|
||||||
// Pets will automatically attack the bot's enemy if they are in "defensive" or "aggressive"
|
|
||||||
// stance, or if the master issues an attack command.
|
|
||||||
// triggers.push_back(new TriggerNode("combat long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f),
|
// triggers.push_back(new TriggerNode("combat long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f),
|
||||||
// new NextAction("repop", 0.8f), nullptr)));
|
// new NextAction("repop", 0.8f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,13 @@
|
|||||||
void UseFoodStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void UseFoodStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
Strategy::InitTriggers(triggers);
|
Strategy::InitTriggers(triggers);
|
||||||
if (botAI->HasCheat(BotCheatMask::food))
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
{
|
|
||||||
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr)));
|
||||||
|
|
||||||
|
if (sPlayerbotAIConfig->freeFood)
|
||||||
|
triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
||||||
|
else
|
||||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ void HunterPetStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr)));
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ bool CastArcaneShotAction::isUseful()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Armor Penetration rating check - will not cast Arcane Shot above 435 ArP
|
// Armor Penetration rating check - will not cast Arcane Shot above 435 ArP
|
||||||
int32 armorPenRating = bot->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1) + bot->GetUInt32Value(CR_ARMOR_PENETRATION);
|
int32 armorPenRating = bot->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_ARMOR_PENETRATION);
|
||||||
if (armorPenRating > 435)
|
if (armorPenRating > 435)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
// Pet/Defensive triggers
|
// Pet/Defensive triggers
|
||||||
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon water elemental", 30.0f), nullptr)));
|
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon water elemental", 30.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 29.5f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
triggers.push_back(new TriggerNode("being attacked", NextAction::array(0, new NextAction("ice barrier", 29.0f), nullptr)));
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ void GenericPriestStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("being attacked",
|
triggers.push_back(new TriggerNode("being attacked",
|
||||||
NextAction::array(0, new NextAction("power word: shield", ACTION_HIGH + 1), nullptr)));
|
NextAction::array(0, new NextAction("power word: shield", ACTION_HIGH + 1), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PriestCureStrategy::PriestCureStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
PriestCureStrategy::PriestCureStrategy(PlayerbotAI* botAI) : Strategy(botAI)
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ void PriestNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("group heal setting", NextAction::array(0, new NextAction("circle of healing on party", 27.0f), NULL)));
|
new TriggerNode("group heal setting", NextAction::array(0, new NextAction("circle of healing on party", 27.0f), NULL)));
|
||||||
triggers.push_back(new TriggerNode("new pet",
|
|
||||||
NextAction::array(0, new NextAction("set pet stance", 10.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PriestBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PriestBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
|||||||
@@ -65,24 +65,6 @@ public:
|
|||||||
creators["vezax cheat action"] = &RaidUlduarActionContext::vezax_cheat_action;
|
creators["vezax cheat action"] = &RaidUlduarActionContext::vezax_cheat_action;
|
||||||
creators["vezax shadow crash action"] = &RaidUlduarActionContext::vezax_shadow_crash_action;
|
creators["vezax shadow crash action"] = &RaidUlduarActionContext::vezax_shadow_crash_action;
|
||||||
creators["vezax mark of the faceless action"] = &RaidUlduarActionContext::vezax_mark_of_the_faceless_action;
|
creators["vezax mark of the faceless action"] = &RaidUlduarActionContext::vezax_mark_of_the_faceless_action;
|
||||||
creators["vezax shadow resistance action"] = &RaidUlduarActionContext::vezax_shadow_resistance_action;
|
|
||||||
creators["sara shadow resistance action"] = &RaidUlduarActionContext::sara_shadow_resistance_action;
|
|
||||||
creators["yogg-saron shadow resistance action"] = &RaidUlduarActionContext::yogg_saron_shadow_resistance_action;
|
|
||||||
creators["yogg-saron ominous cloud cheat action"] = &RaidUlduarActionContext::yogg_saron_ominous_cloud_cheat_action;
|
|
||||||
creators["yogg-saron guardian positioning action"] = &RaidUlduarActionContext::yogg_saron_guardian_positioning_action;
|
|
||||||
creators["yogg-saron sanity action"] = &RaidUlduarActionContext::yogg_saron_sanity_action;
|
|
||||||
creators["yogg-saron death orb action"] = &RaidUlduarActionContext::yogg_saron_death_orb_action;
|
|
||||||
creators["yogg-saron malady of the mind action"] = &RaidUlduarActionContext::yogg_saron_malady_of_the_mind_action;
|
|
||||||
creators["yogg-saron mark target action"] = &RaidUlduarActionContext::yogg_saron_mark_target_action;
|
|
||||||
creators["yogg-saron brain link action"] = &RaidUlduarActionContext::yogg_saron_brain_link_action;
|
|
||||||
creators["yogg-saron move to enter portal action"] = &RaidUlduarActionContext::yogg_saron_move_to_enter_portal_action;
|
|
||||||
creators["yogg-saron use portal action"] = &RaidUlduarActionContext::yogg_saron_use_portal_action;
|
|
||||||
creators["yogg-saron fall from floor action"] = &RaidUlduarActionContext::yogg_saron_fall_from_floor_action;
|
|
||||||
creators["yogg-saron boss room movement cheat action"] = &RaidUlduarActionContext::yogg_saron_boss_room_movement_cheat_action;
|
|
||||||
creators["yogg-saron illusion room action"] = &RaidUlduarActionContext::yogg_saron_illusion_room_action;
|
|
||||||
creators["yogg-saron move to exit portal action"] = &RaidUlduarActionContext::yogg_saron_move_to_exit_portal_action;
|
|
||||||
creators["yogg-saron lunatic gaze action"] = &RaidUlduarActionContext::yogg_saron_lunatic_gaze_action;
|
|
||||||
creators["yogg-saron phase 3 positioning action"] = &RaidUlduarActionContext::yogg_saron_phase_3_positioning_action;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -135,24 +117,6 @@ private:
|
|||||||
static Action* vezax_cheat_action(PlayerbotAI* ai) { return new VezaxCheatAction(ai); }
|
static Action* vezax_cheat_action(PlayerbotAI* ai) { return new VezaxCheatAction(ai); }
|
||||||
static Action* vezax_shadow_crash_action(PlayerbotAI* ai) { return new VezaxShadowCrashAction(ai); }
|
static Action* vezax_shadow_crash_action(PlayerbotAI* ai) { return new VezaxShadowCrashAction(ai); }
|
||||||
static Action* vezax_mark_of_the_faceless_action(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessAction(ai); }
|
static Action* vezax_mark_of_the_faceless_action(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessAction(ai); }
|
||||||
static Action* vezax_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "general vezax"); }
|
|
||||||
static Action* sara_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "sara"); }
|
|
||||||
static Action* yogg_saron_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "yogg-saron"); }
|
|
||||||
static Action* yogg_saron_ominous_cloud_cheat_action(PlayerbotAI* ai) { return new YoggSaronOminousCloudCheatAction(ai); }
|
|
||||||
static Action* yogg_saron_guardian_positioning_action(PlayerbotAI* ai) { return new YoggSaronGuardianPositioningAction(ai); }
|
|
||||||
static Action* yogg_saron_sanity_action(PlayerbotAI* ai) { return new YoggSaronSanityAction(ai); }
|
|
||||||
static Action* yogg_saron_death_orb_action(PlayerbotAI* ai) { return new YoggSaronDeathOrbAction(ai); }
|
|
||||||
static Action* yogg_saron_malady_of_the_mind_action(PlayerbotAI* ai) { return new YoggSaronMaladyOfTheMindAction(ai); }
|
|
||||||
static Action* yogg_saron_mark_target_action(PlayerbotAI* ai) { return new YoggSaronMarkTargetAction(ai); }
|
|
||||||
static Action* yogg_saron_brain_link_action(PlayerbotAI* ai) { return new YoggSaronBrainLinkAction(ai); }
|
|
||||||
static Action* yogg_saron_move_to_enter_portal_action(PlayerbotAI* ai) { return new YoggSaronMoveToEnterPortalAction(ai); }
|
|
||||||
static Action* yogg_saron_use_portal_action(PlayerbotAI* ai) { return new YoggSaronUsePortalAction(ai); }
|
|
||||||
static Action* yogg_saron_fall_from_floor_action(PlayerbotAI* ai) { return new YoggSaronFallFromFloorAction(ai); }
|
|
||||||
static Action* yogg_saron_boss_room_movement_cheat_action(PlayerbotAI* ai) { return new YoggSaronBossRoomMovementCheatAction(ai); }
|
|
||||||
static Action* yogg_saron_illusion_room_action(PlayerbotAI* ai) { return new YoggSaronIllusionRoomAction(ai); }
|
|
||||||
static Action* yogg_saron_move_to_exit_portal_action(PlayerbotAI* ai) { return new YoggSaronMoveToExitPortalAction(ai); }
|
|
||||||
static Action* yogg_saron_lunatic_gaze_action(PlayerbotAI* ai) { return new YoggSaronLunaticGazeAction(ai); }
|
|
||||||
static Action* yogg_saron_phase_3_positioning_action(PlayerbotAI* ai) { return new YoggSaronPhase3PositioningAction(ai); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,14 +20,13 @@
|
|||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "RaidUlduarScripts.h"
|
#include "RaidUlduarScripts.h"
|
||||||
#include "RaidUlduarStrategy.h"
|
#include "RaidUlduarStrategy.h"
|
||||||
|
#include "RaidUlduarTriggers.h"
|
||||||
#include "RtiValue.h"
|
#include "RtiValue.h"
|
||||||
#include "ScriptedCreature.h"
|
#include "ScriptedCreature.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include <RtiTargetValue.h>
|
|
||||||
#include <TankAssistStrategy.h>
|
|
||||||
|
|
||||||
const std::string ADD_STRATEGY_CHAR = "+";
|
const std::string ADD_STRATEGY_CHAR = "+";
|
||||||
const std::string REMOVE_STRATEGY_CHAR = "-";
|
const std::string REMOVE_STRATEGY_CHAR = "-";
|
||||||
@@ -43,12 +42,6 @@ const Position ULDUAR_KOLOGARN_RESTORE_POSITION = Position(1764.3749f, -24.02903
|
|||||||
const Position ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION = Position(1781.2051f, 9.34402f, 449.0f, 0.00087690353f);
|
const Position ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION = Position(1781.2051f, 9.34402f, 449.0f, 0.00087690353f);
|
||||||
const Position ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION = Position(1763.2561f, -24.44305f, 449.0f, 0.00087690353f);
|
const Position ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION = Position(1763.2561f, -24.44305f, 449.0f, 0.00087690353f);
|
||||||
const Position ULDUAR_THORIM_JUMP_START_POINT = Position(2137.137f, -291.19025f, 438.24753f, 1.7059844f);
|
const Position ULDUAR_THORIM_JUMP_START_POINT = Position(2137.137f, -291.19025f, 438.24753f, 1.7059844f);
|
||||||
const Position ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT = Position(1928.8923f, -24.871964f, 324.88956f, 6.247805f);
|
|
||||||
|
|
||||||
const Position yoggPortalLoc[] = {
|
|
||||||
{1970.48f, -9.75f, 325.5f}, {1992.76f, -10.21f, 325.5f}, {1995.53f, -39.78f, 325.5f}, {1969.25f, -42.00f, 325.5f},
|
|
||||||
{1960.62f, -32.00f, 325.5f}, {1981.98f, -5.69f, 325.5f}, {1982.78f, -45.73f, 325.5f}, {2000.66f, -29.68f, 325.5f},
|
|
||||||
{1999.88f, -19.61f, 325.5f}, {1961.37f, -19.54f, 325.5f}};
|
|
||||||
|
|
||||||
bool FlameLeviathanVehicleAction::Execute(Event event)
|
bool FlameLeviathanVehicleAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@@ -1824,24 +1817,24 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
|
|||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
|
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
|
||||||
Unit* currentMoonUnit = botAI->GetUnit(currentMoonTarget);
|
Unit* currentMoonUnit = botAI->GetUnit(currentMoonTarget);
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||||
if (!currentMoonUnit && boss && boss->IsAlive() && boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
if (!currentMoonUnit && boss && boss->IsAlive() && boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::moonIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentMoonUnit && boss && currentMoonUnit->GetEntry() == boss->GetEntry() &&
|
if (currentMoonUnit && boss && currentMoonUnit->GetEntry() == boss->GetEntry() &&
|
||||||
boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (botAI->IsMainTank(bot))
|
if (botAI->IsMainTank(bot))
|
||||||
{
|
{
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
||||||
if (currentSkullUnit && !currentSkullUnit->IsAlive())
|
if (currentSkullUnit && !currentSkullUnit->IsAlive())
|
||||||
{
|
{
|
||||||
@@ -1862,7 +1855,7 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (botAI->IsAssistTankOfIndex(bot, 0))
|
else if (botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
{
|
{
|
||||||
ObjectGuid currentCrossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
|
ObjectGuid currentCrossTarget = group->GetTargetIcon(crossIndex);
|
||||||
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
|
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
|
||||||
if (currentCrossUnit && !currentCrossUnit->IsAlive())
|
if (currentCrossUnit && !currentCrossUnit->IsAlive())
|
||||||
{
|
{
|
||||||
@@ -1898,13 +1891,13 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
|
|||||||
|
|
||||||
if (botAI->IsMainTank(bot))
|
if (botAI->IsMainTank(bot))
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), targetToMark->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (botAI->IsAssistTankOfIndex(bot, 0))
|
if (botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), targetToMark->GetGUID());
|
group->SetTargetIcon(crossIndex, bot->GetGUID(), targetToMark->GetGUID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2445,20 +2438,20 @@ bool MimironAerialCommandUnitAction::Execute(Event event)
|
|||||||
|
|
||||||
if (bombBot)
|
if (bombBot)
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), bombBot->GetGUID());
|
group->SetTargetIcon(crossIndex, bot->GetGUID(), bombBot->GetGUID());
|
||||||
}
|
}
|
||||||
else if (boss)
|
else if (boss)
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(crossIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assaultBot)
|
if (assaultBot)
|
||||||
{
|
{
|
||||||
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||||
Unit* skullUnit = botAI->GetUnit(skullTarget);
|
Unit* skullUnit = botAI->GetUnit(skullTarget);
|
||||||
if (!skullTarget || !skullUnit || !skullUnit->IsAlive())
|
if (!skullTarget || !skullUnit || !skullUnit->IsAlive())
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), assaultBot->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), assaultBot->GetGUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2598,7 +2591,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event)
|
|||||||
highestHealthUnit = aerialCommandUnit;
|
highestHealthUnit = aerialCommandUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID());
|
||||||
if (highestHealthUnit == leviathanMkII)
|
if (highestHealthUnit == leviathanMkII)
|
||||||
{
|
{
|
||||||
if (AI_VALUE(std::string, "rti") == "skull")
|
if (AI_VALUE(std::string, "rti") == "skull")
|
||||||
@@ -2608,7 +2601,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), leviathanMkII->GetGUID());
|
group->SetTargetIcon(crossIndex, bot->GetGUID(), leviathanMkII->GetGUID());
|
||||||
if (AI_VALUE(std::string, "rti") != "cross")
|
if (AI_VALUE(std::string, "rti") != "cross")
|
||||||
{
|
{
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
|
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
|
||||||
@@ -2714,575 +2707,3 @@ bool VezaxMarkOfTheFacelessAction::Execute(Event event)
|
|||||||
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionZ(), false, false, false, true,
|
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionZ(), false, false, false, true,
|
||||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool YoggSaronOminousCloudCheatAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
YoggSaronTrigger yoggSaronTrigger(botAI);
|
|
||||||
|
|
||||||
Unit* boss = yoggSaronTrigger.GetSaraIfAlive();
|
|
||||||
if (!boss)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f);
|
|
||||||
if (!target || !target->IsAlive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
target->Kill(bot, target);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronGuardianPositioningAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_MIDDLE.GetPositionZ(), false, false, false, true,
|
|
||||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronSanityAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f);
|
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), sanityWell->GetPositionX(), sanityWell->GetPositionY(), sanityWell->GetPositionZ(),
|
|
||||||
false, false, false, true, MovementPriority::MOVEMENT_FORCED,
|
|
||||||
true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMarkTargetAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
YoggSaronTrigger yoggSaronTrigger(botAI);
|
|
||||||
if (yoggSaronTrigger.IsPhase2())
|
|
||||||
{
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
Unit* crusherTentacle = bot->FindNearestCreature(NPC_CRUSHER_TENTACLE, 200.0f, true);
|
|
||||||
if (crusherTentacle)
|
|
||||||
{
|
|
||||||
crusherTentacle->Kill(bot, crusherTentacle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
|
|
||||||
Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
|
|
||||||
if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID())
|
|
||||||
{
|
|
||||||
group->SetTargetIcon(RtiTargetValue::moonIndex, bot->GetGUID(), yogg_saron->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
|
|
||||||
Creature* nextPossibleTarget = bot->FindNearestCreature(NPC_CONSTRICTOR_TENTACLE, 200.0f, true);
|
|
||||||
if (!nextPossibleTarget)
|
|
||||||
{
|
|
||||||
nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true);
|
|
||||||
if (!nextPossibleTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentSkullTarget)
|
|
||||||
{
|
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
|
|
||||||
if (currentSkullUnit && currentSkullUnit->IsAlive() &&
|
|
||||||
currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), nextPossibleTarget->GetGUID());
|
|
||||||
}
|
|
||||||
else if (yoggSaronTrigger.IsPhase3())
|
|
||||||
{
|
|
||||||
TankFaceStrategy tankFaceStrategy(botAI);
|
|
||||||
if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT))
|
|
||||||
{
|
|
||||||
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
TankAssistStrategy tankAssistStrategy(botAI);
|
|
||||||
if (!botAI->HasStrategy(tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT))
|
|
||||||
{
|
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
|
|
||||||
int lowestHealth = std::numeric_limits<int>::max();
|
|
||||||
Unit* lowestHealthUnit = nullptr;
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (!unit || !unit->IsAlive())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
|
|
||||||
unit->GetHealthPct() > 10)
|
|
||||||
{
|
|
||||||
if (unit->GetHealth() < lowestHealth)
|
|
||||||
{
|
|
||||||
lowestHealth = unit->GetHealth();
|
|
||||||
lowestHealthUnit = unit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lowestHealthUnit)
|
|
||||||
{
|
|
||||||
// Added because lunatic gaze freeze all bots and they can't attack
|
|
||||||
// If someone fix it then this cheat can be removed
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
lowestHealthUnit->Kill(bot, lowestHealthUnit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), lowestHealthUnit->GetGUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
Unit* currentSkullUnit = nullptr;
|
|
||||||
if (currentSkullTarget)
|
|
||||||
{
|
|
||||||
currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON)
|
|
||||||
{
|
|
||||||
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
|
|
||||||
if (yoggsaron && yoggsaron->IsAlive())
|
|
||||||
{
|
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), yoggsaron->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronBrainLinkAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* player = gref->GetSource();
|
|
||||||
if (player && player->IsAlive() && player->HasAura(SPELL_BRAIN_LINK) && player->GetGUID() != bot->GetGUID())
|
|
||||||
{
|
|
||||||
return MoveNear(player, 10.0f, MovementPriority::MOVEMENT_FORCED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMoveToEnterPortalAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInBrainRoomTeam = false;
|
|
||||||
int portalNumber = 0;
|
|
||||||
int brainRoomTeamCount = 10;
|
|
||||||
if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
|
|
||||||
{
|
|
||||||
brainRoomTeamCount = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
if (master && !botAI->IsTank(master))
|
|
||||||
{
|
|
||||||
portalNumber++;
|
|
||||||
brainRoomTeamCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* member = gref->GetSource();
|
|
||||||
if (!member || !member->IsAlive() || botAI->IsTank(member) || botAI->GetMaster()->GetGUID() == member->GetGUID())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
portalNumber++;
|
|
||||||
if (member->GetGUID() == bot->GetGUID())
|
|
||||||
{
|
|
||||||
isInBrainRoomTeam = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
brainRoomTeamCount--;
|
|
||||||
if (brainRoomTeamCount == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isInBrainRoomTeam)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position assignedPortalPosition = yoggPortalLoc[portalNumber - 1];
|
|
||||||
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("diamond");
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), assignedPortalPosition.GetPositionX(),
|
|
||||||
assignedPortalPosition.GetPositionY(),
|
|
||||||
assignedPortalPosition.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MoveNear(bot->GetMapId(), assignedPortalPosition.GetPositionX(),
|
|
||||||
assignedPortalPosition.GetPositionY(),
|
|
||||||
assignedPortalPosition.GetPositionZ(), sPlayerbotAIConfig->contactDistance,
|
|
||||||
MovementPriority::MOVEMENT_FORCED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronFallFromFloorAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
std::string rtiMark = AI_VALUE(std::string, "rti");
|
|
||||||
if (rtiMark == "skull")
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionZ(),
|
|
||||||
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetOrientation());
|
|
||||||
}
|
|
||||||
if (rtiMark == "cross")
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionZ(),
|
|
||||||
bot->GetOrientation());
|
|
||||||
}
|
|
||||||
if (rtiMark == "circle")
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
if (rtiMark == "star")
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronBossRoomMovementCheatAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
FollowMasterStrategy followMasterStrategy(botAI);
|
|
||||||
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
|
|
||||||
{
|
|
||||||
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
|
|
||||||
if (!currentSkullTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
|
|
||||||
if (!currentSkullUnit || !currentSkullUnit->IsAlive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY(),
|
|
||||||
currentSkullUnit->GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronUsePortalAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true);
|
|
||||||
if (!assignedPortal)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FollowMasterStrategy followMasterStrategy(botAI);
|
|
||||||
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
|
|
||||||
{
|
|
||||||
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return assignedPortal->HandleSpellClick(bot);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
YoggSaronTrigger yoggSaronTrigger(botAI);
|
|
||||||
|
|
||||||
bool resultSetRtiMark = SetRtiMark(yoggSaronTrigger);
|
|
||||||
bool resultSetIllusionRtiTarget = SetIllusionRtiTarget(yoggSaronTrigger);
|
|
||||||
bool resultSetBrainRtiTarget = SetBrainRtiTarget(yoggSaronTrigger);
|
|
||||||
|
|
||||||
return resultSetRtiMark || resultSetIllusionRtiTarget || resultSetBrainRtiTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomAction::SetRtiMark(YoggSaronTrigger yoggSaronTrigger)
|
|
||||||
{
|
|
||||||
if (AI_VALUE(std::string, "rti") == "diamond")
|
|
||||||
{
|
|
||||||
if (yoggSaronTrigger.IsInStormwindKeeperIllusion())
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (yoggSaronTrigger.IsInIcecrownKeeperIllusion())
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("circle");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (yoggSaronTrigger.IsInChamberOfTheAspectsIllusion())
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("star");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomAction::SetIllusionRtiTarget(YoggSaronTrigger yoggSaronTrigger)
|
|
||||||
{
|
|
||||||
Unit* currentRtiTarget = yoggSaronTrigger.GetIllusionRoomRtiTarget();
|
|
||||||
if (currentRtiTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* nextRtiTarget = yoggSaronTrigger.GetNextIllusionRoomRtiTarget();
|
|
||||||
if (!nextRtiTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If proper adds handling in illusion room will be implemented, then this can be removed
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
bot->TeleportTo(bot->GetMapId(), nextRtiTarget->GetPositionX(), nextRtiTarget->GetPositionY(),
|
|
||||||
nextRtiTarget->GetPositionZ(), bot->GetOrientation());
|
|
||||||
|
|
||||||
Unit::DealDamage(bot->GetSession()->GetPlayer(), nextRtiTarget, nextRtiTarget->GetHealth(), nullptr,
|
|
||||||
DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
|
|
||||||
group->SetTargetIcon(rtiIndex, bot->GetGUID(), nextRtiTarget->GetGUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger)
|
|
||||||
{
|
|
||||||
if (AI_VALUE(std::string, "rti") == "square" || !yoggSaronTrigger.IsMasterIsInBrainRoom())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("square");
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature* brain = bot->FindNearestCreature(NPC_BRAIN, 200.0f, true);
|
|
||||||
if (!brain)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
group->SetTargetIcon(RtiTargetValue::squareIndex, bot->GetGUID(), brain->GetGUID());
|
|
||||||
|
|
||||||
Position entrancePosition = yoggSaronTrigger.GetIllusionRoomEntrancePosition();
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
if (Unit const* master = botAI->GetMaster())
|
|
||||||
{
|
|
||||||
Position masterPosition = master->GetPosition();
|
|
||||||
bot->TeleportTo(bot->GetMapId(), masterPosition.GetPositionX(), masterPosition.GetPositionY(),
|
|
||||||
masterPosition.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bot->TeleportTo(bot->GetMapId(), entrancePosition.GetPositionX(), entrancePosition.GetPositionY(),
|
|
||||||
entrancePosition.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MoveTo(bot->GetMapId(), entrancePosition.GetPositionX(), entrancePosition.GetPositionY(),
|
|
||||||
entrancePosition.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
botAI->DoSpecificAction("attack rti target");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMoveToExitPortalAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
GameObject* portal = bot->FindNearestGameObject(GO_FLEE_TO_THE_SURFACE_PORTAL, 100.0f);
|
|
||||||
if (!portal)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
bot->TeleportTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(),
|
|
||||||
bot->GetOrientation());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MoveTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), false,
|
|
||||||
false, false, true, MovementPriority::MOVEMENT_FORCED,
|
|
||||||
true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetDistance2d(portal) > 2.0f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
portal->Use(bot);
|
|
||||||
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("skull");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronLunaticGazeAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "yogg-saron");
|
|
||||||
if (!boss || !boss->IsAlive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
float angle = bot->GetAngle(boss);
|
|
||||||
float newAngle = Position::NormalizeOrientation(angle + M_PI); // Add 180 degrees (PI radians)
|
|
||||||
bot->SetFacingTo(newAngle);
|
|
||||||
|
|
||||||
if (botAI->IsRangedDps(bot))
|
|
||||||
{
|
|
||||||
if (AI_VALUE(std::string, "rti") != "cross")
|
|
||||||
{
|
|
||||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronPhase3PositioningAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
if (botAI->IsRanged(bot))
|
|
||||||
{
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionZ(),
|
|
||||||
bot->GetOrientation());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionZ(), false,
|
|
||||||
false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->IsMelee(bot) && !botAI->IsTank(bot))
|
|
||||||
{
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), false, false, false, true,
|
|
||||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->IsTank(bot))
|
|
||||||
{
|
|
||||||
if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f)
|
|
||||||
{
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), bot->GetOrientation());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), false, false, false, true,
|
|
||||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "RaidUlduarTriggers.h"
|
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -379,125 +378,4 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class YoggSaronOminousCloudCheatAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronOminousCloudCheatAction(PlayerbotAI* ai) : Action(ai, "yogg-saron ominous cloud cheat action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronGuardianPositioningAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronGuardianPositioningAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron guardian positioning action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronSanityAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronSanityAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron sanity action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronDeathOrbAction : public MoveAwayFromCreatureAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronDeathOrbAction(PlayerbotAI* ai) : MoveAwayFromCreatureAction(ai, "yogg-saron death orb action", NPC_DEATH_ORB, 10.0f) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMaladyOfTheMindAction : public MoveAwayFromPlayerWithDebuffAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMaladyOfTheMindAction(PlayerbotAI* ai) : MoveAwayFromPlayerWithDebuffAction(ai, "yogg-saron malady of the mind action", SPELL_MALADY_OF_THE_MIND, 15.0f) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMarkTargetAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMarkTargetAction(PlayerbotAI* ai) : Action(ai, "yogg-saron mark target action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronBrainLinkAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronBrainLinkAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron brain link action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMoveToEnterPortalAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMoveToEnterPortalAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron move to enter portal action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronFallFromFloorAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronFallFromFloorAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron fall from floor action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronBossRoomMovementCheatAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronBossRoomMovementCheatAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron boss room movement cheat action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronUsePortalAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronUsePortalAction(PlayerbotAI* ai) : Action(ai, "yogg-saron use portal action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronIllusionRoomAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronIllusionRoomAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron illusion room action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool SetRtiMark(YoggSaronTrigger yoggSaronTrigger);
|
|
||||||
bool SetIllusionRtiTarget(YoggSaronTrigger yoggSaronTrigger);
|
|
||||||
bool SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger);
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMoveToExitPortalAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMoveToExitPortalAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron move to exit portal action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronLunaticGazeAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronLunaticGazeAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron lunatic gaze action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronPhase3PositioningAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronPhase3PositioningAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron phase 3 positioning action") {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -255,66 +255,6 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"yogg-saron shadow resistance trigger",
|
"yogg-saron shadow resistance trigger",
|
||||||
NextAction::array(0, new NextAction("yogg-saron shadow resistance action", ACTION_RAID), nullptr)));
|
NextAction::array(0, new NextAction("yogg-saron shadow resistance action", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron ominous cloud cheat trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron ominous cloud cheat action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron guardian positioning trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron guardian positioning action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron sanity trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron sanity action", ACTION_RAID + 1), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron death orb trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron death orb action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron malady of the mind trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron malady of the mind action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron mark target trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron mark target action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron brain link trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron brain link action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron move to enter portal trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron move to enter portal action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron use portal trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron use portal action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron fall from floor trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron fall from floor action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron boss room movement cheat trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron boss room movement cheat action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron illusion room trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron illusion room action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron move to exit portal trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron move to exit portal action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron lunatic gaze trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron lunatic gaze action", ACTION_EMERGENCY), nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"yogg-saron phase 3 positioning trigger",
|
|
||||||
NextAction::array(0, new NextAction("yogg-saron phase 3 positioning action", ACTION_RAID), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
|||||||
@@ -67,24 +67,6 @@ public:
|
|||||||
creators["vezax cheat trigger"] = &RaidUlduarTriggerContext::vezax_cheat_trigger;
|
creators["vezax cheat trigger"] = &RaidUlduarTriggerContext::vezax_cheat_trigger;
|
||||||
creators["vezax shadow crash trigger"] = &RaidUlduarTriggerContext::vezax_shadow_crash_trigger;
|
creators["vezax shadow crash trigger"] = &RaidUlduarTriggerContext::vezax_shadow_crash_trigger;
|
||||||
creators["vezax mark of the faceless trigger"] = &RaidUlduarTriggerContext::vezax_mark_of_the_faceless_trigger;
|
creators["vezax mark of the faceless trigger"] = &RaidUlduarTriggerContext::vezax_mark_of_the_faceless_trigger;
|
||||||
creators["vezax shadow resistance trigger"] = &RaidUlduarTriggerContext::vezax_shadow_resistance_trigger;
|
|
||||||
creators["sara shadow resistance trigger"] = &RaidUlduarTriggerContext::sara_shadow_resistance_trigger;
|
|
||||||
creators["yogg-saron shadow resistance triggerr"] = &RaidUlduarTriggerContext::yogg_saron_shadow_resistance_trigger;
|
|
||||||
creators["yogg-saron ominous cloud cheat trigger"] = &RaidUlduarTriggerContext::yogg_saron_ominous_cloud_cheat_trigger;
|
|
||||||
creators["yogg-saron guardian positioning trigger"] = &RaidUlduarTriggerContext::yogg_saron_guardian_positioning_trigger;
|
|
||||||
creators["yogg-saron sanity trigger"] = &RaidUlduarTriggerContext::yogg_saron_sanity_trigger;
|
|
||||||
creators["yogg-saron death orb trigger"] = &RaidUlduarTriggerContext::yogg_saron_death_orb_trigger;
|
|
||||||
creators["yogg-saron malady of the mind trigger"] = &RaidUlduarTriggerContext::yogg_saron_malady_of_the_mind_trigger;
|
|
||||||
creators["yogg-saron mark target trigger"] = &RaidUlduarTriggerContext::yogg_saron_mark_target_trigger;
|
|
||||||
creators["yogg-saron brain link trigger"] = &RaidUlduarTriggerContext::yogg_saron_brain_link_trigger;
|
|
||||||
creators["yogg-saron move to enter portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_move_to_enter_portal_trigger;
|
|
||||||
creators["yogg-saron use portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_use_portal_trigger;
|
|
||||||
creators["yogg-saron fall from floor trigger"] = &RaidUlduarTriggerContext::yogg_saron_fall_from_floor_trigger;
|
|
||||||
creators["yogg-saron boss room movement cheat trigger"] = &RaidUlduarTriggerContext::yogg_saron_boss_room_movement_cheat_trigger;
|
|
||||||
creators["yogg-saron illusion room trigger"] = &RaidUlduarTriggerContext::yogg_saron_illusion_room_trigger;
|
|
||||||
creators["yogg-saron move to exit portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_move_to_exit_portal_trigger;
|
|
||||||
creators["yogg-saron lunatic gaze trigger"] = &RaidUlduarTriggerContext::yogg_saron_lunatic_gaze_trigger;
|
|
||||||
creators["yogg-saron phase 3 positioning trigger"] = &RaidUlduarTriggerContext::yogg_saron_phase_3_positioning_trigger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -138,25 +120,7 @@ private:
|
|||||||
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
|
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
|
||||||
static Trigger* vezax_cheat_trigger(PlayerbotAI* ai) { return new VezaxCheatTrigger(ai); }
|
static Trigger* vezax_cheat_trigger(PlayerbotAI* ai) { return new VezaxCheatTrigger(ai); }
|
||||||
static Trigger* vezax_shadow_crash_trigger(PlayerbotAI* ai) { return new VezaxShadowCrashTrigger(ai); }
|
static Trigger* vezax_shadow_crash_trigger(PlayerbotAI* ai) { return new VezaxShadowCrashTrigger(ai); }
|
||||||
static Trigger* vezax_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "general vezax"); }
|
|
||||||
static Trigger* sara_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "sara"); }
|
|
||||||
static Trigger* yogg_saron_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "yogg-saron"); }
|
|
||||||
static Trigger* vezax_mark_of_the_faceless_trigger(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessTrigger(ai); }
|
static Trigger* vezax_mark_of_the_faceless_trigger(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessTrigger(ai); }
|
||||||
static Trigger* yogg_saron_ominous_cloud_cheat_trigger(PlayerbotAI* ai) { return new YoggSaronOminousCloudCheatTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_guardian_positioning_trigger(PlayerbotAI* ai) { return new YoggSaronGuardianPositioningTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_sanity_trigger(PlayerbotAI* ai) { return new YoggSaronSanityTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_death_orb_trigger(PlayerbotAI* ai) { return new YoggSaronDeathOrbTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_malady_of_the_mind_trigger(PlayerbotAI* ai) { return new YoggSaronMaladyOfTheMindTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_mark_target_trigger(PlayerbotAI* ai) { return new YoggSaronMarkTargetTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_brain_link_trigger(PlayerbotAI* ai) { return new YoggSaronBrainLinkTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_move_to_enter_portal_trigger(PlayerbotAI* ai) { return new YoggSaronMoveToEnterPortalTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_use_portal_trigger(PlayerbotAI* ai) { return new YoggSaronUsePortalTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_fall_from_floor_trigger(PlayerbotAI* ai) { return new YoggSaronFallFromFloorTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_boss_room_movement_cheat_trigger(PlayerbotAI* ai) { return new YoggSaronBossRoomMovementCheatTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_illusion_room_trigger(PlayerbotAI* ai) { return new YoggSaronIllusionRoomTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_move_to_exit_portal_trigger(PlayerbotAI* ai) { return new YoggSaronMoveToExitPortalTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_lunatic_gaze_trigger(PlayerbotAI* ai) { return new YoggSaronLunaticGazeTrigger(ai); }
|
|
||||||
static Trigger* yogg_saron_phase_3_positioning_trigger(PlayerbotAI* ai) { return new YoggSaronPhase3PositioningTrigger(ai); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,33 +12,11 @@
|
|||||||
#include "Trigger.h"
|
#include "Trigger.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include <MovementActions.h>
|
#include <MovementActions.h>
|
||||||
#include <FollowMasterStrategy.h>
|
|
||||||
#include <RtiTargetValue.h>
|
|
||||||
|
|
||||||
const std::vector<uint32> availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER,
|
const std::vector<uint32> availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER,
|
||||||
NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE,
|
NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE,
|
||||||
NPC_SALVAGED_SIEGE_ENGINE_TURRET};
|
NPC_SALVAGED_SIEGE_ENGINE_TURRET};
|
||||||
|
|
||||||
const std::vector<uint32> illusionMobs =
|
|
||||||
{
|
|
||||||
NPC_INFLUENCE_TENTACLE,
|
|
||||||
NPC_RUBY_CONSORT,
|
|
||||||
NPC_AZURE_CONSORT,
|
|
||||||
NPC_BRONZE_CONSORT,
|
|
||||||
NPC_EMERALD_CONSORT,
|
|
||||||
NPC_OBSIDIAN_CONSORT,
|
|
||||||
NPC_ALEXTRASZA,
|
|
||||||
NPC_MALYGOS_ILLUSION,
|
|
||||||
NPC_NELTHARION,
|
|
||||||
NPC_YSERA,
|
|
||||||
NPC_DEATHSWORN_ZEALOT,
|
|
||||||
NPC_LICH_KING_ILLUSION,
|
|
||||||
NPC_IMMOLATED_CHAMPION,
|
|
||||||
NPC_SUIT_OF_ARMOR,
|
|
||||||
NPC_GARONA,
|
|
||||||
NPC_KING_LLANE
|
|
||||||
};
|
|
||||||
|
|
||||||
bool FlameLeviathanOnVehicleTrigger::IsActive()
|
bool FlameLeviathanOnVehicleTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* vehicleBase = bot->GetVehicleBase();
|
Unit* vehicleBase = bot->GetVehicleBase();
|
||||||
@@ -470,8 +448,8 @@ bool KologarnAttackDpsTargetTrigger::IsActive()
|
|||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||||
ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
|
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
|
||||||
|
|
||||||
if (crossTarget && (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)))
|
if (crossTarget && (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)))
|
||||||
{
|
{
|
||||||
@@ -767,7 +745,7 @@ bool ThorimUnbalancingStrikeTrigger::IsActive()
|
|||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||||
|
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return bot->HasAura(SPELL_UNBALANCING_STRIKE);
|
return bot->HasAura(SPELL_UNBALANCING_STRIKE);
|
||||||
@@ -784,7 +762,7 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
|
|||||||
|
|
||||||
if (botAI->IsMainTank(bot))
|
if (botAI->IsMainTank(bot))
|
||||||
{
|
{
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
||||||
if (currentSkullUnit && !currentSkullUnit->IsAlive())
|
if (currentSkullUnit && !currentSkullUnit->IsAlive())
|
||||||
{
|
{
|
||||||
@@ -805,13 +783,13 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
|
|||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||||
|
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive()))
|
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive()))
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
|
group->SetTargetIcon(skullIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,7 +811,7 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
|
|||||||
if (mainTank && bot->GetDistance(mainTank) < 30.0f)
|
if (mainTank && bot->GetDistance(mainTank) < 30.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ObjectGuid currentCrossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
|
ObjectGuid currentCrossTarget = group->GetTargetIcon(crossIndex);
|
||||||
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
|
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
|
||||||
if (currentCrossUnit && !currentCrossUnit->IsAlive())
|
if (currentCrossUnit && !currentCrossUnit->IsAlive())
|
||||||
{
|
{
|
||||||
@@ -985,7 +963,7 @@ bool ThorimArenaPositioningTrigger::IsActive()
|
|||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||||
|
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||||
@@ -1083,7 +1061,7 @@ bool ThorimPhase2PositioningTrigger::IsActive()
|
|||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
|
||||||
|
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
|
||||||
@@ -1430,8 +1408,8 @@ bool MimironAerialCommandUnitTrigger::IsActive()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||||
ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
|
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
|
||||||
|
|
||||||
//if (bombBot && bombBot->GetGUID() != crossTarget)
|
//if (bombBot && bombBot->GetGUID() != crossTarget)
|
||||||
//{
|
//{
|
||||||
@@ -1537,7 +1515,7 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
|
|||||||
highestHealthUnit = aerialCommandUnit;
|
highestHealthUnit = aerialCommandUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
|
||||||
if (!skullTarget)
|
if (!skullTarget)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -1641,750 +1619,3 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
|||||||
|
|
||||||
return distance > 2.0f;
|
return distance > 2.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* YoggSaronTrigger::GetSaraIfAlive()
|
|
||||||
{
|
|
||||||
Unit* sara = AI_VALUE2(Unit*, "find target", "sara");
|
|
||||||
if (!sara || !sara->IsAlive())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return sara;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsPhase2()
|
|
||||||
{
|
|
||||||
Creature* target = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
|
|
||||||
|
|
||||||
return target && target->IsAlive() && target->HasAura(SPELL_SHADOW_BARRIER);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsPhase3()
|
|
||||||
{
|
|
||||||
Creature* target = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
|
|
||||||
Creature* guardian = bot->FindNearestCreature(NPC_GUARDIAN_OF_YS, 200.0f, true);
|
|
||||||
|
|
||||||
return target && target->IsAlive() && !target->HasAura(SPELL_SHADOW_BARRIER) && !guardian;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsInBrainLevel()
|
|
||||||
{
|
|
||||||
return bot->GetPositionZ() > 230.0f && bot->GetPositionZ() < 250.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsYoggSaronFight()
|
|
||||||
{
|
|
||||||
Unit* sara = AI_VALUE2(Unit*, "find target", "sara");
|
|
||||||
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
|
|
||||||
|
|
||||||
if ((sara && sara->IsAlive()) || (yoggsaron && yoggsaron->IsAlive()))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsInIllusionRoom()
|
|
||||||
{
|
|
||||||
if (!IsInBrainLevel())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsInStormwindKeeperIllusion())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsInIcecrownKeeperIllusion())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsInChamberOfTheAspectsIllusion())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsInStormwindKeeperIllusion()
|
|
||||||
{
|
|
||||||
return bot->GetDistance2d(ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionY()) <
|
|
||||||
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsInIcecrownKeeperIllusion()
|
|
||||||
{
|
|
||||||
return bot->GetDistance2d(ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionY()) <
|
|
||||||
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsInChamberOfTheAspectsIllusion()
|
|
||||||
{
|
|
||||||
return bot->GetDistance2d(ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionY()) <
|
|
||||||
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsMasterIsInIllusionGroup()
|
|
||||||
{
|
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
return master && !botAI->IsTank(master);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronTrigger::IsMasterIsInBrainRoom()
|
|
||||||
{
|
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
|
|
||||||
if (!master)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return master->GetDistance2d(ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionY()) <
|
|
||||||
ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS &&
|
|
||||||
master->GetPositionZ() > 230.0f && master->GetPositionZ() < 250.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position YoggSaronTrigger::GetIllusionRoomEntrancePosition()
|
|
||||||
{
|
|
||||||
if (IsInChamberOfTheAspectsIllusion())
|
|
||||||
{
|
|
||||||
return ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
|
|
||||||
}
|
|
||||||
else if (IsInIcecrownKeeperIllusion())
|
|
||||||
{
|
|
||||||
return ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
|
|
||||||
}
|
|
||||||
else if (IsInStormwindKeeperIllusion())
|
|
||||||
{
|
|
||||||
return ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Position();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget()
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
|
|
||||||
if (rtiIndex == -1)
|
|
||||||
{
|
|
||||||
return nullptr; // Invalid RTI mark
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid currentRtiTarget = group->GetTargetIcon(rtiIndex);
|
|
||||||
Unit* currentRtiTargetUnit = botAI->GetUnit(currentRtiTarget);
|
|
||||||
if (!currentRtiTargetUnit || !currentRtiTargetUnit->IsAlive())
|
|
||||||
{
|
|
||||||
currentRtiTargetUnit = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentRtiTargetUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget()
|
|
||||||
{
|
|
||||||
float detectionRadius = 0.0f;
|
|
||||||
if (IsInStormwindKeeperIllusion())
|
|
||||||
{
|
|
||||||
detectionRadius = ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS;
|
|
||||||
}
|
|
||||||
else if (IsInIcecrownKeeperIllusion())
|
|
||||||
{
|
|
||||||
detectionRadius = ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS;
|
|
||||||
}
|
|
||||||
else if (IsInChamberOfTheAspectsIllusion())
|
|
||||||
{
|
|
||||||
detectionRadius = ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_LAUGHING_SKULL)
|
|
||||||
{
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float nearestDistance = std::numeric_limits<float>::max();
|
|
||||||
Unit* nextIllusionRoomRtiTarget = nullptr;
|
|
||||||
|
|
||||||
for (const uint32& creatureId : illusionMobs)
|
|
||||||
{
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (unit && unit->IsAlive() && unit->GetEntry() == creatureId)
|
|
||||||
{
|
|
||||||
float distance = bot->GetDistance(unit);
|
|
||||||
if (distance < nearestDistance)
|
|
||||||
{
|
|
||||||
nextIllusionRoomRtiTarget = unit;
|
|
||||||
nearestDistance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextIllusionRoomRtiTarget)
|
|
||||||
{
|
|
||||||
return nextIllusionRoomRtiTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsInStormwindKeeperIllusion())
|
|
||||||
{
|
|
||||||
Creature* target = bot->FindNearestCreature(NPC_SUIT_OF_ARMOR, detectionRadius, true);
|
|
||||||
if (target)
|
|
||||||
{
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronOminousCloudCheatTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* boss = GetSaraIfAlive();
|
|
||||||
if (!boss)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!botAI->IsBotMainTank(bot))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetDistance2d(boss->GetPositionX(), boss->GetPositionY()) > 50.0f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f, true);
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronGuardianPositioningTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!GetSaraIfAlive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!botAI->IsTank(bot))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
bool thereIsAnyGuardian = false;
|
|
||||||
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (!unit || !unit->IsAlive())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unit->GetEntry() == NPC_GUARDIAN_OF_YS)
|
|
||||||
{
|
|
||||||
thereIsAnyGuardian = true;
|
|
||||||
ObjectGuid unitTargetGuid = unit->GetTarget();
|
|
||||||
Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid);
|
|
||||||
if (!targetedPlayer || !botAI->IsTank(targetedPlayer))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return thereIsAnyGuardian &&
|
|
||||||
bot->GetDistance2d(ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY()) > 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronSanityTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Aura* sanityAura = bot->GetAura(SPELL_SANITY);
|
|
||||||
|
|
||||||
if (!sanityAura)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sanityAuraStacks = sanityAura->GetStackAmount();
|
|
||||||
|
|
||||||
Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f);
|
|
||||||
|
|
||||||
if (!sanityWell)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
float distanceToSanityWell = bot->GetDistance(sanityWell);
|
|
||||||
|
|
||||||
return (distanceToSanityWell >= 1.0f && sanityAuraStacks < 40) ||
|
|
||||||
(distanceToSanityWell < 1.0f && sanityAuraStacks < 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronDeathOrbTrigger::IsActive()
|
|
||||||
{
|
|
||||||
TooCloseToCreatureTrigger tooCloseToDeathOrbTrigger(botAI);
|
|
||||||
return IsPhase2() && tooCloseToDeathOrbTrigger.TooCloseToCreature(NPC_DEATH_ORB, 10.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMaladyOfTheMindTrigger::IsActive()
|
|
||||||
{
|
|
||||||
TooCloseToPlayerWithDebuffTrigger tooCloseToPlayerWithDebuffTrigger(botAI);
|
|
||||||
return IsPhase2() && tooCloseToPlayerWithDebuffTrigger.TooCloseToPlayerWithDebuff(SPELL_MALADY_OF_THE_MIND, 15.0f) && botAI->CanMove();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMarkTargetTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!botAI->IsBotMainTank(bot))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsPhase2())
|
|
||||||
{
|
|
||||||
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
|
|
||||||
Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
|
|
||||||
if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
|
|
||||||
Creature* nextPossibleTarget = bot->FindNearestCreature(NPC_CONSTRICTOR_TENTACLE, 200.0f, true);
|
|
||||||
if (!nextPossibleTarget)
|
|
||||||
{
|
|
||||||
nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true);
|
|
||||||
if (!nextPossibleTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentSkullTarget)
|
|
||||||
{
|
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
|
|
||||||
if (!currentSkullUnit)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentSkullUnit->IsAlive() && currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (IsPhase3())
|
|
||||||
{
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
Unit* currentSkullUnit = nullptr;
|
|
||||||
if (currentSkullTarget)
|
|
||||||
{
|
|
||||||
currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentSkullUnit &&
|
|
||||||
(currentSkullUnit->GetEntry() == NPC_IMMORTAL_GUARDIAN ||
|
|
||||||
currentSkullUnit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
|
|
||||||
currentSkullUnit->GetHealthPct() > 10)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (!unit || !unit->IsAlive())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
|
|
||||||
unit->GetHealthPct() > 10)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronBrainLinkTrigger::IsActive()
|
|
||||||
{
|
|
||||||
TooFarFromPlayerWithAuraTrigger tooFarFromPlayerWithAuraTrigger(botAI);
|
|
||||||
return IsPhase2() && bot->HasAura(SPELL_BRAIN_LINK) &&
|
|
||||||
tooFarFromPlayerWithAuraTrigger.TooFarFromPlayerWithAura(SPELL_BRAIN_LINK, 20.0f, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMoveToEnterPortalTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsPhase2())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature* portal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 100.0f, true);
|
|
||||||
if (!portal)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetDistance2d(portal->GetPositionX(), portal->GetPositionY()) < 2.0f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AI_VALUE(std::string, "rti") != "skull")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int brainRoomTeamCount = 10;
|
|
||||||
if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
|
|
||||||
{
|
|
||||||
brainRoomTeamCount = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsMasterIsInIllusionGroup())
|
|
||||||
{
|
|
||||||
brainRoomTeamCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* member = gref->GetSource();
|
|
||||||
if (!member || !member->IsAlive() || botAI->IsTank(member))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member->GetGUID() == bot->GetGUID())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
brainRoomTeamCount--;
|
|
||||||
if (brainRoomTeamCount == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronFallFromFloorTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string rtiMark = AI_VALUE(std::string, "rti");
|
|
||||||
|
|
||||||
if (rtiMark == "skull" && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ((rtiMark == "cross" || rtiMark == "circle" || rtiMark == "star") && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronBossRoomMovementCheatTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight() || !IsPhase2())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FollowMasterStrategy followMasterStrategy(botAI);
|
|
||||||
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AI_VALUE(std::string, "rti") != "skull")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
|
||||||
|
|
||||||
if (!currentSkullTarget)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
|
|
||||||
|
|
||||||
if (!currentSkullUnit || !currentSkullUnit->IsAlive() || bot->GetDistance2d(currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY()) < 40.0f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronUsePortalTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsPhase2())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AI_VALUE(std::string, "rti") != "diamond")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight() || !IsInIllusionRoom() || AI_VALUE(std::string, "rti") == "square")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SetRtiMarkRequired())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SetRtiTargetRequired())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GoToBrainRoomRequired())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomTrigger::GoToBrainRoomRequired()
|
|
||||||
{
|
|
||||||
if (AI_VALUE(std::string, "rti") == "square")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IsMasterIsInBrainRoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomTrigger::SetRtiMarkRequired()
|
|
||||||
{
|
|
||||||
return AI_VALUE(std::string, "rti") == "diamond";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronIllusionRoomTrigger::SetRtiTargetRequired()
|
|
||||||
{
|
|
||||||
Unit const* currentRtiTarget = GetIllusionRoomRtiTarget();
|
|
||||||
if (currentRtiTarget != nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetNextIllusionRoomRtiTarget() != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronMoveToExitPortalTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight() || !IsInBrainLevel())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature const* brain = bot->FindNearestCreature(NPC_BRAIN, 60.0f, true);
|
|
||||||
if (!brain || !brain->IsAlive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brain->HasUnitState(UNIT_STATE_CASTING))
|
|
||||||
{
|
|
||||||
Spell* induceMadnessSpell = brain->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
|
||||||
|
|
||||||
if (induceMadnessSpell && induceMadnessSpell->m_spellInfo->Id == SPELL_INDUCE_MADNESS)
|
|
||||||
{
|
|
||||||
uint32 castingTimeLeft = induceMadnessSpell->GetCastTimeRemaining();
|
|
||||||
if ((botAI->HasCheat(BotCheatMask::raid) && castingTimeLeft < 6000) ||
|
|
||||||
(!botAI->HasCheat(BotCheatMask::raid) && castingTimeLeft < 8000))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (brain->GetHealth() < brain->GetMaxHealth() * 0.3f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronLunaticGazeTrigger::IsActive()
|
|
||||||
{
|
|
||||||
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
|
|
||||||
|
|
||||||
if (yoggsaron && yoggsaron->IsAlive() && yoggsaron->HasUnitState(UNIT_STATE_CASTING))
|
|
||||||
{
|
|
||||||
Spell* currentSpell = yoggsaron->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
|
||||||
if (currentSpell && currentSpell->m_spellInfo->Id == SPELL_LUNATIC_GAZE_YS)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool YoggSaronPhase3PositioningTrigger::IsActive()
|
|
||||||
{
|
|
||||||
if (!IsYoggSaronFight() || !IsPhase3())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
YoggSaronSanityTrigger sanityTrigger(botAI);
|
|
||||||
if (sanityTrigger.IsActive())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->IsMelee(bot) && !botAI->IsTank(bot) &&
|
|
||||||
bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY()) > 15.0f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botAI->IsTank(bot))
|
|
||||||
{
|
|
||||||
if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
|
|
||||||
bool thereIsAnyGuardian = false;
|
|
||||||
|
|
||||||
for (const ObjectGuid& guid : targets)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (!unit || !unit->IsAlive())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN)
|
|
||||||
{
|
|
||||||
thereIsAnyGuardian = true;
|
|
||||||
ObjectGuid unitTargetGuid = unit->GetTarget();
|
|
||||||
Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid);
|
|
||||||
if (!targetedPlayer || !botAI->IsTank(targetedPlayer))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thereIsAnyGuardian && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
|
|
||||||
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY()) > 3.0f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ enum UlduarIDs
|
|||||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||||
SPELL_RUNE_OF_POWER = 64320,
|
SPELL_RUNE_OF_POWER = 64320,
|
||||||
|
|
||||||
// Kologarn
|
//Kologarn
|
||||||
NPC_RIGHT_ARM = 32934,
|
NPC_RIGHT_ARM = 32934,
|
||||||
NPC_RUBBLE = 33768,
|
NPC_RUBBLE = 33768,
|
||||||
SPELL_CRUNCH_ARMOR = 64002,
|
SPELL_CRUNCH_ARMOR = 64002,
|
||||||
@@ -27,13 +27,13 @@ enum UlduarIDs
|
|||||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||||
|
|
||||||
// Hodir
|
// Hodir
|
||||||
NPC_SNOWPACKED_ICICLE = 33174,
|
NPC_SNOWPACKED_ICICLE = 33174,
|
||||||
NPC_TOASTY_FIRE = 33342,
|
NPC_TOASTY_FIRE = 33342,
|
||||||
SPELL_FLASH_FREEZE = 61968,
|
SPELL_FLASH_FREEZE = 61968,
|
||||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||||
|
|
||||||
// Freya
|
// Freya
|
||||||
NPC_SNAPLASHER = 32916,
|
NPC_SNAPLASHER = 32916,
|
||||||
NPC_STORM_LASHER = 32919,
|
NPC_STORM_LASHER = 32919,
|
||||||
@@ -44,7 +44,7 @@ enum UlduarIDs
|
|||||||
NPC_EONARS_GIFT = 33228,
|
NPC_EONARS_GIFT = 33228,
|
||||||
GOBJECT_NATURE_BOMB = 194902,
|
GOBJECT_NATURE_BOMB = 194902,
|
||||||
|
|
||||||
// Thorim
|
//Thorim
|
||||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||||
@@ -62,7 +62,7 @@ enum UlduarIDs
|
|||||||
NPC_IRON_HONOR_GUARD = 32875,
|
NPC_IRON_HONOR_GUARD = 32875,
|
||||||
SPELL_UNBALANCING_STRIKE = 62130,
|
SPELL_UNBALANCING_STRIKE = 62130,
|
||||||
|
|
||||||
// Mimiron
|
//Mimiron
|
||||||
NPC_LEVIATHAN_MKII = 33432,
|
NPC_LEVIATHAN_MKII = 33432,
|
||||||
NPC_VX001 = 33651,
|
NPC_VX001 = 33651,
|
||||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||||
@@ -78,73 +78,23 @@ enum UlduarIDs
|
|||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||||
|
|
||||||
// General Vezax
|
//General Vezax
|
||||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||||
SPELL_SHADOW_CRASH = 63277,
|
SPELL_SHADOW_CRASH = 63277,
|
||||||
|
|
||||||
// Yogg-Saron
|
|
||||||
ACTION_ILLUSION_DRAGONS = 1,
|
|
||||||
ACTION_ILLUSION_ICECROWN = 2,
|
|
||||||
ACTION_ILLUSION_STORMWIND = 3,
|
|
||||||
NPC_GUARDIAN_OF_YS = 33136,
|
|
||||||
NPC_YOGG_SARON = 33288,
|
|
||||||
NPC_OMINOUS_CLOUD = 33292,
|
|
||||||
NPC_RUBY_CONSORT = 33716,
|
|
||||||
NPC_AZURE_CONSORT = 33717,
|
|
||||||
NPC_BRONZE_CONSORT = 33718,
|
|
||||||
NPC_EMERALD_CONSORT = 33719,
|
|
||||||
NPC_OBSIDIAN_CONSORT = 33720,
|
|
||||||
NPC_ALEXTRASZA = 33536,
|
|
||||||
NPC_MALYGOS_ILLUSION = 33535,
|
|
||||||
NPC_NELTHARION = 33523,
|
|
||||||
NPC_YSERA = 33495,
|
|
||||||
GO_DRAGON_SOUL = 194462,
|
|
||||||
NPC_SARA_PHASE_1 = 33134,
|
|
||||||
NPC_LICH_KING_ILLUSION = 33441,
|
|
||||||
NPC_IMMOLATED_CHAMPION = 33442,
|
|
||||||
NPC_SUIT_OF_ARMOR = 33433,
|
|
||||||
NPC_GARONA = 33436,
|
|
||||||
NPC_KING_LLANE = 33437,
|
|
||||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
|
||||||
NPC_INFLUENCE_TENTACLE = 33943,
|
|
||||||
NPC_DEATH_ORB = 33882,
|
|
||||||
NPC_BRAIN = 33890,
|
|
||||||
NPC_CRUSHER_TENTACLE = 33966,
|
|
||||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
|
||||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
|
||||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
|
||||||
NPC_LAUGHING_SKULL = 33990,
|
|
||||||
NPC_SANITY_WELL = 33991,
|
|
||||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
|
||||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
|
||||||
SPELL_SANITY = 63050,
|
|
||||||
SPELL_BRAIN_LINK = 63802,
|
|
||||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
|
||||||
SPELL_SHADOW_BARRIER = 63894,
|
|
||||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
|
||||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
|
||||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
|
||||||
SPELL_TELEPORT_BACK = 63992,
|
|
||||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
|
||||||
SPELL_INDUCE_MADNESS = 64059,
|
|
||||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
|
||||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
|
||||||
|
|
||||||
// Buffs
|
// Buffs
|
||||||
SPELL_FROST_TRAP = 13809
|
SPELL_FROST_TRAP = 13809
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int8 skullIndex = 7; // Skull
|
||||||
|
const int8 crossIndex = 6; // Cross
|
||||||
|
const int8 moonIndex = 4; // Moon
|
||||||
|
|
||||||
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||||
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||||
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||||
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||||
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||||
const float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
|
||||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
|
||||||
|
|
||||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||||
@@ -173,16 +123,6 @@ const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553
|
|||||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
|
||||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Flame Levi
|
// Flame Levi
|
||||||
@@ -508,139 +448,4 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Yogg-Saron
|
|
||||||
//
|
|
||||||
class YoggSaronTrigger : public Trigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronTrigger(PlayerbotAI* ai, std::string const name = "yogg saron trigger", int32 checkInteval = 1)
|
|
||||||
: Trigger(ai, name, checkInteval) {}
|
|
||||||
|
|
||||||
bool IsYoggSaronFight();
|
|
||||||
bool IsPhase2();
|
|
||||||
bool IsPhase3();
|
|
||||||
bool IsInBrainLevel();
|
|
||||||
bool IsInIllusionRoom();
|
|
||||||
bool IsInStormwindKeeperIllusion();
|
|
||||||
bool IsInIcecrownKeeperIllusion();
|
|
||||||
bool IsInChamberOfTheAspectsIllusion();
|
|
||||||
bool IsMasterIsInIllusionGroup();
|
|
||||||
bool IsMasterIsInBrainRoom();
|
|
||||||
Position GetIllusionRoomEntrancePosition();
|
|
||||||
Unit* GetIllusionRoomRtiTarget();
|
|
||||||
Unit* GetNextIllusionRoomRtiTarget();
|
|
||||||
Unit* GetSaraIfAlive();
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronOminousCloudCheatTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronOminousCloudCheatTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron ominous cloud cheat trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronGuardianPositioningTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronGuardianPositioningTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron guardian positioning trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronSanityTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronSanityTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron sanity trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronDeathOrbTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronDeathOrbTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron death orb trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMaladyOfTheMindTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMaladyOfTheMindTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron malady of the mind trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMarkTargetTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMarkTargetTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron mark target trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronBrainLinkTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronBrainLinkTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron brain link trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMoveToEnterPortalTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMoveToEnterPortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron move to enter portal trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronFallFromFloorTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronFallFromFloorTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron fall from floor trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronBossRoomMovementCheatTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronBossRoomMovementCheatTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron boss room movement cheat trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronUsePortalTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronUsePortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron use portal trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronIllusionRoomTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronIllusionRoomTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron illusion room trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool GoToBrainRoomRequired();
|
|
||||||
bool SetRtiMarkRequired();
|
|
||||||
bool SetRtiTargetRequired();
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronMoveToExitPortalTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronMoveToExitPortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron move to exit portal trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronLunaticGazeTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronLunaticGazeTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron lunatic gaze trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class YoggSaronPhase3PositioningTrigger : public YoggSaronTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
YoggSaronPhase3PositioningTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron phase 3 positioning trigger") {}
|
|
||||||
bool IsActive() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ public:
|
|||||||
creators["emalon lighting nova action"] = &RaidVoAActionContext::emalon_lighting_nova_action;
|
creators["emalon lighting nova action"] = &RaidVoAActionContext::emalon_lighting_nova_action;
|
||||||
creators["emalon overcharge action"] = &RaidVoAActionContext::emalon_overcharge_action;
|
creators["emalon overcharge action"] = &RaidVoAActionContext::emalon_overcharge_action;
|
||||||
creators["emalon fall from floor action"] = &RaidVoAActionContext::emalon_fall_from_floor_action;
|
creators["emalon fall from floor action"] = &RaidVoAActionContext::emalon_fall_from_floor_action;
|
||||||
creators["emalon nature resistance action"] = &RaidVoAActionContext::emalon_nature_resistance_action;
|
|
||||||
creators["koralon fire resistance action"] = &RaidVoAActionContext::koralon_fire_resistance_action;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -29,8 +27,6 @@ private:
|
|||||||
static Action* emalon_lighting_nova_action(PlayerbotAI* ai) { return new EmalonLightingNovaAction(ai); }
|
static Action* emalon_lighting_nova_action(PlayerbotAI* ai) { return new EmalonLightingNovaAction(ai); }
|
||||||
static Action* emalon_overcharge_action(PlayerbotAI* ai) { return new EmalonOverchargeAction(ai); }
|
static Action* emalon_overcharge_action(PlayerbotAI* ai) { return new EmalonOverchargeAction(ai); }
|
||||||
static Action* emalon_fall_from_floor_action(PlayerbotAI* ai) { return new EmalonFallFromFloorAction(ai); }
|
static Action* emalon_fall_from_floor_action(PlayerbotAI* ai) { return new EmalonFallFromFloorAction(ai); }
|
||||||
static Action* emalon_nature_resistance_action(PlayerbotAI* ai) { return new BossNatureResistanceAction(ai, "emalon the storm watcher"); }
|
|
||||||
static Action* koralon_fire_resistance_action(PlayerbotAI* ai) { return new BossFireResistanceAction(ai, "koralon the flame watcher"); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -24,16 +24,4 @@ void RaidVoAStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(new TriggerNode(
|
triggers.push_back(new TriggerNode(
|
||||||
"emalon fall from floor trigger",
|
"emalon fall from floor trigger",
|
||||||
NextAction::array(0, new NextAction("emalon fall from floor action", ACTION_RAID), nullptr)));
|
NextAction::array(0, new NextAction("emalon fall from floor action", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"emalon nature resistance trigger",
|
|
||||||
NextAction::array(0, new NextAction("emalon nature resistance action", ACTION_RAID), nullptr)));
|
|
||||||
|
|
||||||
//
|
|
||||||
// Koralon the Flame Watcher
|
|
||||||
//
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
|
||||||
"koralon fire resistance trigger",
|
|
||||||
NextAction::array(0, new NextAction("koralon fire resistance action", ACTION_RAID), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ public:
|
|||||||
creators["emalon lighting nova trigger"] = &RaidVoATriggerContext::emalon_lighting_nova_trigger;
|
creators["emalon lighting nova trigger"] = &RaidVoATriggerContext::emalon_lighting_nova_trigger;
|
||||||
creators["emalon overcharge trigger"] = &RaidVoATriggerContext::emalon_overcharge_trigger;
|
creators["emalon overcharge trigger"] = &RaidVoATriggerContext::emalon_overcharge_trigger;
|
||||||
creators["emalon fall from floor trigger"] = &RaidVoATriggerContext::emalon_fall_from_floor_trigger;
|
creators["emalon fall from floor trigger"] = &RaidVoATriggerContext::emalon_fall_from_floor_trigger;
|
||||||
creators["emalon nature resistance trigger"] = &RaidVoATriggerContext::emalon_nature_resistance_trigger;
|
|
||||||
creators["koralon fire resistance trigger"] = &RaidVoATriggerContext::koralon_fire_resistance_trigger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -28,8 +26,6 @@ private:
|
|||||||
static Trigger* emalon_lighting_nova_trigger(PlayerbotAI* ai) { return new EmalonLightingNovaTrigger(ai); }
|
static Trigger* emalon_lighting_nova_trigger(PlayerbotAI* ai) { return new EmalonLightingNovaTrigger(ai); }
|
||||||
static Trigger* emalon_overcharge_trigger(PlayerbotAI* ai) { return new EmalonOverchargeTrigger(ai); }
|
static Trigger* emalon_overcharge_trigger(PlayerbotAI* ai) { return new EmalonOverchargeTrigger(ai); }
|
||||||
static Trigger* emalon_fall_from_floor_trigger(PlayerbotAI* ai) { return new EmalonFallFromFloorTrigger(ai); }
|
static Trigger* emalon_fall_from_floor_trigger(PlayerbotAI* ai) { return new EmalonFallFromFloorTrigger(ai); }
|
||||||
static Trigger* emalon_nature_resistance_trigger(PlayerbotAI* ai) { return new BossNatureResistanceTrigger(ai, "emalon the storm watcher"); }
|
|
||||||
static Trigger* koralon_fire_resistance_trigger(PlayerbotAI* ai) { return new BossFireResistanceTrigger(ai, "koralon the flame watcher"); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1060,13 +1060,6 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector<NewRpgStatus> candidateSta
|
|||||||
probSum += sPlayerbotAIConfig->RpgStatusProbWeight[status];
|
probSum += sPlayerbotAIConfig->RpgStatusProbWeight[status];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Safety check. Default to "rest" if all RPG weights = 0
|
|
||||||
if (availableStatus.empty() || probSum == 0)
|
|
||||||
{
|
|
||||||
botAI->rpgInfo.ChangeToRest();
|
|
||||||
bot->SetStandState(UNIT_STAND_STATE_SIT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
uint32 rand = urand(1, probSum);
|
uint32 rand = urand(1, probSum);
|
||||||
uint32 accumulate = 0;
|
uint32 accumulate = 0;
|
||||||
NewRpgStatus chosenStatus = RPG_STATUS_END;
|
NewRpgStatus chosenStatus = RPG_STATUS_END;
|
||||||
@@ -1225,4 +1218,4 @@ bool NewRpgBaseAction::CheckRpgStatusAvailable(NewRpgStatus status)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -133,8 +133,6 @@ void GenericShamanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
// NextAction("riptide", 26.0f), nullptr)));
|
// NextAction("riptide", 26.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("heroism", NextAction::array(0, new NextAction("heroism", 31.0f), nullptr)));
|
triggers.push_back(new TriggerNode("heroism", NextAction::array(0, new NextAction("heroism", 31.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("bloodlust", NextAction::array(0, new NextAction("bloodlust", 30.0f), nullptr)));
|
triggers.push_back(new TriggerNode("bloodlust", NextAction::array(0, new NextAction("bloodlust", 30.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 65.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShamanBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void ShamanBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
@@ -189,4 +187,4 @@ void ShamanHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
new TriggerNode("medium aoe and healer should attack",
|
new TriggerNode("medium aoe and healer should attack",
|
||||||
NextAction::array(0,
|
NextAction::array(0,
|
||||||
new NextAction("chain lightning", ACTION_DEFAULT + 0.3f), nullptr)));
|
new NextAction("chain lightning", ACTION_DEFAULT + 0.3f), nullptr)));
|
||||||
}
|
}
|
||||||
@@ -49,10 +49,6 @@ void ShamanNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
new TriggerNode("cure disease", NextAction::array(0, new NextAction("cure disease", 31.0f), nullptr)));
|
new TriggerNode("cure disease", NextAction::array(0, new NextAction("cure disease", 31.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("party member cure disease",
|
triggers.push_back(new TriggerNode("party member cure disease",
|
||||||
NextAction::array(0, new NextAction("cure disease on party", 30.0f), nullptr)));
|
NextAction::array(0, new NextAction("cure disease on party", 30.0f), nullptr)));
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(
|
|
||||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 65.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShamanNonCombatStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void ShamanNonCombatStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ bool BossFireResistanceTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
||||||
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if bot is paladin
|
// Check if bot is paladin
|
||||||
@@ -68,7 +68,7 @@ bool BossFrostResistanceTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
||||||
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if bot is paladin
|
// Check if bot is paladin
|
||||||
@@ -121,7 +121,7 @@ bool BossNatureResistanceTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
||||||
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if bot is alive
|
// Check if bot is alive
|
||||||
@@ -176,7 +176,7 @@ bool BossShadowResistanceTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
// Check boss and it is alive
|
// Check boss and it is alive
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
|
||||||
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
|
if (!boss || !boss->IsAlive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if bot is paladin
|
// Check if bot is paladin
|
||||||
|
|||||||
@@ -133,11 +133,9 @@ public:
|
|||||||
creators["calc"] = &ChatTriggerContext::calc;
|
creators["calc"] = &ChatTriggerContext::calc;
|
||||||
creators["qi"] = &ChatTriggerContext::qi;
|
creators["qi"] = &ChatTriggerContext::qi;
|
||||||
creators["wipe"] = &ChatTriggerContext::wipe;
|
creators["wipe"] = &ChatTriggerContext::wipe;
|
||||||
creators["tame"] = &ChatTriggerContext::tame;
|
creators["pet"] = &ChatTriggerContext::pet;
|
||||||
creators["glyphs"] = &ChatTriggerContext::glyphs; // Added for custom Glyphs
|
creators["glyphs"] = &ChatTriggerContext::glyphs; // Added for custom Glyphs
|
||||||
creators["glyph equip"] = &ChatTriggerContext::glyph_equip; // Added for custom Glyphs
|
creators["glyph equip"] = &ChatTriggerContext::glyph_equip; // Added for custom Glyphs
|
||||||
creators["pet"] = &ChatTriggerContext::pet;
|
|
||||||
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
|
||||||
creators["roll"] = &ChatTriggerContext::roll_action;
|
creators["roll"] = &ChatTriggerContext::roll_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,11 +249,9 @@ private:
|
|||||||
static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); }
|
static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); }
|
||||||
static Trigger* qi(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "qi"); }
|
static Trigger* qi(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "qi"); }
|
||||||
static Trigger* wipe(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wipe"); }
|
static Trigger* wipe(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wipe"); }
|
||||||
static Trigger* tame(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "tame"); }
|
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
||||||
static Trigger* glyphs(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "glyphs"); } // Added for custom Glyphs
|
static Trigger* glyphs(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "glyphs"); } // Added for custom Glyphs
|
||||||
static Trigger* glyph_equip(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "glyph equip"); } // Added for custom Glyphs
|
static Trigger* glyph_equip(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "glyph equip"); } // Added for custom Glyphs
|
||||||
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
|
||||||
static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); }
|
|
||||||
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
#include "TemporarySummon.h"
|
#include "TemporarySummon.h"
|
||||||
#include "ThreatMgr.h"
|
#include "ThreatMgr.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "PlayerbotAI.h"
|
|
||||||
#include "Player.h"
|
|
||||||
|
|
||||||
bool LowManaTrigger::IsActive()
|
bool LowManaTrigger::IsActive()
|
||||||
{
|
{
|
||||||
@@ -249,7 +247,7 @@ bool AoeTrigger::IsActive()
|
|||||||
bool NoFoodTrigger::IsActive()
|
bool NoFoodTrigger::IsActive()
|
||||||
{
|
{
|
||||||
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
||||||
if (isRandomBot && botAI->HasCheat(BotCheatMask::food))
|
if (isRandomBot && sPlayerbotAIConfig->freeFood)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured food").empty();
|
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured food").empty();
|
||||||
@@ -258,7 +256,7 @@ bool NoFoodTrigger::IsActive()
|
|||||||
bool NoDrinkTrigger::IsActive()
|
bool NoDrinkTrigger::IsActive()
|
||||||
{
|
{
|
||||||
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
||||||
if (isRandomBot && botAI->HasCheat(BotCheatMask::food))
|
if (isRandomBot && sPlayerbotAIConfig->freeFood)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured water").empty();
|
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured water").empty();
|
||||||
@@ -687,46 +685,3 @@ bool AmmoCountTrigger::IsActive()
|
|||||||
|
|
||||||
return ItemCountTrigger::IsActive();
|
return ItemCountTrigger::IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewPetTrigger::IsActive()
|
|
||||||
{
|
|
||||||
// Get the bot player object from the AI
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
if (!bot)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Try to get the current pet; initialize guardian and GUID to null/empty
|
|
||||||
Pet* pet = bot->GetPet();
|
|
||||||
Guardian* guardian = nullptr;
|
|
||||||
ObjectGuid currentPetGuid = ObjectGuid::Empty;
|
|
||||||
|
|
||||||
// If bot has a pet, get its GUID
|
|
||||||
if (pet)
|
|
||||||
{
|
|
||||||
currentPetGuid = pet->GetGUID();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If no pet, try to get a guardian pet and its GUID
|
|
||||||
guardian = bot->GetGuardianPet();
|
|
||||||
if (guardian)
|
|
||||||
currentPetGuid = guardian->GetGUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current pet or guardian GUID has changed (including becoming empty), reset the trigger state
|
|
||||||
if (currentPetGuid != lastPetGuid)
|
|
||||||
{
|
|
||||||
triggered = false;
|
|
||||||
lastPetGuid = currentPetGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's a valid current pet/guardian (non-empty GUID) and we haven't triggered yet, activate trigger
|
|
||||||
if (currentPetGuid != ObjectGuid::Empty && !triggered)
|
|
||||||
{
|
|
||||||
triggered = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, do not activate
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -943,16 +943,4 @@ public:
|
|||||||
bool IsActive() override { return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); }
|
bool IsActive() override { return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewPetTrigger : public Trigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NewPetTrigger(PlayerbotAI* ai) : Trigger(ai, "new pet"), lastPetGuid(ObjectGuid::Empty), triggered(false) {}
|
|
||||||
|
|
||||||
bool IsActive() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ObjectGuid lastPetGuid;
|
|
||||||
bool triggered;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -214,84 +214,3 @@ bool FarFromMasterTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance);
|
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive)
|
|
||||||
{
|
|
||||||
Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive);
|
|
||||||
return nearestCreature != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TooCloseToPlayerWithDebuffTrigger::TooCloseToPlayerWithDebuff(uint32 spellId, float range)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Player*> debuffedPlayers;
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* player = gref->GetSource();
|
|
||||||
if (player && player->IsAlive() && player->HasAura(spellId))
|
|
||||||
{
|
|
||||||
debuffedPlayers.push_back(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debuffedPlayers.empty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Unit* debuffedPlayer : debuffedPlayers)
|
|
||||||
{
|
|
||||||
float dist = debuffedPlayer->GetExactDist2d(bot->GetPositionX(), bot->GetPositionY());
|
|
||||||
if (dist < range)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TooFarFromPlayerWithAuraTrigger::TooFarFromPlayerWithAura(uint32 spellId, float range, bool selfInclude)
|
|
||||||
{
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Player*> debuffedPlayers;
|
|
||||||
|
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
|
||||||
{
|
|
||||||
Player* player = gref->GetSource();
|
|
||||||
if (player && player->IsAlive() && player->HasAura(spellId) &&
|
|
||||||
(selfInclude || (!selfInclude && player->GetGUID() != bot->GetGUID())))
|
|
||||||
{
|
|
||||||
debuffedPlayers.push_back(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !debuffedPlayers.empty();
|
|
||||||
|
|
||||||
if (debuffedPlayers.empty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Unit* debuffedPlayer : debuffedPlayers)
|
|
||||||
{
|
|
||||||
float dist = debuffedPlayer->GetExactDist2d(bot->GetPositionX(), bot->GetPositionY());
|
|
||||||
if (dist > range)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -121,28 +121,4 @@ public:
|
|||||||
OutOfReactRangeTrigger(PlayerbotAI* botAI) : FarFromMasterTrigger(botAI, "out of react range", 50.0f, 5) {}
|
OutOfReactRangeTrigger(PlayerbotAI* botAI) : FarFromMasterTrigger(botAI, "out of react range", 50.0f, 5) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TooCloseToCreatureTrigger : public Trigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TooCloseToCreatureTrigger(PlayerbotAI* ai) : Trigger(ai, "too close to creature trigger") {}
|
|
||||||
|
|
||||||
bool TooCloseToCreature(uint32 creatureId, float range, bool alive = true);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TooCloseToPlayerWithDebuffTrigger : public Trigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TooCloseToPlayerWithDebuffTrigger(PlayerbotAI* ai) : Trigger(ai, "too cloose to player with debuff trigger") {}
|
|
||||||
|
|
||||||
bool TooCloseToPlayerWithDebuff(uint32 spellId, float range);
|
|
||||||
};
|
|
||||||
|
|
||||||
class TooFarFromPlayerWithAuraTrigger : public Trigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TooFarFromPlayerWithAuraTrigger(PlayerbotAI* ai) : Trigger(ai, "too far from player with aura trigger") {}
|
|
||||||
|
|
||||||
bool TooFarFromPlayerWithAura(uint32 spellId, float range, bool selfInclude = false);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ public:
|
|||||||
creators["do quest status"] = &TriggerContext::do_quest_status;
|
creators["do quest status"] = &TriggerContext::do_quest_status;
|
||||||
creators["travel flight status"] = &TriggerContext::travel_flight_status;
|
creators["travel flight status"] = &TriggerContext::travel_flight_status;
|
||||||
creators["can self resurrect"] = &TriggerContext::can_self_resurrect;
|
creators["can self resurrect"] = &TriggerContext::can_self_resurrect;
|
||||||
creators["new pet"] = &TriggerContext::new_pet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -426,7 +425,6 @@ private:
|
|||||||
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
|
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
|
||||||
static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); }
|
static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); }
|
||||||
static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); }
|
static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); }
|
||||||
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -21,14 +21,6 @@ public:
|
|||||||
|
|
||||||
static int32 GetRtiIndex(std::string const rti);
|
static int32 GetRtiIndex(std::string const rti);
|
||||||
Unit* Calculate() override;
|
Unit* Calculate() override;
|
||||||
static const int8 starIndex = 0;
|
|
||||||
static const int8 circleIndex = 1;
|
|
||||||
static const int8 diamondIndex = 2;
|
|
||||||
static const int8 triangleIndex = 3;
|
|
||||||
static const int8 moonIndex = 4;
|
|
||||||
static const int8 squareIndex = 5;
|
|
||||||
static const int8 crossIndex = 6;
|
|
||||||
static const int8 skullIndex = 7;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string const type;
|
std::string const type;
|
||||||
|
|||||||
@@ -556,7 +556,7 @@ private:
|
|||||||
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
|
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
|
||||||
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
|
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
// Flag for cutom glyphs : true when /w bot glyph equip
|
// Flag for cutom glyphs : true when /w bot glyph equip …
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
static UntypedValue* custom_glyphs(PlayerbotAI* ai)
|
static UntypedValue* custom_glyphs(PlayerbotAI* ai)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
|
|||||||
{
|
{
|
||||||
NonCombatStrategy::InitTriggers(triggers);
|
NonCombatStrategy::InitTriggers(triggers);
|
||||||
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
|
||||||
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("fel domination", 30.0f), nullptr)));
|
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("fel domination", 30.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("no soul shard", NextAction::array(0, new NextAction("create soul shard", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("no soul shard", NextAction::array(0, new NextAction("create soul shard", 60.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("too many soul shards", NextAction::array(0, new NextAction("destroy soul shard", 60.0f), nullptr)));
|
triggers.push_back(new TriggerNode("too many soul shards", NextAction::array(0, new NextAction("destroy soul shard", 60.0f), nullptr)));
|
||||||
|
|||||||
Reference in New Issue
Block a user