mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-22 13:06:23 +00:00
Compare commits
12 Commits
02e8465a3b
...
hermensbas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0dc63da67 | ||
|
|
944940bd6f | ||
|
|
9e0250b973 | ||
|
|
29613e29b7 | ||
|
|
e2c203a35e | ||
|
|
1b1ed18a23 | ||
|
|
2ab73c1fd5 | ||
|
|
6b97c379ba | ||
|
|
965d300203 | ||
|
|
dc55ecfd9c | ||
|
|
59d6eb139e | ||
|
|
00171a8c82 |
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
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
# THRESHOLDS
|
# THRESHOLDS
|
||||||
# QUESTS
|
# QUESTS
|
||||||
# COMBAT
|
# COMBAT
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
# CHEATS
|
# CHEATS
|
||||||
# SPELLS
|
# SPELLS
|
||||||
# FLIGHTPATH
|
# FLIGHTPATH
|
||||||
@@ -91,17 +91,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
|
||||||
@@ -153,7 +156,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
|
||||||
|
|
||||||
@@ -187,7 +191,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
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -264,7 +269,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)
|
# Randombots and altbots automatically equip any items in their inventory that are sufficient upgrades
|
||||||
# Default: 1 (enabled)
|
# Default: 1 (enabled)
|
||||||
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||||
|
|
||||||
@@ -312,7 +317,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
|
||||||
@@ -406,10 +412,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
|
||||||
|
|
||||||
@@ -434,7 +441,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
|
||||||
|
|
||||||
@@ -461,7 +468,7 @@ AiPlayerbot.FleeingEnabled = 1
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# PALADIN BUFFS STRATEGIES
|
# GREATER BUFFS STRATEGIES
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -484,12 +491,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
|
||||||
@@ -501,6 +509,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
|
||||||
@@ -515,8 +524,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
|
||||||
|
|
||||||
@@ -582,15 +591,21 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
|
|||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# PROFESSIONS
|
# PROFESSIONS
|
||||||
# Random bots currently do not get professions.
|
# Note: Random bots currently do not get professions
|
||||||
#
|
#
|
||||||
|
|
||||||
# EnableFishingWithMaster automatically adds the 'master fishing' strategy to bots that can fish that can.
|
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
|
||||||
# Default: 1 (Enabled)
|
# Default: 1 (Enabled)
|
||||||
AiPlayerbot.EnableFishingWithMaster = 1
|
AiPlayerbot.EnableFishingWithMaster = 1
|
||||||
#FishingDistance sets how far a bot without a master will search for water, while FishingDistanceFromMaster limits it to a closer range, and overrides the following distance to the same value. EndFishingWithMaster sets the distance from water a bot needs to have to automatically drop the 'master fishing' strategy.
|
|
||||||
|
# Distance from itself (in yards) that a bot with a master will search for water to fish
|
||||||
AiPlayerbot.FishingDistanceFromMaster = 10.0
|
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
|
AiPlayerbot.FishingDistance = 40.0
|
||||||
|
|
||||||
|
# Distance from water (in yards) beyond which a bot will remove the 'master fishing' strategy
|
||||||
AiPlayerbot.EndFishingWithMaster = 30.0
|
AiPlayerbot.EndFishingWithMaster = 30.0
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -719,7 +734,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
|
||||||
@@ -730,7 +745,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
|
||||||
|
|
||||||
@@ -882,13 +898,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 = ""
|
||||||
|
|
||||||
|
|||||||
@@ -3214,6 +3214,12 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
|
if (!botAI)
|
||||||
|
{
|
||||||
|
LOG_ERROR("playerbots", "Player/Bot {} is registered in sRandomPlayerbotMgr playerBots and has no bot AI!", bot->GetName().c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (botAI->AllowActivity())
|
if (botAI->AllowActivity())
|
||||||
++active;
|
++active;
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,11 @@ void PlayerbotFactory::Init()
|
|||||||
uint32 maxStoreSize = sSpellMgr->GetSpellInfoStoreSize();
|
uint32 maxStoreSize = sSpellMgr->GetSpellInfoStoreSize();
|
||||||
for (uint32 id = 1; id < maxStoreSize; ++id)
|
for (uint32 id = 1; id < maxStoreSize; ++id)
|
||||||
{
|
{
|
||||||
if (id == 47181 || id == 50358 || id == 47242 || id == 52639 || id == 47147 || id == 7218) // Test Enchant
|
if (id == 7218 || id == 19927 || id == 44119 || id == 47147 || id == 47181 ||
|
||||||
|
id == 47242 || id == 50358 || id == 52639) // Test Enchants
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (id == 35791 || id == 39405) // Grandfathered TBC Enchants
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (id == 15463 || id == 15490) // Legendary Arcane Amalgamation
|
if (id == 15463 || id == 15490) // Legendary Arcane Amalgamation
|
||||||
@@ -284,17 +288,17 @@ void PlayerbotFactory::Randomize(bool incremental)
|
|||||||
pmo->finish();
|
pmo->finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1");
|
LOG_DEBUG("playerbots", "Initializing skills (step 1)...");
|
||||||
LOG_DEBUG("playerbots", "Initializing spells (step 1)...");
|
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1");
|
||||||
bot->LearnDefaultSkills();
|
bot->LearnDefaultSkills();
|
||||||
InitClassSpells();
|
InitSkills();
|
||||||
InitAvailableSpells();
|
|
||||||
if (pmo)
|
if (pmo)
|
||||||
pmo->finish();
|
pmo->finish();
|
||||||
|
|
||||||
LOG_DEBUG("playerbots", "Initializing skills (step 1)...");
|
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1");
|
||||||
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1");
|
LOG_DEBUG("playerbots", "Initializing spells (step 1)...");
|
||||||
InitSkills();
|
InitClassSpells();
|
||||||
|
InitAvailableSpells();
|
||||||
if (pmo)
|
if (pmo)
|
||||||
pmo->finish();
|
pmo->finish();
|
||||||
|
|
||||||
@@ -502,9 +506,9 @@ void PlayerbotFactory::Refresh()
|
|||||||
InitPotions();
|
InitPotions();
|
||||||
InitPet();
|
InitPet();
|
||||||
InitPetTalents();
|
InitPetTalents();
|
||||||
|
InitSkills();
|
||||||
InitClassSpells();
|
InitClassSpells();
|
||||||
InitAvailableSpells();
|
InitAvailableSpells();
|
||||||
InitSkills();
|
|
||||||
InitReputation();
|
InitReputation();
|
||||||
InitSpecialSpells();
|
InitSpecialSpells();
|
||||||
InitMounts();
|
InitMounts();
|
||||||
@@ -2546,13 +2550,19 @@ void PlayerbotFactory::InitAvailableSpells()
|
|||||||
|
|
||||||
for (auto& spell : trainer->GetSpells())
|
for (auto& spell : trainer->GetSpells())
|
||||||
{
|
{
|
||||||
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
|
// simplified version of Trainer::TeachSpell method
|
||||||
|
|
||||||
|
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
|
||||||
|
if (!trainerSpell)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (spell.IsCastable())
|
if (!trainer->CanTeachSpell(bot, trainerSpell))
|
||||||
bot->CastSpell(bot, spell.SpellId, true);
|
continue;
|
||||||
|
|
||||||
|
if (trainerSpell->IsCastable())
|
||||||
|
bot->CastSpell(bot, trainerSpell->SpellId, true);
|
||||||
else
|
else
|
||||||
bot->learnSpell(spell.SpellId, false);
|
bot->learnSpell(trainerSpell->SpellId, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ void Engine::LogAction(char const* format, ...)
|
|||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
vsprintf(buf, format, ap);
|
vsnprintf(buf, sizeof(buf), format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
lastAction += "|";
|
lastAction += "|";
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -261,7 +261,9 @@ bool MoveNearWaterAction::isUseful()
|
|||||||
return false;
|
return false;
|
||||||
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
WorldPosition pos = fishingSpotValueObject->Get();
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) || pos != bot->GetPosition();
|
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) ||
|
||||||
|
bot->GetExactDist(&pos) < 0.1f;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoveNearWaterAction::isPossible()
|
bool MoveNearWaterAction::isPossible()
|
||||||
@@ -299,6 +301,17 @@ bool MoveNearWaterAction::isPossible()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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.
|
// Lets find some water where we can fish.
|
||||||
WorldPosition water = FindWaterRadial(
|
WorldPosition water = FindWaterRadial(
|
||||||
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||||
@@ -435,10 +448,14 @@ bool FishingAction::isUseful()
|
|||||||
{
|
{
|
||||||
if (!AI_VALUE(bool, "can fish"))
|
if (!AI_VALUE(bool, "can fish"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
WorldPosition pos = fishingSpotValueObject->Get();
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
|
||||||
return pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition();
|
if (!pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return bot->GetExactDist(&pos) < 0.1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UseBobberAction::isUseful()
|
bool UseBobberAction::isUseful()
|
||||||
|
|||||||
@@ -178,9 +178,9 @@ bool MaintenanceAction::Execute(Event event)
|
|||||||
factory.InitTalentsTree(true);
|
factory.InitTalentsTree(true);
|
||||||
factory.InitPet();
|
factory.InitPet();
|
||||||
factory.InitPetTalents();
|
factory.InitPetTalents();
|
||||||
|
factory.InitSkills();
|
||||||
factory.InitClassSpells();
|
factory.InitClassSpells();
|
||||||
factory.InitAvailableSpells();
|
factory.InitAvailableSpells();
|
||||||
factory.InitSkills();
|
|
||||||
factory.InitReputation();
|
factory.InitReputation();
|
||||||
factory.InitSpecialSpells();
|
factory.InitSpecialSpells();
|
||||||
factory.InitMounts();
|
factory.InitMounts();
|
||||||
@@ -221,15 +221,15 @@ bool MaintenanceAction::Execute(Event event)
|
|||||||
if (sPlayerbotAIConfig->altMaintenancePetTalents)
|
if (sPlayerbotAIConfig->altMaintenancePetTalents)
|
||||||
factory.InitPetTalents();
|
factory.InitPetTalents();
|
||||||
|
|
||||||
|
if (sPlayerbotAIConfig->altMaintenanceSkills)
|
||||||
|
factory.InitSkills();
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->altMaintenanceClassSpells)
|
if (sPlayerbotAIConfig->altMaintenanceClassSpells)
|
||||||
factory.InitClassSpells();
|
factory.InitClassSpells();
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->altMaintenanceAvailableSpells)
|
if (sPlayerbotAIConfig->altMaintenanceAvailableSpells)
|
||||||
factory.InitAvailableSpells();
|
factory.InitAvailableSpells();
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->altMaintenanceSkills)
|
|
||||||
factory.InitSkills();
|
|
||||||
|
|
||||||
if (sPlayerbotAIConfig->altMaintenanceReputation)
|
if (sPlayerbotAIConfig->altMaintenanceReputation)
|
||||||
factory.InitReputation();
|
factory.InitReputation();
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
|
|||||||
if (!guid)
|
if (!guid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!bot->IsInWorld())
|
if (!bot->IsInWorld() || bot->IsDuringRemoveFromWorld())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->IsInFlight())
|
if (bot->IsInFlight())
|
||||||
|
|||||||
@@ -11,10 +11,6 @@
|
|||||||
|
|
||||||
bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
|
bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
|
||||||
{
|
{
|
||||||
// Apply parent's filtering first (includes level difference checks)
|
|
||||||
if (!PossibleTargetsValue::AcceptUnit(unit))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool inCannon = botAI->IsInVehicle(false, true);
|
bool inCannon = botAI->IsInVehicle(false, true);
|
||||||
Player* enemy = dynamic_cast<Player*>(unit);
|
Player* enemy = dynamic_cast<Player*>(unit);
|
||||||
if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() &&
|
if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() &&
|
||||||
@@ -23,14 +19,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
|
|||||||
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
|
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
|
||||||
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
|
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
|
||||||
!(enemy->HasSpiritOfRedemptionAura()))
|
!(enemy->HasSpiritOfRedemptionAura()))
|
||||||
{
|
|
||||||
// If with master, only attack if master is PvP flagged
|
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
if (master && !master->IsPvP() && !master->IsFFAPvP())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,32 +16,6 @@
|
|||||||
#include "SpellAuraEffects.h"
|
#include "SpellAuraEffects.h"
|
||||||
#include "SpellMgr.h"
|
#include "SpellMgr.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "AreaDefines.h"
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
// Level difference thresholds for attack probability
|
|
||||||
constexpr int32 EXTREME_LEVEL_DIFF = 5; // Don't attack if enemy is this much higher
|
|
||||||
constexpr int32 HIGH_LEVEL_DIFF = 4; // 25% chance at +/- this difference
|
|
||||||
constexpr int32 MID_LEVEL_DIFF = 3; // 50% chance at +/- this difference
|
|
||||||
constexpr int32 LOW_LEVEL_DIFF = 2; // 75% chance at +/- this difference
|
|
||||||
|
|
||||||
// Cache duration before reconsidering attack decision, and old cache cleanup interval
|
|
||||||
constexpr uint32 ATTACK_DECISION_CACHE_DURATION = 2 * MINUTE;
|
|
||||||
constexpr uint32 ATTACK_DECISION_CACHE_CLEANUP_INTERVAL = 10 * MINUTE;
|
|
||||||
|
|
||||||
// Custom hash function for (botGUID, targetGUID) pairs
|
|
||||||
struct PairGuidHash
|
|
||||||
{
|
|
||||||
std::size_t operator()(const std::pair<ObjectGuid, ObjectGuid>& pair) const
|
|
||||||
{
|
|
||||||
return std::hash<uint64>()(pair.first.GetRawValue()) ^
|
|
||||||
(std::hash<uint64>()(pair.second.GetRawValue()) << 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cache for probability-based attack decisions (Per-bot: non-global)
|
|
||||||
// Map: (botGUID, targetGUID) -> (should attack decision, timestamp)
|
|
||||||
static std::unordered_map<std::pair<ObjectGuid, ObjectGuid>, std::pair<bool, time_t>, PairGuidHash> attackDecisionCache;
|
|
||||||
|
|
||||||
void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
|
void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
|
||||||
{
|
{
|
||||||
@@ -50,117 +24,7 @@ void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
|
|||||||
Cell::VisitObjects(bot, searcher, range);
|
Cell::VisitObjects(bot, searcher, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CleanupAttackDecisionCache()
|
bool PossibleTargetsValue::AcceptUnit(Unit* unit) { return AttackersValue::IsPossibleTarget(unit, bot, range); }
|
||||||
{
|
|
||||||
time_t currentTime = time(nullptr);
|
|
||||||
for (auto it = attackDecisionCache.begin(); it != attackDecisionCache.end();)
|
|
||||||
{
|
|
||||||
if (currentTime - it->second.second >= ATTACK_DECISION_CACHE_DURATION)
|
|
||||||
it = attackDecisionCache.erase(it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PossibleTargetsValue::AcceptUnit(Unit* unit)
|
|
||||||
{
|
|
||||||
// attackDecisionCache cleanup
|
|
||||||
static time_t lastCleanup = 0;
|
|
||||||
time_t currentTime = time(nullptr);
|
|
||||||
if (currentTime - lastCleanup > ATTACK_DECISION_CACHE_CLEANUP_INTERVAL)
|
|
||||||
{
|
|
||||||
CleanupAttackDecisionCache();
|
|
||||||
lastCleanup = currentTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AttackersValue::IsPossibleTarget(unit, bot, range))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Level-based PvP restrictions
|
|
||||||
if (unit->IsPlayer())
|
|
||||||
{
|
|
||||||
// Self-defense - always allow fighting back
|
|
||||||
if (bot->IsInCombat() && bot->GetVictim() == unit)
|
|
||||||
return true; // Already fighting
|
|
||||||
|
|
||||||
Unit* botAttacker = bot->getAttackerForHelper();
|
|
||||||
if (botAttacker)
|
|
||||||
{
|
|
||||||
if (botAttacker == unit)
|
|
||||||
return true; // Enemy attacking
|
|
||||||
|
|
||||||
if (botAttacker->IsPet())
|
|
||||||
{
|
|
||||||
Unit* petOwner = botAttacker->GetOwner();
|
|
||||||
if (petOwner && petOwner == unit)
|
|
||||||
return true; // Enemy's pet attacking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip restrictions in BG/Arena
|
|
||||||
if (bot->InBattleground() || bot->InArena())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Skip restrictions if in duel with this player
|
|
||||||
if (bot->duel && bot->duel->Opponent == unit)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Capital cities - no restrictions
|
|
||||||
uint32 zoneId = bot->GetZoneId();
|
|
||||||
bool inCapitalCity = (zoneId == AREA_STORMWIND_CITY ||
|
|
||||||
zoneId == AREA_IRONFORGE ||
|
|
||||||
zoneId == AREA_DARNASSUS ||
|
|
||||||
zoneId == AREA_THE_EXODAR ||
|
|
||||||
zoneId == AREA_ORGRIMMAR ||
|
|
||||||
zoneId == AREA_THUNDER_BLUFF ||
|
|
||||||
zoneId == AREA_UNDERCITY ||
|
|
||||||
zoneId == AREA_SILVERMOON_CITY);
|
|
||||||
|
|
||||||
if (inCapitalCity)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Level difference check
|
|
||||||
int32 levelDifference = unit->GetLevel() - bot->GetLevel();
|
|
||||||
int32 absLevelDifference = std::abs(levelDifference);
|
|
||||||
|
|
||||||
// Extreme difference - do not attack
|
|
||||||
if (levelDifference >= EXTREME_LEVEL_DIFF)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Calculate attack chance based on level difference
|
|
||||||
uint32 attackChance = 100; // Default 100%: Bot and target's levels are very close
|
|
||||||
|
|
||||||
// There's a chance a bot might gank on an extremly low target
|
|
||||||
if ((absLevelDifference < EXTREME_LEVEL_DIFF && absLevelDifference >= HIGH_LEVEL_DIFF) ||
|
|
||||||
levelDifference <= -EXTREME_LEVEL_DIFF)
|
|
||||||
attackChance = 25;
|
|
||||||
|
|
||||||
else if (absLevelDifference < HIGH_LEVEL_DIFF && absLevelDifference >= MID_LEVEL_DIFF)
|
|
||||||
attackChance = 50;
|
|
||||||
|
|
||||||
else if (absLevelDifference < MID_LEVEL_DIFF && absLevelDifference >= LOW_LEVEL_DIFF)
|
|
||||||
attackChance = 75;
|
|
||||||
|
|
||||||
// If probability check needed, use cache
|
|
||||||
if (attackChance < 100)
|
|
||||||
{
|
|
||||||
std::pair<ObjectGuid, ObjectGuid> cacheKey = std::make_pair(bot->GetGUID(), unit->GetGUID());
|
|
||||||
|
|
||||||
auto it = attackDecisionCache.find(cacheKey);
|
|
||||||
if (it != attackDecisionCache.end())
|
|
||||||
{
|
|
||||||
if (currentTime - it->second.second < ATTACK_DECISION_CACHE_DURATION)
|
|
||||||
return it->second.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldAttack = (urand(1, 100) <= attackChance);
|
|
||||||
attackDecisionCache[cacheKey] = std::make_pair(shouldAttack, currentTime);
|
|
||||||
return shouldAttack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
|
void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
|
||||||
{
|
{
|
||||||
@@ -172,8 +36,9 @@ void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
|
|||||||
bool PossibleTriggersValue::AcceptUnit(Unit* unit)
|
bool PossibleTriggersValue::AcceptUnit(Unit* unit)
|
||||||
{
|
{
|
||||||
if (!unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
|
if (!unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
Unit::AuraEffectList const& aurasPeriodicTriggerSpell =
|
Unit::AuraEffectList const& aurasPeriodicTriggerSpell =
|
||||||
unit->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
unit->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||||
Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell =
|
Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell =
|
||||||
@@ -193,7 +58,9 @@ bool PossibleTriggersValue::AcceptUnit(Unit* unit)
|
|||||||
for (int j = 0; j < MAX_SPELL_EFFECTS; j++)
|
for (int j = 0; j < MAX_SPELL_EFFECTS; j++)
|
||||||
{
|
{
|
||||||
if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
|
if (triggerSpellInfo->Effects[j].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user