mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-23 13:36:23 +00:00
Compare commits
44 Commits
f5c84ee7ff
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf456ee07f | ||
|
|
a5bd0b9a41 | ||
|
|
34bab48dd4 | ||
|
|
41c53365ae | ||
|
|
fd07e02a8a | ||
|
|
6cf7f1aaef | ||
|
|
9f54d7e702 | ||
|
|
aeaaee15da | ||
|
|
a1137dbddc | ||
|
|
2eb98c3233 | ||
|
|
29613e29b7 | ||
|
|
e2c203a35e | ||
|
|
1b1ed18a23 | ||
|
|
2ab73c1fd5 | ||
|
|
6b97c379ba | ||
|
|
965d300203 | ||
|
|
dc55ecfd9c | ||
|
|
59d6eb139e | ||
|
|
00171a8c82 | ||
|
|
02e8465a3b | ||
|
|
f53a8704eb | ||
|
|
e5525958c8 | ||
|
|
9ae457d069 | ||
|
|
c9e98a6b4e | ||
|
|
3d9623f119 | ||
|
|
c9cc4324d3 | ||
|
|
b13fb7d12a | ||
|
|
962fdeb3d1 | ||
|
|
83c6977de5 | ||
|
|
686fe513b2 | ||
|
|
61402e83a1 | ||
|
|
b16789fa54 | ||
|
|
8f638b6a66 | ||
|
|
33f5e733dc | ||
|
|
9917863ca1 | ||
|
|
2317652d72 | ||
|
|
1fcd6c5cda | ||
|
|
88016789ba | ||
|
|
6be860c967 | ||
|
|
9971622093 | ||
|
|
895df9b197 | ||
|
|
467b63b840 | ||
|
|
66f5f597bb | ||
|
|
cafbd4681e |
6
.github/workflows/windows_build.yml
vendored
6
.github/workflows/windows_build.yml
vendored
@@ -25,21 +25,25 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||||
ref: 'Playerbot'
|
ref: 'Playerbot'
|
||||||
|
path: 'ac'
|
||||||
- name: Checkout Playerbot Module
|
- name: Checkout Playerbot Module
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: 'mod-playerbots/mod-playerbots'
|
repository: 'mod-playerbots/mod-playerbots'
|
||||||
path: 'modules/mod-playerbots'
|
#path: 'modules/mod-playerbots'
|
||||||
|
path: ac/modules/mod-playerbots
|
||||||
- name: ccache
|
- name: ccache
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||||
- name: Configure OS
|
- name: Configure OS
|
||||||
shell: bash
|
shell: bash
|
||||||
|
working-directory: ac
|
||||||
env:
|
env:
|
||||||
CONTINUOUS_INTEGRATION: true
|
CONTINUOUS_INTEGRATION: true
|
||||||
run: |
|
run: |
|
||||||
./acore.sh install-deps
|
./acore.sh install-deps
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
working-directory: ac
|
||||||
run: |
|
run: |
|
||||||
export CTOOLS_BUILD=all
|
export CTOOLS_BUILD=all
|
||||||
./acore.sh compiler build
|
./acore.sh compiler build
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
# "Randombot": randomly generated bots that log in separately from players and populate the world. Depending on settings, randombots may automatically grind, quest, and upgrade equipment and can be invited to groups and given commands.
|
# "Randombot": randomly generated bots that log in separately from players and populate the world. Randombots may automatically grind, quest, level up, and upgrade equipment and can be invited to groups and given commands.
|
||||||
# "Altbot": characters created on player accounts, which may be logged in by the player and invited to groups and given commands like randombots. Depending on settings, altbots can be limited to characters on the active player's account or in the active player's guild.
|
# "AddClass bot": bots from the AddClassAccountPoolSize accounts. They are used for quickly adding a leveled and geared bot of any class to your party. They are recommended for a quick formation of a party.
|
||||||
|
# "Altbot": characters created on player accounts, which may be logged in by the player and invited to groups and given commands like randombots. They are best suited for long-progression playthroughs.
|
||||||
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands.
|
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands.
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@@ -21,10 +22,11 @@
|
|||||||
# THRESHOLDS
|
# THRESHOLDS
|
||||||
# QUESTS
|
# QUESTS
|
||||||
# COMBAT
|
# COMBAT
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
# CHEATS
|
# CHEATS
|
||||||
# SPELLS
|
# SPELLS
|
||||||
# FLIGHTPATH
|
# FLIGHTPATH
|
||||||
|
# PROFESSIONS
|
||||||
# RANDOMBOT-SPECIFIC SETTINGS
|
# RANDOMBOT-SPECIFIC SETTINGS
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# LEVELS
|
# LEVELS
|
||||||
@@ -44,7 +46,7 @@
|
|||||||
# HUNTER
|
# HUNTER
|
||||||
# ROGUE
|
# ROGUE
|
||||||
# PRIEST
|
# PRIEST
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
# SHAMAN
|
# SHAMAN
|
||||||
# MAGE
|
# MAGE
|
||||||
# WARLOCK
|
# WARLOCK
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# HUNTER
|
# HUNTER
|
||||||
# ROGUE
|
# ROGUE
|
||||||
# PRIEST
|
# PRIEST
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
# SHAMAN
|
# SHAMAN
|
||||||
# MAGE
|
# MAGE
|
||||||
# WARLOCK
|
# WARLOCK
|
||||||
@@ -90,17 +92,20 @@ AiPlayerbot.MinRandomBots = 500
|
|||||||
AiPlayerbot.MaxRandomBots = 500
|
AiPlayerbot.MaxRandomBots = 500
|
||||||
|
|
||||||
# Randombot accounts
|
# Randombot accounts
|
||||||
# If you are not using any expansion at all, you may have to set this manually, in which case please ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
# If you are not using any expansion at all, you may have to set this manually, in which case please
|
||||||
|
# ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||||
# Default: 0 (automatic)
|
# Default: 0 (automatic)
|
||||||
AiPlayerbot.RandomBotAccountCount = 0
|
AiPlayerbot.RandomBotAccountCount = 0
|
||||||
|
|
||||||
# Delete all randombot accounts
|
# Delete all randombot accounts
|
||||||
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would like to recreate randombots, set the number back to 0 and rerun the Worldserver.
|
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would
|
||||||
|
# like to recreate randombots, set the number back to 0 and rerun the Worldserver.
|
||||||
AiPlayerbot.DeleteRandomBotAccounts = 0
|
AiPlayerbot.DeleteRandomBotAccounts = 0
|
||||||
|
|
||||||
# Disable randombots when no real players are logged in
|
# Disable randombots when no real players are logged in
|
||||||
# Default: 0 (randombots will login when server starts)
|
# Default: 0 (randombots will login when server starts)
|
||||||
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will log out 300 seconds (default) after all real players log out
|
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will
|
||||||
|
# log out 300 seconds (default) after all real players log out
|
||||||
AiPlayerbot.DisabledWithoutRealPlayer = 0
|
AiPlayerbot.DisabledWithoutRealPlayer = 0
|
||||||
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
|
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
|
||||||
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
|
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
|
||||||
@@ -152,7 +157,8 @@ AiPlayerbot.AllowGuildBots = 1
|
|||||||
AiPlayerbot.AllowTrustedAccountBots = 1
|
AiPlayerbot.AllowTrustedAccountBots = 1
|
||||||
|
|
||||||
# Randombots will create guilds with nearby randombots
|
# Randombots will create guilds with nearby randombots
|
||||||
# Note: currently, randombots will not invite more bots after a guild is created (i.e., randombot guilds will have only the 10 initial randombots needed to sign the charter)
|
# Note: currently, randombots will not invite more bots after a guild is created,
|
||||||
|
# meaning randombot guilds will have only the 10 initial randombots needed to sign the charter
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.RandomBotGuildNearby = 0
|
AiPlayerbot.RandomBotGuildNearby = 0
|
||||||
|
|
||||||
@@ -186,7 +192,8 @@ AiPlayerbot.AutoInitOnly = 0
|
|||||||
# Default: 1.0 (same with the player)
|
# Default: 1.0 (same with the player)
|
||||||
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
|
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
|
||||||
|
|
||||||
# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells)
|
# Bot automatically trains spells when talking to trainer
|
||||||
|
# yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells
|
||||||
AiPlayerbot.AutoTrainSpells = yes
|
AiPlayerbot.AutoTrainSpells = yes
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -263,7 +270,7 @@ AiPlayerbot.UseFastFlyMountAtMinLevel = 70
|
|||||||
AiPlayerbot.RandomBotShowHelmet = 1
|
AiPlayerbot.RandomBotShowHelmet = 1
|
||||||
AiPlayerbot.RandomBotShowCloak = 1
|
AiPlayerbot.RandomBotShowCloak = 1
|
||||||
|
|
||||||
# Randombots and altbots automatically equip upgrades (bots will equip any item obtained from looting or a quest if they are sufficient upgrades)
|
# Toggles whether altbots will automatically equip items in their inventory that are sufficient upgrades
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||||
|
|
||||||
@@ -311,7 +318,8 @@ AiPlayerbot.GlobalCooldown = 500
|
|||||||
# Max wait time when moving
|
# Max wait time when moving
|
||||||
AiPlayerbot.MaxWaitForMove = 5000
|
AiPlayerbot.MaxWaitForMove = 5000
|
||||||
|
|
||||||
# Disable use of MoveSplinePath for bot movement, will result in more erratic bot movement but means stun/snare/root/etc will work on bots (they wont reliably work when MoveSplinePath is enabled, though slowing effects still work ok)
|
# Enable/disable use of MoveSplinePath for bot movement
|
||||||
|
# Disabling will result in more erratic movement but is required for stuns, snares, and roots to work on bots
|
||||||
# Default: 0 - MoveSplinePath enabled
|
# Default: 0 - MoveSplinePath enabled
|
||||||
# 1 - MoveSplinePath disabled in BG/Arena only
|
# 1 - MoveSplinePath disabled in BG/Arena only
|
||||||
# 2 - MoveSplinePath disabled everywhere
|
# 2 - MoveSplinePath disabled everywhere
|
||||||
@@ -405,10 +413,11 @@ AiPlayerbot.HighMana = 65
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
# Bots pick their quest rewards (yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple)
|
# Bots pick their quest rewards
|
||||||
|
# yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple
|
||||||
AiPlayerbot.AutoPickReward = yes
|
AiPlayerbot.AutoPickReward = yes
|
||||||
|
|
||||||
# Sync quests with player (Bots will complete quests the moment you hand them in and will not loot quest items.)
|
# Sync quests with player (bots will complete quests the moment you hand them in and will not loot quest items.)
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.SyncQuestWithPlayer = 1
|
AiPlayerbot.SyncQuestWithPlayer = 1
|
||||||
|
|
||||||
@@ -433,7 +442,7 @@ AiPlayerbot.DropObsoleteQuests = 1
|
|||||||
# Auto add dungeon/raid strategies when entering the instance if implemented
|
# Auto add dungeon/raid strategies when entering the instance if implemented
|
||||||
AiPlayerbot.ApplyInstanceStrategies = 1
|
AiPlayerbot.ApplyInstanceStrategies = 1
|
||||||
|
|
||||||
# Enable auto avoid aoe strategy (experimental)
|
# Enable auto avoid aoe strategy
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoAvoidAoe = 1
|
AiPlayerbot.AutoAvoidAoe = 1
|
||||||
|
|
||||||
@@ -460,7 +469,7 @@ AiPlayerbot.FleeingEnabled = 1
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -483,12 +492,13 @@ AiPlayerbot.RPWarningCooldown = 30
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Enable/Disable maintenance command
|
# Enable/Disable maintenance command
|
||||||
# Learn all available spells and skills, refresh consumables, repair, enchant equipment and socket gems if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
# Learn all available spells and skills, assign talent points, refresh consumables, repair, enchant equipment, socket gems, etc.
|
||||||
|
# Applies if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.MaintenanceCommand = 1
|
AiPlayerbot.MaintenanceCommand = 1
|
||||||
|
|
||||||
# Enable/Disable specific maintenance command functionality for alt bots
|
# Enable/Disable specific maintenance command functionality for alt bots
|
||||||
# Disable to prevent players from giving free bags, spells, skill levels etc to their alt bots
|
# Disable to prevent players from giving free bags, spells, skill levels, etc. to their alt bots
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AltMaintenanceAmmo = 1
|
AiPlayerbot.AltMaintenanceAmmo = 1
|
||||||
AiPlayerbot.AltMaintenanceFood = 1
|
AiPlayerbot.AltMaintenanceFood = 1
|
||||||
@@ -500,6 +510,7 @@ AiPlayerbot.AltMaintenanceBags = 1
|
|||||||
AiPlayerbot.AltMaintenanceMounts = 1
|
AiPlayerbot.AltMaintenanceMounts = 1
|
||||||
AiPlayerbot.AltMaintenanceSkills = 1
|
AiPlayerbot.AltMaintenanceSkills = 1
|
||||||
|
|
||||||
|
# "Special Spells" consist of any spells listed in AiPlayerbot.RandomBotSpellIds and Death Gate for Death Knights
|
||||||
AiPlayerbot.AltMaintenanceClassSpells = 1
|
AiPlayerbot.AltMaintenanceClassSpells = 1
|
||||||
AiPlayerbot.AltMaintenanceAvailableSpells = 1
|
AiPlayerbot.AltMaintenanceAvailableSpells = 1
|
||||||
AiPlayerbot.AltMaintenanceSpecialSpells = 1
|
AiPlayerbot.AltMaintenanceSpecialSpells = 1
|
||||||
@@ -514,8 +525,8 @@ AiPlayerbot.AltMaintenanceReputation = 1
|
|||||||
AiPlayerbot.AltMaintenanceAttunementQuests = 1
|
AiPlayerbot.AltMaintenanceAttunementQuests = 1
|
||||||
AiPlayerbot.AltMaintenanceKeyring = 1
|
AiPlayerbot.AltMaintenanceKeyring = 1
|
||||||
|
|
||||||
|
# Enable/Disable autogear command, which automatically upgrades bots' gear
|
||||||
# Enable/Disable autogear command, which automatically upgrades bots' gear; the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
# The quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoGearCommand = 1
|
AiPlayerbot.AutoGearCommand = 1
|
||||||
|
|
||||||
@@ -579,6 +590,30 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
|
|||||||
#
|
#
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# PROFESSIONS
|
||||||
|
# Note: Random bots currently do not get professions
|
||||||
|
#
|
||||||
|
|
||||||
|
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
|
||||||
|
# Default: 1 (Enabled)
|
||||||
|
AiPlayerbot.EnableFishingWithMaster = 1
|
||||||
|
|
||||||
|
# Distance from itself (in yards) that a bot with a master will search for water to fish
|
||||||
|
AiPlayerbot.FishingDistanceFromMaster = 10.0
|
||||||
|
|
||||||
|
# Distance from itself (in yards) that a bot without a master will search for water to fish
|
||||||
|
# Currently not relevant since masterless bots will not fish
|
||||||
|
AiPlayerbot.FishingDistance = 40.0
|
||||||
|
|
||||||
|
# Distance from water (in yards) beyond which a bot will remove the 'master fishing' strategy
|
||||||
|
AiPlayerbot.EndFishingWithMaster = 30.0
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# #
|
# #
|
||||||
# RANDOMBOT-SPECIFIC SETTINGS #
|
# RANDOMBOT-SPECIFIC SETTINGS #
|
||||||
@@ -635,7 +670,7 @@ AiPlayerbot.DisableDeathKnightLogin = 0
|
|||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.LimitTalentsExpansion = 0
|
AiPlayerbot.LimitTalentsExpansion = 0
|
||||||
|
|
||||||
# Configure randombots and addClass bot trading (0: Disabled, 1: Enabled, 2: Only Buy, 3: Only Sell)
|
# Configure randombot trading (0: Disabled, 1: Enabled, 2: Only Buy, 3: Only Sell)
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.EnableRandomBotTrading = 1
|
AiPlayerbot.EnableRandomBotTrading = 1
|
||||||
|
|
||||||
@@ -700,7 +735,7 @@ AiPlayerbot.RandomGearQualityLimit = 3
|
|||||||
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
|
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
|
||||||
# TBC
|
# TBC
|
||||||
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
|
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
|
||||||
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
||||||
# Wotlk
|
# Wotlk
|
||||||
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
|
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
|
||||||
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
|
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
|
||||||
@@ -711,7 +746,8 @@ 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 and socket gems into their equipment with maintenance
|
||||||
|
# If greater than RandomBotMaxlevel, bots will not automatically enchant equipment or socket gems
|
||||||
# Default: 60
|
# Default: 60
|
||||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||||
|
|
||||||
@@ -863,13 +899,15 @@ 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/role-based default strategies.
|
||||||
|
# These rules are processed after the defaults.
|
||||||
# Example: "+threat,-potions"
|
# Example: "+threat,-potions"
|
||||||
AiPlayerbot.RandomBotCombatStrategies = ""
|
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/role-based default strategies.
|
||||||
|
# These rules are processed after the defaults.
|
||||||
AiPlayerbot.CombatStrategies = ""
|
AiPlayerbot.CombatStrategies = ""
|
||||||
AiPlayerbot.NonCombatStrategies = ""
|
AiPlayerbot.NonCombatStrategies = ""
|
||||||
|
|
||||||
@@ -1455,7 +1493,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -1778,7 +1816,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# DEATHKNIGHT
|
# DEATH KNIGHT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN ('no_fishing_pole_error');
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('no_fishing_pole_error');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||||
|
(1736, 'no_fishing_pole_error', "I don't have a Fishing Pole", 0, 0,
|
||||||
|
"낚싯대가 없습니다",
|
||||||
|
"Je n’ai pas de canne à pêche",
|
||||||
|
"Ich habe keine Angelrute",
|
||||||
|
"我沒有釣魚竿",
|
||||||
|
"我没有钓鱼竿",
|
||||||
|
"No tengo una caña de pescar",
|
||||||
|
"No tengo una caña de pescar",
|
||||||
|
"У меня нет удочки");
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('no_fishing_pole_error', 100);
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
#include "OutfitAction.h"
|
#include "OutfitAction.h"
|
||||||
#include "PositionAction.h"
|
#include "PositionAction.h"
|
||||||
#include "DropQuestAction.h"
|
#include "DropQuestAction.h"
|
||||||
#include "RaidNaxxActions.h"
|
|
||||||
#include "RandomBotUpdateAction.h"
|
#include "RandomBotUpdateAction.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
#include "ReleaseSpiritAction.h"
|
#include "ReleaseSpiritAction.h"
|
||||||
@@ -64,6 +63,7 @@
|
|||||||
#include "WorldBuffAction.h"
|
#include "WorldBuffAction.h"
|
||||||
#include "XpGainAction.h"
|
#include "XpGainAction.h"
|
||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
|
#include "FishingAction.h"
|
||||||
#include "CancelChannelAction.h"
|
#include "CancelChannelAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@@ -191,6 +191,11 @@ public:
|
|||||||
creators["buy tabard"] = &ActionContext::buy_tabard;
|
creators["buy tabard"] = &ActionContext::buy_tabard;
|
||||||
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
||||||
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
||||||
|
creators["move near water"] = &ActionContext::move_near_water;
|
||||||
|
creators["go fishing"] = &ActionContext::go_fishing;
|
||||||
|
creators["use fishing bobber"] = &ActionContext::use_fishing_bobber;
|
||||||
|
creators["end master fishing"] = &ActionContext::end_master_fishing;
|
||||||
|
creators["remove bobber strategy"] = &ActionContext::remove_bobber_strategy;
|
||||||
creators["roll"] = &ActionContext::roll_action;
|
creators["roll"] = &ActionContext::roll_action;
|
||||||
creators["cancel channel"] = &ActionContext::cancel_channel;
|
creators["cancel channel"] = &ActionContext::cancel_channel;
|
||||||
|
|
||||||
@@ -380,6 +385,11 @@ private:
|
|||||||
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
||||||
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
||||||
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
||||||
|
static Action* move_near_water(PlayerbotAI* botAI) { return new MoveNearWaterAction(botAI); }
|
||||||
|
static Action* go_fishing(PlayerbotAI* botAI) { return new FishingAction(botAI);}
|
||||||
|
static Action* use_fishing_bobber(PlayerbotAI* botAI) { return new UseBobberAction(botAI);}
|
||||||
|
static Action* end_master_fishing(PlayerbotAI* botAI) { return new EndMasterFishingAction(botAI); }
|
||||||
|
static Action* remove_bobber_strategy(PlayerbotAI* botAI) { return new RemoveBobberStrategyAction(botAI); }
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
|
|
||||||
// BG Tactics
|
// BG Tactics
|
||||||
@@ -49,7 +49,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
|||||||
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
botAI->SetMaster(inviter);
|
botAI->SetMaster(inviter);
|
||||||
// else
|
// else
|
||||||
// sPlayerbotDbStore->Save(botAI);
|
// sPlayerbotRepository->Save(botAI);
|
||||||
|
|
||||||
botAI->ResetStrategies();
|
botAI->ResetStrategies();
|
||||||
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
|
||||||
@@ -59,7 +59,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
|||||||
|
|
||||||
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
||||||
{
|
{
|
||||||
Teleport(inviter, bot);
|
Teleport(inviter, bot, true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,5 @@ bool AcceptResurrectAction::Execute(Event event)
|
|||||||
packet << uint8(1); // accept
|
packet << uint8(1); // accept
|
||||||
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
|
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
|
||||||
|
|
||||||
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
bot->StopMoving();
|
bot->StopMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||||
sServerFacade->SetFacingTo(bot, target);
|
sServerFacade->SetFacingTo(bot, target);
|
||||||
|
|
||||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
@@ -75,36 +75,79 @@ void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out)
|
|||||||
void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out)
|
void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out)
|
||||||
{
|
{
|
||||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||||
|
factory.InitSkills();
|
||||||
factory.InitClassSpells();
|
factory.InitClassSpells();
|
||||||
factory.InitAvailableSpells();
|
factory.InitAvailableSpells();
|
||||||
factory.InitSkills();
|
|
||||||
factory.InitPet();
|
factory.InitPet();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
void AutoMaintenanceOnLevelupAction::LearnQuestSpells(std::ostringstream* out)
|
||||||
{
|
{
|
||||||
// CreatureTemplate const* co = sCreatureStorage.LookupEntry<CreatureTemplate>(id);
|
|
||||||
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates();
|
||||||
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i)
|
||||||
{
|
{
|
||||||
//uint32 questId = i->first; //not used, line marked for removal.
|
|
||||||
Quest const* quest = i->second;
|
Quest const* quest = i->second;
|
||||||
|
|
||||||
if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10)
|
// only process class-specific quests to learn class-related spells, cuz
|
||||||
|
// we don't want all these bunch of entries to be handled!
|
||||||
|
if (!quest->GetRequiredClasses())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->GetLevel() ||
|
// skip quests that are repeatable, too low level, or above bots' level
|
||||||
!bot->SatisfyQuestRace(quest, false))
|
if (quest->IsRepeatable() || quest->GetMinLevel() < 10 || quest->GetMinLevel() > bot->GetLevel())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (quest->GetRewSpellCast() > 0)
|
// skip if bot doesnt satisfy class, race, or skill requirements
|
||||||
|
if (!bot->SatisfyQuestClass(quest, false) || !bot->SatisfyQuestRace(quest, false) ||
|
||||||
|
!bot->SatisfyQuestSkill(quest, false))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// use the same logic and impl from Player::learnQuestRewardedSpells
|
||||||
|
|
||||||
|
int32 spellId = quest->GetRewSpellCast();
|
||||||
|
if (!spellId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (!spellInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// xinef: find effect with learn spell and check if we have this spell
|
||||||
|
bool found = false;
|
||||||
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
{
|
{
|
||||||
LearnSpell(quest->GetRewSpellCast(), out);
|
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL && spellInfo->Effects[i].TriggerSpell &&
|
||||||
|
!bot->HasSpell(spellInfo->Effects[i].TriggerSpell))
|
||||||
|
{
|
||||||
|
// pusywizard: don't re-add profession specialties!
|
||||||
|
if (SpellInfo const* triggeredInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell))
|
||||||
|
if (triggeredInfo->Effects[0].Effect == SPELL_EFFECT_TRADE_SKILL)
|
||||||
|
break; // pussywizard: break and not cast the spell (found is false)
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (quest->GetRewSpell() > 0)
|
|
||||||
|
// xinef: we know the spell, continue
|
||||||
|
if (!found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bot->CastSpell(bot, spellId, true);
|
||||||
|
|
||||||
|
// Check if RewardDisplaySpell is set to output the proper spell learned
|
||||||
|
// after processing quests. Output the original RewardSpell otherwise.
|
||||||
|
uint32 rewSpellId = quest->GetRewSpell();
|
||||||
|
if (rewSpellId)
|
||||||
{
|
{
|
||||||
LearnSpell(quest->GetRewSpell(), out);
|
if (SpellInfo const* rewSpellInfo = sSpellMgr->GetSpellInfo(rewSpellId))
|
||||||
|
{
|
||||||
|
*out << FormatSpell(rewSpellInfo) << ", ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*out << FormatSpell(spellInfo) << ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,39 +164,6 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::LearnSpell(uint32 spellId, std::ostringstream* out)
|
|
||||||
{
|
|
||||||
SpellInfo const* proto = sSpellMgr->GetSpellInfo(spellId);
|
|
||||||
if (!proto)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool learned = false;
|
|
||||||
for (uint8 j = 0; j < 3; ++j)
|
|
||||||
{
|
|
||||||
if (proto->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
|
|
||||||
{
|
|
||||||
uint32 learnedSpell = proto->Effects[j].TriggerSpell;
|
|
||||||
|
|
||||||
if (!bot->HasSpell(learnedSpell))
|
|
||||||
{
|
|
||||||
bot->learnSpell(learnedSpell);
|
|
||||||
*out << FormatSpell(sSpellMgr->GetSpellInfo(learnedSpell)) << ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
learned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!learned)
|
|
||||||
{
|
|
||||||
if (!bot->HasSpell(spellId))
|
|
||||||
{
|
|
||||||
bot->learnSpell(spellId);
|
|
||||||
*out << FormatSpell(proto) << ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
@@ -28,7 +28,6 @@ protected:
|
|||||||
void LearnSpells(std::ostringstream* out);
|
void LearnSpells(std::ostringstream* out);
|
||||||
void LearnTrainerSpells(std::ostringstream* out);
|
void LearnTrainerSpells(std::ostringstream* out);
|
||||||
void LearnQuestSpells(std::ostringstream* out);
|
void LearnQuestSpells(std::ostringstream* out);
|
||||||
void LearnSpell(uint32 spellId, std::ostringstream* out);
|
|
||||||
std::string const FormatSpell(SpellInfo const* sInfo);
|
std::string const FormatSpell(SpellInfo const* sInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -224,42 +224,36 @@ bool BuyAction::Execute(Event event)
|
|||||||
|
|
||||||
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
|
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
|
||||||
{
|
{
|
||||||
uint32 oldCount = AI_VALUE2(uint32, "item count", proto->Name1);
|
if (!tItems || !proto)
|
||||||
|
|
||||||
if (!tItems)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 itemId = proto->ItemId;
|
uint32 itemId = proto->ItemId;
|
||||||
for (uint32 slot = 0; slot < tItems->GetItemCount(); slot++)
|
uint32 oldCount = bot->GetItemCount(itemId, false);
|
||||||
|
|
||||||
|
for (uint32 slot = 0; slot < tItems->GetItemCount(); ++slot)
|
||||||
{
|
{
|
||||||
if (tItems->GetItem(slot)->item == itemId)
|
if (tItems->GetItem(slot)->item != itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 botMoney = bot->GetMoney();
|
||||||
|
if (botAI->HasCheat(BotCheatMask::gold))
|
||||||
|
bot->SetMoney(10000000);
|
||||||
|
|
||||||
|
bot->BuyItemFromVendorSlot(vendorguid, slot, itemId, 1, NULL_BAG, NULL_SLOT);
|
||||||
|
|
||||||
|
if (botAI->HasCheat(BotCheatMask::gold))
|
||||||
|
bot->SetMoney(botMoney);
|
||||||
|
|
||||||
|
uint32 newCount = bot->GetItemCount(itemId, false);
|
||||||
|
if (newCount > oldCount)
|
||||||
{
|
{
|
||||||
uint32 botMoney = bot->GetMoney();
|
std::ostringstream out;
|
||||||
if (botAI->HasCheat(BotCheatMask::gold))
|
out << "Buying " << ChatHelper::FormatItem(proto);
|
||||||
{
|
botAI->TellMaster(out.str());
|
||||||
bot->SetMoney(10000000);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
bot->BuyItemFromVendorSlot(vendorguid, slot, itemId, 1, NULL_BAG, NULL_SLOT);
|
|
||||||
|
|
||||||
if (botAI->HasCheat(BotCheatMask::gold))
|
|
||||||
{
|
|
||||||
bot->SetMoney(botMoney);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldCount <
|
|
||||||
AI_VALUE2(
|
|
||||||
uint32, "item count",
|
|
||||||
proto->Name1)) // BuyItem Always returns false (unless unique) so we have to check the item counts.
|
|
||||||
{
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "Buying " << ChatHelper::FormatItem(proto);
|
|
||||||
botAI->TellMaster(out.str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "ChangeStrategyAction.h"
|
#include "ChangeStrategyAction.h"
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "PlayerbotDbStore.h"
|
#include "PlayerbotRepository.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool ChangeCombatStrategyAction::Execute(Event event)
|
bool ChangeCombatStrategyAction::Execute(Event event)
|
||||||
@@ -24,7 +24,7 @@ bool ChangeCombatStrategyAction::Execute(Event event)
|
|||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
case '~':
|
case '~':
|
||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotRepository->Save(botAI);
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
break;
|
break;
|
||||||
@@ -62,7 +62,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event)
|
|||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
case '~':
|
case '~':
|
||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotRepository->Save(botAI);
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
break;
|
break;
|
||||||
@@ -241,20 +241,6 @@ bool MaxDpsChatShortcutAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NaxxChatShortcutAction::Execute(Event event)
|
|
||||||
{
|
|
||||||
Player* master = GetMaster();
|
|
||||||
if (!master)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
botAI->Reset();
|
|
||||||
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
|
|
||||||
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
|
|
||||||
botAI->TellMasterNoFacing("Add Naxx Strategies!");
|
|
||||||
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BwlChatShortcutAction::Execute(Event event)
|
bool BwlChatShortcutAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
@@ -85,13 +85,6 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NaxxChatShortcutAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
|
|
||||||
virtual bool Execute(Event event);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BwlChatShortcutAction : public Action
|
class BwlChatShortcutAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -78,20 +78,17 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
|
|||||||
if (!trigger->IsActive())
|
if (!trigger->IsActive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NextAction** nextActions = triggerNode->getHandlers();
|
std::vector<NextAction> nextActions = triggerNode->getHandlers();
|
||||||
|
|
||||||
bool isRpg = false;
|
bool isRpg = false;
|
||||||
|
|
||||||
for (int32 i = 0; i < NextAction::size(nextActions); i++)
|
for (NextAction nextAction : nextActions)
|
||||||
{
|
{
|
||||||
NextAction* nextAction = nextActions[i];
|
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
|
||||||
|
|
||||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
|
|
||||||
|
|
||||||
if (dynamic_cast<RpgEnabled*>(action))
|
if (dynamic_cast<RpgEnabled*>(action))
|
||||||
isRpg = true;
|
isRpg = true;
|
||||||
}
|
}
|
||||||
NextAction::destroy(nextActions);
|
|
||||||
|
|
||||||
if (isRpg)
|
if (isRpg)
|
||||||
{
|
{
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "LootObjectStack.h"
|
#include "LootObjectStack.h"
|
||||||
#include "NewRpgStrategy.h"
|
#include "NewRpgStrategy.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
#include "PossibleRpgTargetsValue.h"
|
#include "PossibleRpgTargetsValue.h"
|
||||||
#include "PvpTriggers.h"
|
#include "PvpTriggers.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
@@ -87,9 +88,7 @@ bool DropTargetAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
Spell const* spell = bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL); // Get the current spell being cast by the bot
|
Spell const* spell = bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL); // Get the current spell being cast by the bot
|
||||||
if (spell && spell->m_spellInfo->Id == 75) //Check spell is not nullptr before accessing m_spellInfo
|
if (spell && spell->m_spellInfo->Id == 75) //Check spell is not nullptr before accessing m_spellInfo
|
||||||
{
|
|
||||||
bot->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); // Interrupt Auto Shot
|
bot->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); // Interrupt Auto Shot
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
|
|
||||||
@@ -142,6 +141,23 @@ bool AttackRtiTargetAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
||||||
|
|
||||||
|
// Fallback: if the "rti target" value did not resolve a valid unit yet,
|
||||||
|
// try to resolve the raid icon directly from the group.
|
||||||
|
if (!rtiTarget)
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
std::string const rti = AI_VALUE(std::string, "rti");
|
||||||
|
int32 const index = RtiTargetValue::GetRtiIndex(rti);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
ObjectGuid const guid = group->GetTargetIcon(index);
|
||||||
|
if (!guid.IsEmpty())
|
||||||
|
rtiTarget = botAI->GetUnit(guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
|
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
|
||||||
{
|
{
|
||||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
|
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
|
||||||
@@ -153,9 +169,7 @@ bool AttackRtiTargetAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
botAI->TellError("I dont see my rti attack target");
|
botAI->TellError("I dont see my rti attack target");
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -344,6 +344,27 @@ bool EquipUpgradesAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.GetSource() == "item push result")
|
||||||
|
{
|
||||||
|
WorldPacket p(event.getPacket());
|
||||||
|
p.rpos(0);
|
||||||
|
ObjectGuid playerGuid;
|
||||||
|
uint32 received, created, sendChatMessage, itemSlot, itemId;
|
||||||
|
uint8 bagSlot;
|
||||||
|
|
||||||
|
p >> playerGuid;
|
||||||
|
p >> received;
|
||||||
|
p >> created;
|
||||||
|
p >> sendChatMessage;
|
||||||
|
p >> bagSlot;
|
||||||
|
p >> itemSlot;
|
||||||
|
p >> itemId;
|
||||||
|
|
||||||
|
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CollectItemsVisitor visitor;
|
CollectItemsVisitor visitor;
|
||||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||||
|
|
||||||
510
src/Ai/Base/Actions/FishingAction.cpp
Normal file
510
src/Ai/Base/Actions/FishingAction.cpp
Normal file
@@ -0,0 +1,510 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FishingAction.h"
|
||||||
|
#include "FishValues.h"
|
||||||
|
#include "Event.h"
|
||||||
|
|
||||||
|
#include "GridNotifiers.h"
|
||||||
|
#include "GridNotifiersImpl.h"
|
||||||
|
#include "ItemPackets.h"
|
||||||
|
#include "LastMovementValue.h"
|
||||||
|
#include "Map.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Position.h"
|
||||||
|
|
||||||
|
uint32 const FISHING_SPELL = 7620;
|
||||||
|
uint32 const FISHING_POLE = 6256;
|
||||||
|
uint32 const FISHING_BOBBER = 35591;
|
||||||
|
float const MIN_DISTANCE_TO_WATER = 10.0f; // Minimum spell distance
|
||||||
|
float const MAX_DISTANCE_TO_WATER = 20.0f; // Maximum spell distance
|
||||||
|
float const HEIGHT_ABOVE_WATER_TOLERANCE = 1.0f; // Can stand in up to 1 unit of water and still fish.
|
||||||
|
float const SEARCH_INCREMENT = 2.5f;
|
||||||
|
float const HEIGHT_SEARCH_BUFFER = 10.0f; // Height buffer to prevent potentially missing the model the bot is standing on.
|
||||||
|
float const SEARCH_LAND_BUFFER = 0.5f;
|
||||||
|
uint32 const FISHING_LOCATION_TIMEOUT = 180000; //Three minutes
|
||||||
|
|
||||||
|
static bool IsFishingPole(Item* const item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
const ItemTemplate* proto = item->GetTemplate();
|
||||||
|
return proto && proto->Class == ITEM_CLASS_WEAPON &&
|
||||||
|
proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HasFishableWaterOrLand(float x, float y, float z, Map* map, uint32 phaseMask, bool checkForLand=false)
|
||||||
|
{
|
||||||
|
if (!map)
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
|
||||||
|
LiquidData const& liq = map->GetLiquidData(phaseMask, x, y, z+HEIGHT_ABOVE_WATER_TOLERANCE, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||||
|
float ground = map->GetHeight(phaseMask, x, y, z + HEIGHT_SEARCH_BUFFER, true);
|
||||||
|
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER)
|
||||||
|
{
|
||||||
|
if (checkForLand)
|
||||||
|
return ground;
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
if (checkForLand)
|
||||||
|
{
|
||||||
|
if (ground > liq.Level - HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||||
|
return ground;
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (liq.Level + HEIGHT_ABOVE_WATER_TOLERANCE > ground)
|
||||||
|
{
|
||||||
|
if (abs(liq.DepthLevel) < 0.5f) // too shallow to fish in.
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
return liq.Level;
|
||||||
|
}
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasLosToWater(Player* bot, float wx, float wy, float waterZ)
|
||||||
|
{
|
||||||
|
float z = bot->GetCollisionHeight() + bot->GetPositionZ();
|
||||||
|
return bot->GetMap()->isInLineOfSight(
|
||||||
|
bot->GetPositionX(), bot->GetPositionY(), z,
|
||||||
|
wx, wy, waterZ,
|
||||||
|
bot->GetPhaseMask(),
|
||||||
|
LINEOFSIGHT_ALL_CHECKS,
|
||||||
|
VMAP::ModelIgnoreFlags::Nothing);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindLandFromPosition(PlayerbotAI* botAI, float startDistance, float endDistance, float increment, float orientation, WorldPosition targetPos, float fishingSearchWindow, bool checkLOS = true)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
uint32 phaseMask = bot->GetPhaseMask();
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
|
||||||
|
float targetX = targetPos.GetPositionX();
|
||||||
|
float targetY = targetPos.GetPositionY();
|
||||||
|
float targetZ = targetPos.GetPositionZ();
|
||||||
|
|
||||||
|
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||||
|
{
|
||||||
|
//step backwards from position to bot to find edge of shore.
|
||||||
|
float checkX = targetX - dist * cos(orientation);
|
||||||
|
float checkY = targetY - dist * sin(orientation);
|
||||||
|
|
||||||
|
float groundZ = map->GetHeight(phaseMask, checkX, checkY, targetZ + HEIGHT_SEARCH_BUFFER, true);
|
||||||
|
|
||||||
|
if (groundZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LiquidData const& liq = map->GetLiquidData(phaseMask, checkX, checkY, targetZ, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||||
|
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER || groundZ > liq.DepthLevel + HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||||
|
{
|
||||||
|
if (checkLOS)
|
||||||
|
{
|
||||||
|
bool hasLOS = map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
|
||||||
|
if (!hasLOS)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Add a distance check for the position to prevent the bot from moving out of range to the master.
|
||||||
|
if (master && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return WorldPosition(bot->GetMapId(), checkX, checkY, groundZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WorldPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindLandRadialFromPosition (PlayerbotAI* botAI, WorldPosition targetPos, float startDistance, float endDistance, float increment, float fishingSearchWindow, int angles = 16)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
const int numDirections = angles;
|
||||||
|
std::vector<WorldPosition> boundaryPoints;
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
if (!master)
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
uint32 phaseMask = bot->GetPhaseMask();
|
||||||
|
|
||||||
|
float targetX = targetPos.GetPositionX();
|
||||||
|
float targetY = targetPos.GetPositionY();
|
||||||
|
float targetZ = targetPos.GetPositionZ();
|
||||||
|
|
||||||
|
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numDirections; ++i)
|
||||||
|
{
|
||||||
|
float angle = (2.0f * M_PI * i) / numDirections;
|
||||||
|
float checkX = targetX - cos(angle) * dist;
|
||||||
|
float checkY = targetY - sin(angle) * dist;
|
||||||
|
|
||||||
|
float groundZ = HasFishableWaterOrLand(checkX, checkY, targetZ, map, phaseMask, true);
|
||||||
|
|
||||||
|
if (groundZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, groundZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boundaryPoints.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundaryPoints.empty())
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
if (boundaryPoints.size() == 1)
|
||||||
|
return boundaryPoints[0];
|
||||||
|
|
||||||
|
float minDistance = FLT_MAX;
|
||||||
|
WorldLocation closestPoint = WorldPosition();
|
||||||
|
for (auto const& pos : boundaryPoints)
|
||||||
|
{
|
||||||
|
float distance = bot->GetExactDist2d(&pos);
|
||||||
|
if (distance < minDistance)
|
||||||
|
{
|
||||||
|
minDistance = distance;
|
||||||
|
closestPoint = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS, int numDirections)
|
||||||
|
{
|
||||||
|
std::vector<WorldPosition> boundaryPoints;
|
||||||
|
|
||||||
|
float dist = minDistance;
|
||||||
|
while (dist <= maxDistance)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numDirections; ++i)
|
||||||
|
{
|
||||||
|
float angle = (2.0f * M_PI * i) / numDirections;
|
||||||
|
float checkX = x + cos(angle) * dist;
|
||||||
|
float checkY = y + sin(angle) * dist;
|
||||||
|
|
||||||
|
float waterZ = HasFishableWaterOrLand(checkX, checkY, z, map, phaseMask);
|
||||||
|
|
||||||
|
if (waterZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (checkLOS && !HasLosToWater(bot, checkX, checkY, waterZ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, waterZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boundaryPoints.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
dist += increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundaryPoints.empty())
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
if (boundaryPoints.size() == 1)
|
||||||
|
return boundaryPoints[0];
|
||||||
|
// return the central point in the identified positions in to try to be perpendicular to the shore.
|
||||||
|
return boundaryPoints[boundaryPoints.size() / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindFishingHole(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Player* player = botAI->GetBot();
|
||||||
|
GuidVector gos = PAI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
|
GameObject* nearestFishingHole = nullptr;
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
for (auto const& guid : gos)
|
||||||
|
{
|
||||||
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
|
if (!go)
|
||||||
|
continue;
|
||||||
|
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
|
||||||
|
{
|
||||||
|
float dist = player->GetDistance2d(go);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
nearestFishingHole = go;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearestFishingHole)
|
||||||
|
return WorldPosition(nearestFishingHole->GetMapId(), nearestFishingHole->GetPositionX(), nearestFishingHole->GetPositionY(), nearestFishingHole->GetPositionZ());
|
||||||
|
|
||||||
|
return WorldPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot");
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
return MoveTo(landSpot.GetMapId(), landSpot.GetPositionX(), landSpot.GetPositionY(), landSpot.GetPositionZ());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::isUseful()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE(bool, "can fish"))
|
||||||
|
return false;
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) ||
|
||||||
|
bot->GetExactDist(&pos) < 0.1f;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::isPossible()
|
||||||
|
{
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
float fishingSearchWindow;
|
||||||
|
|
||||||
|
if (master)
|
||||||
|
fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster;
|
||||||
|
else
|
||||||
|
fishingSearchWindow = sPlayerbotAIConfig->fishingDistance;
|
||||||
|
|
||||||
|
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||||
|
|
||||||
|
if (fishingHole.IsValid())
|
||||||
|
{
|
||||||
|
float distance = bot->GetExactDist2d(&fishingHole);
|
||||||
|
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||||
|
// Water spot is in range, and we have LOS to it. Set bot position to fishing spot and do not move
|
||||||
|
if (distance >= MIN_DISTANCE_TO_WATER &&
|
||||||
|
distance <= MAX_DISTANCE_TO_WATER && hasLOS)
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Water spot is out of range, lets look for a spot to move to for the fishing hole.
|
||||||
|
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
|
||||||
|
{
|
||||||
|
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
|
||||||
|
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Can the bot fish from current position?
|
||||||
|
WorldPosition waterAtCurrentPos =
|
||||||
|
FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMap(),
|
||||||
|
bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true);
|
||||||
|
if (waterAtCurrentPos.IsValid())
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot",
|
||||||
|
WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Lets find some water where we can fish.
|
||||||
|
WorldPosition water = FindWaterRadial(
|
||||||
|
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||||
|
bot->GetMap(), bot->GetPhaseMask(),
|
||||||
|
MIN_DISTANCE_TO_WATER,
|
||||||
|
fishingSearchWindow + MAX_DISTANCE_TO_WATER,
|
||||||
|
SEARCH_INCREMENT, false);
|
||||||
|
|
||||||
|
if (!water.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
|
||||||
|
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
|
||||||
|
WorldPosition landSpot =
|
||||||
|
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);
|
||||||
|
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquipFishingPoleAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (!_pole)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
|
eqPacket << _pole->GetGUID() << uint8(EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
|
||||||
|
nicePacket.Read();
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquipFishingPoleAction::isUseful()
|
||||||
|
{
|
||||||
|
Item* mainHand = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
if (IsFishingPole(mainHand))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
||||||
|
{
|
||||||
|
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||||
|
{
|
||||||
|
if (IsFishingPole(item))
|
||||||
|
{
|
||||||
|
_pole = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
|
||||||
|
{
|
||||||
|
if (Bag* pBag = bot->GetBagByPos(bag))
|
||||||
|
{
|
||||||
|
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||||
|
{
|
||||||
|
if (Item* item = pBag->GetItemByPos(j))
|
||||||
|
{
|
||||||
|
if (IsFishingPole(item))
|
||||||
|
{
|
||||||
|
_pole = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
|
{
|
||||||
|
bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
if (!master)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string masterName = master->GetName();
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"no_fishing_pole_error", "I don't have a Fishing Pole",{});
|
||||||
|
botAI->Whisper(text, masterName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FishingAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
WorldPosition target = WorldPosition();
|
||||||
|
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||||
|
if (fishingHole.IsValid())
|
||||||
|
{
|
||||||
|
Position pos = fishingHole;
|
||||||
|
float distance = bot->GetExactDist2d(&pos);
|
||||||
|
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||||
|
if (distance < MAX_DISTANCE_TO_WATER &&
|
||||||
|
distance > MIN_DISTANCE_TO_WATER && hasLOS)
|
||||||
|
target = fishingHole;
|
||||||
|
}
|
||||||
|
if (!target.IsValid())
|
||||||
|
{
|
||||||
|
target = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), bot->GetMap(), bot->GetPhaseMask(),
|
||||||
|
MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true, 32);
|
||||||
|
if (!target.IsValid())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Position pos = target;
|
||||||
|
|
||||||
|
if (!bot->HasInArc(1.0, &pos, 1.0))
|
||||||
|
{
|
||||||
|
float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY());
|
||||||
|
bot->SetOrientation(angle);
|
||||||
|
if (!bot->IsRooted())
|
||||||
|
bot->SendMovementFlagUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
EquipFishingPoleAction equipAction(botAI);
|
||||||
|
if (equipAction.isUseful())
|
||||||
|
return equipAction.Execute(event);
|
||||||
|
|
||||||
|
botAI->CastSpell(FISHING_SPELL, bot);
|
||||||
|
botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FishingAction::isUseful()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE(bool, "can fish"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
|
||||||
|
if (!pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->GetExactDist(&pos) < 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseBobberAction::isUseful()
|
||||||
|
{
|
||||||
|
return AI_VALUE(bool, "can use fishing bobber");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseBobberAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
|
for (auto const& guid : gos)
|
||||||
|
{
|
||||||
|
if (GameObject* go = botAI->GetGameObject(guid))
|
||||||
|
{
|
||||||
|
if (go->GetEntry() != FISHING_BOBBER)
|
||||||
|
continue;
|
||||||
|
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||||
|
continue;
|
||||||
|
if (go->getLootState() == GO_READY)
|
||||||
|
{
|
||||||
|
go->Use(bot);
|
||||||
|
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndMasterFishingAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndMasterFishingAction::isUseful()
|
||||||
|
{
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
if (pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||||
|
bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f);
|
||||||
|
return !nearWater.IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveBobberStrategyAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
71
src/Ai/Base/Actions/FishingAction.h
Normal file
71
src/Ai/Base/Actions/FishingAction.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_FISHINGACTION_H
|
||||||
|
#define _PLAYERBOT_FISHINGACTION_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "Event.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
extern const uint32 FISHING_SPELL;
|
||||||
|
extern const uint32 FISHING_POLE;
|
||||||
|
extern const uint32 FISHING_BOBBER;
|
||||||
|
|
||||||
|
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class FishingAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EquipFishingPoleAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
private:
|
||||||
|
Item* _pole = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoveNearWaterAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
bool isPossible() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseBobberAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EndMasterFishingAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RemoveBobberStrategyAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -97,6 +97,8 @@ bool FollowAction::isUseful()
|
|||||||
|
|
||||||
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
||||||
}
|
}
|
||||||
|
if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT))
|
||||||
|
return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster);
|
||||||
|
|
||||||
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
||||||
}
|
}
|
||||||
@@ -265,11 +265,6 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NextAction** CastSpellAction::getPrerequisites()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("attacker without aura", spell);
|
return context->GetValue<Unit*>("attacker without aura", spell);
|
||||||
@@ -27,7 +27,11 @@ public:
|
|||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
|
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
|
||||||
|
|
||||||
NextAction** getPrerequisites() override;
|
std::vector<NextAction> getPrerequisites() override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string const getSpell() { return spell; }
|
std::string const getSpell() { return spell; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -193,10 +197,12 @@ public:
|
|||||||
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "party member to resurrect"; }
|
std::string const GetTargetName() override { return "party member to resurrect"; }
|
||||||
NextAction** getPrerequisites() override
|
std::vector<NextAction> getPrerequisites() override
|
||||||
{
|
{
|
||||||
return NextAction::merge(NextAction::array(0, new NextAction("reach party member to resurrect"), NULL),
|
return NextAction::merge(
|
||||||
Action::getPrerequisites());
|
{ NextAction("reach party member to resurrect") },
|
||||||
|
Action::getPrerequisites()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user