Compare commits

..

46 Commits

Author SHA1 Message Date
bash
3a756f5840 Revert "Fixed opening trade window while using DBM or Questie addon (#1363)"
This reverts commit dfa87faf5e.
2025-06-09 00:27:17 +02:00
kadeshar
499a80db14 - Migrated tips for ilvl gear in config (#1364) 2025-06-08 16:25:03 +02:00
kadeshar
dfa87faf5e Fixed opening trade window while using DBM or Questie addon (#1363)
* - Fixed opening trade window while using DBM or Questie addon

* - Added excluded prefixes for trade actions to config

* -Fixed config description
2025-06-08 16:23:51 +02:00
Jelly
d15ec79252 Addresses #1110 - Add a system to blacklist GameObject GUID's (#1365)
* Addresses #1110

* Addresses #1110
2025-06-08 16:22:06 +02:00
kadeshar
cfc8e85706 - Fixed Thorim strategy positioning in phase 2 (#1362) 2025-06-06 22:00:48 +02:00
kadeshar
fc6309c521 - Added Blessing of Sanctuary to paladin health buff strategy (#1345)
- Removed Blessing of Sanctuary from paladin dps buff strategy
- Added health buff strategy as default for protection paladin
2025-06-06 12:10:45 +02:00
Jelly
9986469042 Addresses #1360 (#1361)
* Addresses #1360

* Additional check, If item exists in bags, don't roll.
2025-06-06 08:55:03 +02:00
kadeshar
db9bcb97ba - Added Mimiron Strategy (#1358)
- Fixed nearest npc value
2025-06-06 08:51:28 +02:00
Brian
3c05e47cb2 CMaNGOS Playerbots "lfg" command implemented (#1291)
* CMaNGOS Playerbots "lfg" command implemented

* Remove logging, fix warning, add suggestion

- Remove LOG_INFO's console clutter, since 'lfg' command is working correctly now.
- Warning C26813 fixed for: placeholders["%role"] = (role == BOT_ROLE_TANK ? "tank" : (role == BOT_ROLE_HEALER ? "healer" : "dps"));
- Added suggestion to let bots do autogear & maintenance, so players can instantly start their dungeon or raid activities without manually having to configure the playerbots gear. It could save a lot of time. This is up to discussion for playerbots maintainers.
2025-06-01 15:31:29 +08:00
Revision
89556dafa1 Fix crash when using random levels with a lowered max player level (#1344) 2025-05-27 18:07:13 +02:00
Yunfan Li
b726f3dfcb Active loot roll (#1338) 2025-05-25 21:24:25 +08:00
NoxMax
45f98e52b4 Update playerbots.conf.dist with DisabledWithoutRealPlayer context (#1340)
* Update playerbots.conf.dist with DisabledWithoutRealPlayer context

* Update playerbots.conf.dist
2025-05-25 11:37:58 +02:00
EricksOliveira
f8660bc939 Do not disband alt bots group when master goes away (#702)
* Do not disband alt bots group when master goes away

* This update makes the original PR functionality optional, which prevents alternative bots (random bots) from automatically leaving the group when the master leaves.

* Fix

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-05-25 00:19:11 +02:00
kadeshar
c24b7a7bb3 - Added disabled without real player feature (#1335) 2025-05-24 20:51:44 +08:00
kadeshar
1195e67c97 - Added casting Blessing of Sanctuary on tank bots (#1329) 2025-05-24 13:32:59 +02:00
Alex Dcnh
faee49beaa Update PlayerbotFactory.cpp (#1324)
To correcte the build warning in VS

Commented:
// bot->GetPetStable()->CurrentPet.value();

Added:
auto petGuid = bot->GetPetStable()->CurrentPet.value();
2025-05-24 17:11:49 +08:00
Brian
d87d5a46c7 Typo fix in AcceptBgInvitationAction (#1332) 2025-05-24 17:11:34 +08:00
kadeshar
c34617e133 - Reverted arena join queue changes (#1333) 2025-05-24 17:11:19 +08:00
kadeshar
a4ff66f12a - [Bugfix] Fixed bug with unequip and item in bags (#1327) 2025-05-22 22:10:53 +02:00
kadeshar
e68c5a76c6 - Added Auriaya fall from floor protection (#1318) 2025-05-20 23:24:45 +08:00
Yunfan Li
5910866362 Calculation of the power of items with random properties (#1312)
* Score calculation of item random property

* Equip auto repair on repop

* Item random property calculation

* Random Property calculation
2025-05-20 23:24:33 +08:00
Yunfan Li
c5b185455c Convert PlayerbotsXPRate to RandomBotXPRate (#1313) 2025-05-18 19:34:17 +02:00
kadeshar
e1a8bd66c5 - Fixed Iron Assembly Rune of Power trigger (#1314) 2025-05-18 19:32:31 +02:00
kadeshar
f7bd9ae5c1 - Added skipping messages from AdvancedWotLKCombatLog addon in trade action (#1310) 2025-05-18 19:31:57 +08:00
NoxMax
b5426152fb Expose zone level brackets to user configuration (#1309)
* Make variable name more intuitive

* Make variable name more intuitive

* Make variable name more intuitive and clarify function

* Update playerbots.conf.dist

* Update PlayerbotAIConfig.cpp

* Update PlayerbotAIConfig.h

* Update RandomPlayerbotMgr.cpp

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist
2025-05-18 19:31:32 +08:00
brighton-chi
f3ee83d471 Additional improvements to readability and accessibility of config (#1301)
* Additional improvements to readability and accessibility of config

Additional improvements to readability and accessibility of config

* Update playerbots.conf.dist

Inconsistency improvements.

* Update playerbots.conf.dist

1 more

* Update playerbots.conf.dist

* Update playerbots.conf.dist

Improved explanation of strategy processing

* Revisions to worldbuff comments

---------

Co-authored-by: SaW <swerkhoven@outlook.com>
2025-05-18 11:48:36 +02:00
Revision
b2534691b8 Fixed a number of issues with texts (#1308) 2025-05-18 11:23:20 +08:00
NotCronky
36adb62f2a druid cat and ret pally offheal strat (#1298)
co +offheal to either cat druid or ret pally with disable dps mode and enable offheal where the bots will now focus damage but heal when necessary.
2025-05-17 22:33:55 +08:00
sd3420625
fd99b373c2 Translated Chinese text documents (#1306) 2025-05-17 22:03:01 +08:00
kadeshar
e2b5ab766d - Added Thorim strategy (#1305) 2025-05-17 21:03:17 +08:00
SaW
9641092078 Revert "fix(#1276) Prevent bots from equipping relics intended for other clas…" (#1303)
This reverts commit f365b79e96.
2025-05-16 13:39:04 +02:00
NoxMax
aaad67f7b9 Clarify EnableNewRpgStrategy relation to RandomBotTeleLowerLevel and RandomBotTeleHigherLevel, and making them more intuitive (#1294)
* Make variable name more intuitive

* Make variable name more intuitive

* Make variable name more intuitive and clarify function

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist

* Update playerbots.conf.dist
2025-05-16 01:30:58 +02:00
brighton-chi
7901f5da19 Improve readability and accessibility of config (#1296)
* Improve readability and accessibility of config

* Typo fix
2025-05-15 11:40:09 +02:00
EricksOliveira
4f26a8a09b Fix hunter bot losing dismissed pet during maintenance (#1286)
* Prevents recreating the tamed pet in InitPet() for hunter bots

This update adds a check to prevent the InitPet() method from re-creating a pet for hunter bots that already have a tamed pet (GetPetId() != 0). This prevents pets that have already been trained and have talents set from being lost, especially after login or teleportation. Responsibility for re-summoning an existing pet can be handled separately via LoadPetFromDB.

* Including check at start to avoid creating a new pet if the bot already has one active or saved in the stable (CurrentPet).

Initial check to avoid pet re-creation.

Selection of tamable creatures appropriate to the bot's level.

Creation, configuration and saving of the pet.

Application of visual effects and initialization of talents.

Activation of autocast for non-passive spells.

* Fix

* .
2025-05-14 20:56:50 +08:00
SaW
f17ec36cde Revert "Introduction of the SafeLearn(spellId) lambda that checks if the bot …" (#1289)
This reverts commit 6d24db6999.
2025-05-12 23:46:05 +02:00
Noscopezz
3881940f33 PoS Strategies (#1283)
PoS Strategies
2025-05-11 00:24:09 +02:00
Revision
dc703cc897 Fixed a typo in filename and renamed table for consistency (#1279)
* Fixed a typo in filename and renamed table for consistency

* Added update file to rename the tables

* Drop old tables if they exist and create new ones if they don't exist already
2025-05-10 13:47:22 +02:00
Type1Error
97f582b9b1 Fix bot chest looting exploits in ICC and Halls of Stone (#1280)
- Prevent bots from looting Gunship Armory chest in ICC before the event ends (#1162)
- Fix bots being able to loot Tribunal Cache in Halls of Stone multiple times (#1200)
- Ensure bots do not attempt to loot Deathbringer's Cache in ICC before it is available

Three chests (Gunship Armory, Tribunal Cache, Deathbringer's Cache) were tested and now behave as expected: bots will not approach or attempt to loot chests while they are unlootable, even if players move close to them.

Closes #1162, #1200
2025-05-10 13:45:58 +02:00
Type1Error
95c572bf48 feat(playerbots): staggered taxi take-off for bots (#1281)
* feat(playerbots): staggered taxi take-off for bots

Adds four new configurable settings to playerbots.conf:

- AiPlayerbot.BotTaxiDelayMinMs:   Min random delay before the 1st follower bot clicks the flight-master
- AiPlayerbot.BotTaxiDelayMaxMs:   Upper bound for the overall taxi delay window – larger spreads big raids
- AiPlayerbot.BotTaxiGapMs:        Fixed gap added per group-slot so bots never take off together
- AiPlayerbot.BotTaxiGapJitterMs:  Extra small randomness added to each gap so launches don’t look robotic

These options allow server owners to fine-tune how bots queue up and take off from flight masters, making their behavior appear more natural.

Closes #1017 : Bots use Flight master nearly the same time.

* fixed build errors

Was missing a header and variable declarations.
2025-05-10 13:45:15 +02:00
Not
43b0852438 Update ChatHelper.cpp, Replace sprintf with snprintf (#1278)
Use snprintf instead of sprintf for item color formatting to prevent buffer overflows and to silence warnings in macOS versions.
2025-05-09 22:01:21 +08:00
kadeshar
9ca326c8cb - Added Iron Assembly Rune of Power strategy (#1275) 2025-05-09 12:42:33 +02:00
Type1Error
f365b79e96 fix(#1276) Prevent bots from equipping relics intended for other classes (#1277)
Fixes #1276

This change adds logic to detect if an item is a relic and ensures it can only be equipped by bots of the corresponding class:

- Idols are restricted to druids
- Librams are restricted to paladins
- Sigils are restricted to death knights
- Totems are restricted to shamans

Fixes a bug where bots would attempt to equip relics regardless of class restrictions.

Additionally, this commit resolves two unrelated build warnings found in the same file.
2025-05-08 23:37:33 +02:00
kadeshar
e104c5f8be - Added Kologarn Focused Eyebeam cheat (#1274)
- Added Kologarn Crunch Armor cheat
- Added Kologarn proper targeting and setting rti target
2025-05-07 21:55:56 +02:00
Type1Error
0c6f656236 fix(strategy): Revert InitTriggers to previous implementation (#1272)
Fixes #1169

Reverts TankAssistStrategy and DpsAssistStrategy InitTriggers implementations to their previous versions.
The changes from commit 24efa7e appeared to be optimizations without fixing any documented bugs, but were causing issues.
2025-05-07 21:54:41 +02:00
Noscopezz
4603dbaf35 FoS Improve, ICC LM, LDW, PP improve (#1268)
* ICC PP WIP

WIP

* added mutated plague for PP

* BPC added (kinetic and boss targeting need to be done by player)

OT collects dark nucles, bots spread on vortex and other stuff they do ok on their own.
Tested only on 10NM, should work on 25NM

* Tank pos. correction

* BQL, ranged spread, link, flame, bite, tanking tested 10NM

to do (better fire spread, hc tacti, melee spread when in air)

* LDW improved

improved shadow logic, ranged spread for easier shadow handling

* dbs update, fixed teleporting

Bots should only go and teleport to the mage that is actually below zero now

* DBS ranged fix

Ranged should spread more quickly and freak out less

* Festergut && DBS

fixed ranged spread (both)
fixed spore logic (fester)

* Rotface fix

Improved big ooze tanking (static pos, todo kiting)
ooze explosion spread mechanic fix
ooze pool fix
Player needs to mark rotface with skull icon, oterwise bots try to attack oozes

* BQL fixed for 25nm

todo: better melee logic in air phase, better melee flame spread

* VDW, Sister Svalna, Sindy update

Sister Svalna, bots can pickup spears and throw at svalna when she has shield up

VDW added healer strats to use portal and heal boss (atm druids are for raid healing only, so use druide + any other healer, ideally player should be healer)
todo (focus on supressers, add healer rotations, atm they use quickest spell they can)

Sindragosa
Added tank boss manipulation (boss orientation and position)
bots detect (buffet, unchained magic and chilled to the bone and act accordingly)
bots detect frost beacon move to safe spot and los frost bombs around them, while dpsing tombs (todo stop dps if only one tomb is left, if we have frost bombs around, not a big deal atm since in nm they dont one shot)
Last phase bots los behind tomb to loose buffet, tanks swap when they have hi buffet count.
Player should tell bots with skull rti if they should kill tomb or focus boss.
todo (dynamic tomb los in last phase so that healers can see tank but also hide behind tomb to break los from boss)

Removed some debug messages, improved LM spike action (nearest bots also try to help kill it)

Improved Lady Deathwshiper shade action (only targeted bots will run away instead of every bot that is near it)

dbs improved tank switch

I recommend to use 3 healers (just to be safe) and 2 paladin tanks (warr seems to struggle with agro) in 10 man
25 man 6-7 healers (just to be safe) Since most of the bosses are about survival and not dps

* LK Update (doable)

LK added

Improved tank switching for all bosses

Fixed PP gas cloud kiting
Malleable goo todo (dont know how to detect it since its not object or npc) just summon ranged bots to safe position to bypass

BPC  fixed OT sometimes not tanking kele
kinectic bombs todo (for now player should take care of them)

Sindragosa fixed rare case when she is in air phase but tombs went to last phase position

LK
Bots can handle necrotic
Bots can handle adds
Bots should focus valkyre that actually grabbed someone (if unlucky and player just use attack command and summon bots to you if they are far away from you) if they grab bots you can either summon to make them useless or let bots cc them and do it legit way.

Defile should be watched by player and once it was cast just summon bots to you
Vile spirits for some reason go to the ground and get nuked by bots aoe spells so there is not much to be done

**Player needs to be alive the whole LK fight since you will have to watch out for frost spehers (sometimes bots ignore them), summon bots when defile is up and summon ranged bots if they get stuck near shambling or raging spirits since their aoe will wipe you)

all in all LK  is doable both 10 and 25nm, player needs to have knowledge of lk fight and needs to know how to use multibot addon and make macros for eg summoning or commanding groups of bots or individual bots)

Dont forget frost/shadow/nature resist auras in whole ICC since it will help alot

I have done whole icc 10 and 25 with 2 pala tanks, 2/5 heals and rest dps,  if you use +1 or +2 heals it should be easier (since I was testing I did close to 0 dmg in fights same with heals)

* fixed changes made by mistake

fix

* Malleable fix (simple spread mechanic)

Malleable mechanic added (simple spread for now)
Gas cloud fixed (Bots sometimes got stuck between puddle and kite location)

* Defile Update

Bots detect and avoid defile (they struggle to find a way back to the boss around it tho, use summon to help them)

Melee bots should be able to stand behind/to the flank of shambling/spirits

* GS fixed bots not returning to their ship for A and H

Bots will return back to ship after killing mage

* PP gas cloud kiting improved

PP gas cloud kiting improved

* BPC targeting fixed

Bots will mark valid prince with skull RTI now

* BQL added melee spread in air pahse

BQL added melee spread

* VDW healing rotation improved

Healers will now use strong heals and hots

* Fixed Necrotic Plague

Fixed issue with Necrotic where it would get dispelled too soon, or would not get dispelled at all

* LK Update

Refined defile logic
Added 3 points for ranged and melee in winter phase (east, west, south when facing throne)
fixed frost spheres targeting (hunter will focus them)
Atm bots will reset z axis if they fall underground or if they get teleported by lk
Better positioning in 1st phase

* 10HC update until PP

LK defile improved for 10nm (bots sometimes stood 2 close to defile until it grew few times)

Improved rotface for HC

PP remade for 10HC.
Gas cloud is now properly kited
Fixed a rare case of server crash when there were ooze and gas cloud alive at same time.
Bots will move around puddles according to its size now.
Bots that get unboud plague will simply move away from raid and die thus loosing it from raid.
Volatile ooze improved stacking.
Fixed ranged sometimes glitching thru walls when spreading out from other members.

10HC PP is now doable but its hard without summoning (summoning break gascloud and ooze targets so its easier to do). You need to watch boss so you dont phase 2 soon otherwise you will get 2x ooze and cloud which is almoust always a wipe. If abo is not played near perfection bots will struggle with oozes and gas clouds if they are not slowed on time. Always save energy to slow gas cloud since it will wipe the group if it reach its target.
Bots will sometimes stand in puddle, just command them to move and they will figure out what to do.

todo (proper malleable handling)

* Up until Sindy 10HC

BPC added shadow prison handling, bots stop moving if more than 12 stack, tanks more than 18
Improved spreading logic

VDW
fixed issue where bots in portal wold move at half speed compared to real player

* fixed accidental change

* LK 10HC update

Added Shadow trap logic
(if they stand in it, not a big deal since bots wont get yeeted only players will)
When harvest soul, only player will be in another dimension (you must survive)

**Sindy and LK can be done, but I must dissapoint purist, at my skill level I could not achieve to do HC without using summon or other "cheat" bot functions.

Other bosses are all doable now in 10hc

* ICC fixes GS, PP; BQL, SINDY

Minor fixes, bite action improved

* ICC improve Sindy

Bots will now choose non beacon position based on difficulty, 10/25

* ICC fixed missing A/H buff

Fixed missing ICC buff for A and H

Buff will only be present when logged on and in ICC, once any bot or player leave ICC the buff is gone to prevent abuse.

This will make ICC easier now and with recent DPS update and movement improvement bots will now actully do decent dps and even greater healing.

Ally buff
https://www.wowhead.com/wotlk/spell=73828/strength-of-wrynn

Horde buff
https://www.wowhead.com/wotlk/spell=73822/hellscreams-warsong

* revert last change

revert buff

* ICC improve Rotface

Bots will now mark Rotface with skull icon, which will make them focus boss instead of oozes automatically

* ICC Festergut 10 Man fix

There was a rare case in 10 Man when 2 tanks would get spores which made them both stack at melee spot. Now the code will check if any of the spored player is main tank, if it is, it will stay at melee and other spores will go at ranged spot since off tank doesn't really need to stand near main tank all the time.

* ICC BPC major update, fix and improve

Fixed main tank sometimes not tanking both bosses (vala and talda)

Improved marking of current prince

Empowered vortex: bots will now spread out when it is being cast, instead of always spreading(ranged). This will make melee also spread better now since bots will calculate and move to optimal positions.

Added Kinetic bomb handling.
Hunters will take care of bombs, if no hunter is present then any ranged dps will take care of kinetic bombs.

* ICC BQL/VDW major update + minor fixes/improvements

LDW improved spreading for ranged, tanks will move boss in the middle of the room now when shield is depleted which will make bots bug less around pillars

GS Assist tank will not teleport to enemy ship and will keep tanking adds on our ship now

DBS ranged spread improved (they will calcualte spot for each bot and stick with it reducing movment to minimum), fixed bug where in 25 man ranged bots would go behind walls, making them unable to dps boss, improved rune of blood tanking

Festergut ranged spread improved (they will calcualte spot for each bot and stick with it reducing movment to minimum)

BQL Melee spread improved, bite logic improved, added swarming shadows logic (not perfect but at least it wont be all over the room anymore), Tanks will properly handle blood mirror now

VDW boss and raid healers will be automatically assinged depening by number of healers(if more than 3 healers in group, 2 will focus on raid and others will heal boss, otherwise one healer will heal raid). Druid will be assigned to raid healing if no druid present then some other random heal class. Added rotations for druid healers if they end up healing the boss. Raid healers will not use portals anymore. Healers will come to the ground now after using portals (they were stuck in air)

* ICC LK minor update

PP

Removed pre stacking for ranged when volatile ooze spawn (they will kill it much faster now and only stack if someone actually gets targeted by ooze)

LK

Hunters will use trnaq shot to remove enrage from shamblings

Improved valkyr cc

Minimized ping-ponging during winter phase

* ICC minor Sindy improve

Reduced position tolerances and forced movement for beacon and non beacon positions which will make bots move to spot that they actually need to be at instead of randomly running to sindragosa or beaconed players.

* ICC minor update

GS

Bots will mark mage with skull rti

Rotface

Fixed bots glitching thru walls and floors (added check if position is valid before moving)

PP

Bots will mark volatile ooze with skull rti now which will help them focus it and kill asap (usefull for heroic when both volatile ooze and gas cloud are present at the same time)

VDW

Added default group position in the middle if the room so that bots don't spread out too much which will force them to focus supressers more

Fixed Boss healers not keeping themself alive when low on HP

* ICC LK minor update

Commented out z axis bypass since it was fixed with recent core updates.

Bots no longer fall thru buggy platforms so its no longer needed which in return makes valkyrs works as they should.

* ICC LM & LDW Improved

LM

Removed Attack Action since it was buggy and replaced it with RTI. (Improved DPS)

Simplified trigger for spike action, since attack logic is handled by skull RTI now.

Bots (tanks) will mark spikes with skull RTI, which will make bots instantly switch to Spike targets. If no spike is present, the boss will be marked.

Added logic that will make non-tank bots move away from LM to a fixed position behind LM if they happened to be in front of LM (No more insta kills with cleave).
If LM is casting Bone Storm, bots will ignore the above logic and do max dps.

LDW

Removed Attack Action since it was buggy and replaced it with RTI. (Improved DPS)

Bots (tanks) will mark adds with skull RTI, which will make bots instantly switch to adds targets. If no spike is present, the boss will be marked, which will help with keeping allies alive when they get mind controlled by LDW.

Moved 2nd phase position deeper into the room and reduced boss hp trigger to 95%, which will keep tanks where they should be during 1st phase (sometimes the boss dropped below 98% and tanks would ping pong around the room).

Ranged bots will now spread closer to each other now and will ignore spreading if they are 2 far or 2 close to the boss so they can properly reposition.

These changes should also fix recent bugs with bots not casting/standing still on LM and LDW.

* ICC DBS Improve

Replaced attack action with RTI. (Improved DPS)

Ranged bots will move away from blood beast if targeted by it now.

* ICC Sindragosa and LK Improve

Sindragosa

Improved tomb los for frost bombs. Bots will now mark tomb with moon rti to avoid killing it too soon. (If they still attack the tomb, simply reset and after bombs are over, simply attack the tomb to kill or mark with a skull.)
Main Tank will reset mystic buffet stacks now to avoid tank swapping since it's really unreliable which will make sindy less frustrating to kill (other bots will need to do mechanics properly by hiding behind ice tombs).

LK

Necrotic plague action

Bots will now check before moving to the Shambling if they are in front of the Shambling to avoid getting one shoted by shockwave.
Improved multiplier, which will now properly handle dispelling of necrotic plague.

Winter phase action

Bots will properly move behind adds to avoid getting one shoted by shamblings or raging spirits.
If there is a hunter in the grouphunter will focus on spheres; if not, any ranged DPS will focus spheres.

Adds action

Bots will now be in proper positions during the 1st phase, and when the off tank is done collecting shamblings, it will move away from the group.
Bots (non-tanks) will move behind the shambling if they are in front of it to avoid getting one shoted in the 1st phase.
Restored action for checking if bots are at ground level, since they were still sometimes glitching through the floor (valkrys will be able to carry bots and drop them off the edge if not killed in time).
Improved defile calculations for 10 man and 25 man raids since defile was growing differently for 10 and 25, which would make bots in 25 man move very far away from somewhat small defile puddle.
Bots will now properly CC any valkyr that wasn't CC'd, and they won't freak out anymore if multiple valkyrs are present. (They can't tell which valkyr is holding bots/ players, so we should command an attack on valkyrs that are holding someone.)
Bots will now run away from Vile Spirits if they are targeted by them during the last phase.

* ICC 25HC ldw shades fix

Fixed bots could not detect shade if there were multiple present

* ICC trigger optimization

Optimized trigger and action for:

KineticBomb
OozePuddle

* ICC LK Improve

Improved Shadow Trap, Winter phase and 1st phase.

Bots will now have fixed positions for 1st phase if no shadow trap is present.

Tanks will now take care of taunting adds during winter phase. (Assist tank will collect adds and bring them to main tank, main tank will take over the adds so we have minimal chance of adds rotating towards the raid)

Fixed shadow trap detection.

* ToC dungeon update

Added TOC dungeon strategies for 1st encounter.

Other encounters are pretty easy and straightforward so they do not require strategies. They can be all done with follow, reset, summon, attack, skull icon etc...

Bots (A/H) will automatically check if they have lance equiped or in inventory. If they don't have lance they will pick it up from nearest lance stand and equip it.

Bots will mount nearest mount if they have lance equipped.

Once mounted they will keep Defend aura at 3 stacks (top priority).
In fight they will cast charge if more than 5f away, they will cast shield break if target has Defended aura active and they will use Thrust if they cant use anything else.
In short they will melt any target and enemies wont stand a chance keeping their defend aura up.

Once champions are defeated bot will automatically equip best weapon they have in inventory (weapon that was swapped with lance)

* ICC minor update

LM

Bots will now move in smaller increments to get behind boss
They will still move towards fixed position, but as soon they are not in front of boss, they will stop moving and do other actions.

GS

Fixed cannon trigger, now will return true if cannon is 100f from bots.
It used to return true all the time.

* ToC added Eadric strat

bots will look away during radiance cast

* ICC LK minor fix

fixed crash in icclichkingsaddsaction

* ICC GS fix

While encountering edge case when playing horde, the logic was to detect mages, there are usually 2 around, but the longer the fight last the bigger the chance that cannons would kill all the mages thus once we kill the mage that was casting deep freeze bots could not execute action that would return them back to the ship, so the boss had the time to nuke them one by one.
Even tanks have hard time surviving the boss.
I have changed it now (both A/H) so that we keep track of the enemy boss (which is hard if not impossible to kill as main trigger) and we are keeping track of the  mage that is actually casting deep freeze, we mark it with skull, and now as soon the mage is dead, bots will remove the mark so there is 0 confusion about what to do next on their side, and they will come back asap.

* FoS Improved and fixed, ICC LM, LDW and PP updates

FoS

Improved/fixed triggers, Improved/fixed actions.

Bronjahm

Tank will properly kite soul fragment around the room
Replaced attack action with skull rti to focus soul fragments
Non-tank bots will only try to stay within 10f of boss only once we enter last phase, tank will be in the middle of the room if it is not kiting soul fragment

Devourer of Souls

DPS bots will stop dps if boss is casting mirrored soul
Laser can be easily avoided with follow/summon

ICC

LM

Tank will only move to tank position if it actually have the agro, it used to move there without agro and few of the bots would get killed since tank was unable to reach bot

LDW

Fixed edge case where tank would be target of shade and it would be unable to move away it from it due to tank position restrictions, now if Tank is targeted by shade it will be able to move and run away from it (in hc this could cause wipes since explosion is aoe + tons of dmg)

PP

When oozes spawn, tank will move towards green ooze spawn to keep raid near green ooze (faster kill) and far away from gas cloud (better chance to kill if abo didn't manage to slow it)
2025-05-06 21:10:16 +02:00
Type1Error
e48c3351d3 fix (#1179): resolve bot stuttering in BGs after player death (#1269)
Fixes #1179

This update fixes several issues related to bot behavior in battlegrounds when the player dies and releases spirit:

- Removed conflicting strategies (+stay / +follow): Bots were being told to follow and stay simultaneously each tick, causing oscillating movement ("stuttering"). The +stay strategy is now skipped in BGs to allow bots to continue objectives normally.

- Disabled follow while in BG: When the player died, bots attempted to return to the player repeatedly, abandoning objective-based movement. PlayerbotAI now sets -follow while in BGs to keep bots engaged with the objectives.

- Prevented dead bots from following ghosts: Previously, dead bots would follow ghost players away from the graveyard and potentially miss Rez. Bots now wait at the spirit healer when dead in BGs.

- Removed redundant strategy changes and messages in ReleaseSpiritAction as they no longer apply in battleground contexts.
2025-05-06 21:09:32 +02:00
993 changed files with 8848 additions and 4568 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
DROP TABLE IF EXISTS `playerbot_account_keys`;
DROP TABLE IF EXISTS `playerbots_account_keys`;
CREATE TABLE `playerbot_account_keys` (
CREATE TABLE `playerbots_account_keys` (
`account_id` INT PRIMARY KEY,
`security_key` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP

View File

@@ -1,6 +1,6 @@
DROP TABLE IF EXISTS `playerbot_account_links`;
DROP TABLE IF EXISTS `playerbots_account_links`;
CREATE TABLE `playerbot_account_links` (
CREATE TABLE `playerbots_account_links` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`account_id` INT NOT NULL,
`linked_account_id` INT NOT NULL,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
DROP TABLE IF EXISTS `playerbot_account_keys`;
CREATE TABLE IF NOT EXISTS `playerbots_account_keys` (
`account_id` INT PRIMARY KEY,
`security_key` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=INNODB DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `playerbot_account_links`;
CREATE TABLE IF NOT EXISTS `playerbots_account_links` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`account_id` INT NOT NULL,
`linked_account_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `account_link` (`account_id`, `linked_account_id`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

View File

@@ -531,7 +531,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20)
{
nonCombatEngine->addStrategy("bstats", false);
nonCombatEngine->addStrategy("bhealth", false);
}
else
{

View File

@@ -305,6 +305,49 @@ ItemIds ChatHelper::parseItems(std::string const text)
return itemIds;
}
ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text)
{
ItemWithRandomProperty res;
size_t itemStart = text.find("Hitem:");
if (itemStart == std::string::npos)
return res;
itemStart += 6;
if (itemStart >= text.length())
return res;
size_t colonPos = text.find(':', itemStart);
if (colonPos == std::string::npos)
return res;
std::string itemIdStr = text.substr(itemStart, colonPos - itemStart);
res.itemId = atoi(itemIdStr.c_str());
std::vector<std::string> params;
size_t currentPos = colonPos + 1;
while (currentPos < text.length()) {
size_t nextColon = text.find(':', currentPos);
if (nextColon == std::string::npos) {
size_t hTag = text.find("|h", currentPos);
if (hTag != std::string::npos) {
params.push_back(text.substr(currentPos, hTag - currentPos));
}
break;
}
params.push_back(text.substr(currentPos, nextColon - currentPos));
currentPos = nextColon + 1;
}
if (params.size() >= 6) {
res.randomPropertyId = atoi(params[5].c_str());
}
return res;
}
std::string const ChatHelper::FormatQuest(Quest const* quest)
{
if (!quest)
@@ -382,7 +425,7 @@ std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
{
char color[32];
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
snprintf(color, sizeof(color), "%x", ItemQualityColors[proto->Quality]);
std::string itemName;
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
@@ -409,7 +452,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
std::string const ChatHelper::FormatQItem(uint32 itemId)
{
char color[32];
sprintf(color, "%x", ItemQualityColors[0]);
snprintf(color, sizeof(color), "%x", ItemQualityColors[0]);
std::ostringstream out;
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"

View File

@@ -25,6 +25,11 @@ struct ItemTemplate;
typedef std::set<uint32> ItemIds;
typedef std::set<uint32> SpellIds;
struct ItemWithRandomProperty {
uint32 itemId{0};
int32 randomPropertyId{0};
};
class ChatHelper : public PlayerbotAIAware
{
public:
@@ -33,6 +38,7 @@ public:
static std::string const formatMoney(uint32 copper);
static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text);
static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text);
uint32 parseSpell(std::string const text);
static std::string parseValue(const std::string& type, const std::string& text);

View File

@@ -85,7 +85,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
bool hasAnyQuestItems = false;
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
{
if (!items || i >= items->size())
break;
@@ -297,6 +297,11 @@ bool LootObject::IsLootPossible(Player* bot)
return false;
}
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
GameObject* go = botAI->GetGameObject(guid);
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
return false;
if (skillId == SKILL_NONE)
return true;

View File

@@ -29,6 +29,7 @@ public:
LootObject() : skillId(0), reqSkillValue(0), reqItem(0) {}
LootObject(Player* bot, ObjectGuid guid);
LootObject(LootObject const& other);
LootObject& operator=(LootObject const& other) = default;
bool IsEmpty() { return !guid; }
bool IsLootPossible(Player* bot);

View File

@@ -19,6 +19,7 @@
#include "CreatureData.h"
#include "EmoteAction.h"
#include "Engine.h"
#include "EventProcessor.h"
#include "ExternalEventHelper.h"
#include "GameObjectData.h"
#include "GameTime.h"
@@ -156,7 +157,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
masterIncomingPacketHandlers.AddHandler(CMSG_AREATRIGGER, "area trigger");
// masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object");
masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
// masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll");
masterIncomingPacketHandlers.AddHandler(CMSG_GOSSIP_HELLO, "gossip hello");
masterIncomingPacketHandlers.AddHandler(CMSG_QUESTGIVER_HELLO, "gossip hello");
masterIncomingPacketHandlers.AddHandler(CMSG_ACTIVATETAXI, "activate taxi");
@@ -796,6 +797,7 @@ bool PlayerbotAI::IsAllowedCommand(std::string const text)
unsecuredCommands.insert("sendmail");
unsecuredCommands.insert("invite");
unsecuredCommands.insert("leave");
unsecuredCommands.insert("lfg");
unsecuredCommands.insert("rpg status");
}
@@ -1423,12 +1425,21 @@ void PlayerbotAI::DoNextAction(bool min)
master = newMaster;
botAI->SetMaster(newMaster);
botAI->ResetStrategies();
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
if (!bot->InBattleground())
{
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
if (botAI->GetMaster() == botAI->GetGroupMaster())
botAI->TellMaster("Hello, I follow you!");
if (botAI->GetMaster() == botAI->GetGroupMaster())
botAI->TellMaster("Hello, I follow you!");
else
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
}
else
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
{
// we're in a battleground, stay with the pack and focus on objective
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
}
}
}
@@ -6270,3 +6281,23 @@ SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls)
}
return SPELLFAMILY_GENERIC;
}
void PlayerbotAI::AddTimedEvent(std::function<void()> callback, uint32 delayMs)
{
class LambdaEvent final : public BasicEvent
{
std::function<void()> _cb;
public:
explicit LambdaEvent(std::function<void()> cb) : _cb(std::move(cb)) {}
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
{
_cb();
return true; // remove after execution
}
};
// Every Player already owns an EventMap called m_Events
bot->m_Events.AddEvent(
new LambdaEvent(std::move(callback)),
bot->m_Events.CalculateTime(delayMs));
}

View File

@@ -590,6 +590,9 @@ public:
NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest;
// Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false);

View File

@@ -82,6 +82,8 @@ bool PlayerbotAIConfig::Initialize()
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
@@ -129,6 +131,7 @@ bool PlayerbotAIConfig::Initialize()
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
allowTrustedAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowTrustedAccountBots", true);
disabledWithoutRealPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.DisabledWithoutRealPlayer", false);
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
@@ -153,7 +156,9 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
randomBotQuestIds);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
disallowedGameObjects);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
@@ -307,11 +312,48 @@ bool PlayerbotAIConfig::Initialize()
summonAtInnkeepersEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.SummonAtInnkeepersEnabled", true);
randomBotMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMinLevel", 1);
randomBotMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotMaxLevel", 80);
if (randomBotMaxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
randomBotMaxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
randomBotLoginAtStartup = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotLoginAtStartup", true);
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 3);
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 1);
randomBotTeleLowerLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleLowerLevel", 1);
randomBotTeleHigherLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleHigherLevel", 3);
openGoSpell = sConfigMgr->GetOption<int32>("AiPlayerbot.OpenGoSpell", 6477);
// Zones for NewRpgStrategy teleportation brackets
std::vector<uint32> zoneIds = {
// Classic WoW - Low-level zones
1, 12, 14, 85, 141, 215, 3430, 3524,
// Classic WoW - Mid-level zones
17, 38, 40, 130, 148, 3433, 3525,
// Classic WoW - High-level zones
10, 11, 44, 267, 331, 400, 406,
// Classic WoW - Higher-level zones
3, 8, 15, 16, 33, 45, 47, 51, 357, 405, 440,
// Classic WoW - Top-level zones
4, 28, 46, 139, 361, 490, 618, 1377,
// The Burning Crusade - Zones
3483, 3518, 3519, 3520, 3521, 3522, 3523, 4080,
// Wrath of the Lich King - Zones
65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4197
};
for (uint32 zoneId : zoneIds)
{
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
if (!value.empty())
{
size_t commaPos = value.find(',');
if (commaPos != std::string::npos)
{
uint32 minLevel = atoi(value.substr(0, commaPos).c_str());
uint32 maxLevel = atoi(value.substr(commaPos + 1).c_str());
zoneBrackets[zoneId] = std::make_pair(minLevel, maxLevel);
}
}
}
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "-threat");
@@ -331,6 +373,12 @@ bool PlayerbotAIConfig::Initialize()
useFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFlyMountAtMinLevel", 60);
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
// stagger bot flightpath takeoff
delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u);
delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u);
gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u);
gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u);
LOG_INFO("server.loading", "Loading TalentSpecs...");
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
@@ -466,6 +514,7 @@ bool PlayerbotAIConfig::Initialize()
equipmentPersistence = sConfigMgr->GetOption<bool>("AiPlayerbot.EquipmentPersistence", false);
equipmentPersistenceLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.EquipmentPersistenceLevel", 80);
groupInvitationPermission = sConfigMgr->GetOption<int32>("AiPlayerbot.GroupInvitationPermission", 1);
keepAltsInGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.KeepAltsInGroup", false);
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
allowSummonWhenMasterIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenMasterIsDead", true);
allowSummonWhenBotIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenBotIsDead", true);
@@ -483,7 +532,7 @@ bool PlayerbotAIConfig::Initialize()
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
autoGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearScoreLimit", 0);
playerbotsXPrate = sConfigMgr->GetOption<float>("AiPlayerbot.PlayerbotsXPRate", 1.0);
randomBotXPRate = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotXPRate", 1.0);
randomBotAllianceRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAllianceRatio", 50);
randomBotHordeRatio = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotHordeRatio", 50);
disableDeathKnightLogin = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableDeathKnightLogin", 0);

View File

@@ -55,6 +55,7 @@ public:
bool IsInPvpProhibitedArea(uint32 id);
bool enabled;
bool disabledWithoutRealPlayer;
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
@@ -71,6 +72,7 @@ public:
float maxAoeAvoidRadius;
std::set<uint32> aoeAvoidSpellWhitelist;
bool tellWhenAvoidAoe;
std::set<uint32> disallowedGameObjects;
uint32 openGoSpell;
bool randomBotAutologin;
@@ -99,6 +101,7 @@ public:
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
bool randomBotJoinLfg;
// chat
@@ -195,6 +198,7 @@ public:
bool randomBotLoginAtStartup;
uint32 randomBotTeleLowerLevel, randomBotTeleHigherLevel;
std::map<uint32, std::pair<uint32, uint32>> zoneBrackets;
bool logInGroupOnly, logValuesPerTick;
bool fleeingEnabled;
bool summonAtInnkeepersEnabled;
@@ -275,7 +279,7 @@ public:
bool randomBotShowCloak;
bool randomBotFixedLevel;
bool disableRandomLevels;
float playerbotsXPrate;
float randomBotXPRate;
uint32 randomBotAllianceRatio;
uint32 randomBotHordeRatio;
bool disableDeathKnightLogin;
@@ -330,6 +334,8 @@ public:
bool equipmentPersistence;
int32 equipmentPersistenceLevel;
int32 groupInvitationPermission;
bool keepAltsInGroup = false;
bool KeepAltsInGroup() const { return keepAltsInGroup; }
bool allowSummonInCombat;
bool allowSummonWhenMasterIsDead;
bool allowSummonWhenBotIsDead;
@@ -348,6 +354,12 @@ public:
uint32 useFlyMountAtMinLevel;
uint32 useFastFlyMountAtMinLevel;
// stagger flightpath takeoff
uint32 delayMin;
uint32 delayMax;
uint32 gapMs;
uint32 gapJitterMs;
std::string const GetTimestampStr();
bool hasLog(std::string const fileName)
{

View File

@@ -157,7 +157,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
{
QueryResult result = PlayerbotsDatabase.Query(
"SELECT 1 FROM playerbot_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
"SELECT 1 FROM playerbots_account_links WHERE account_id = {} AND linked_account_id = {}", accountId, linkedAccountId);
return result != nullptr;
}
@@ -473,11 +473,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
Player* master = botAI->GetMaster();
if (!master)
if (master)
{
// Log a warning to indicate that the master is null
LOG_DEBUG("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue());
return;
ObjectGuid masterGuid = master->GetGUID();
if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid))
master->GetGroup()->ChangeLeader(masterGuid);
}
Group* group = bot->GetGroup();
@@ -496,7 +496,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
break;
}
}
else
// Don't disband alt groups when master goes away
// Controlled by config
if (sPlayerbotAIConfig->KeepAltsInGroup())
{
uint32 account = sCharacterCache->GetCharacterAccountIdByGuid(member);
if (!sPlayerbotAIConfig->IsInRandomAccountList(account))
@@ -1734,7 +1737,7 @@ void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string
// Store the hashed key in the database
PlayerbotsDatabase.Execute(
"REPLACE INTO playerbot_account_keys (account_id, security_key) VALUES ({}, '{}')",
"REPLACE INTO playerbots_account_keys (account_id, security_key) VALUES ({}, '{}')",
accountId, hashedKey.str());
ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully.");
@@ -1752,7 +1755,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
Field* fields = result->Fetch();
uint32 linkedAccountId = fields[0].Get<uint32>();
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbot_account_keys WHERE account_id = {}", linkedAccountId);
result = PlayerbotsDatabase.Query("SELECT security_key FROM playerbots_account_keys WHERE account_id = {}", linkedAccountId);
if (!result)
{
ChatHandler(player->GetSession()).PSendSysMessage("Invalid security key.");
@@ -1778,10 +1781,10 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
uint32 accountId = player->GetSession()->GetAccountId();
PlayerbotsDatabase.Execute(
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
accountId, linkedAccountId);
PlayerbotsDatabase.Execute(
"INSERT IGNORE INTO playerbot_account_links (account_id, linked_account_id) VALUES ({}, {})",
"INSERT IGNORE INTO playerbots_account_links (account_id, linked_account_id) VALUES ({}, {})",
linkedAccountId, accountId);
ChatHandler(player->GetSession()).PSendSysMessage("Account linked successfully.");
@@ -1790,7 +1793,7 @@ void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& a
void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player)
{
uint32 accountId = player->GetSession()->GetAccountId();
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbot_account_links WHERE account_id = {}", accountId);
QueryResult result = PlayerbotsDatabase.Query("SELECT linked_account_id FROM playerbots_account_links WHERE account_id = {}", accountId);
if (!result)
{
@@ -1831,7 +1834,7 @@ void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string&
uint32 linkedAccountId = fields[0].Get<uint32>();
uint32 accountId = player->GetSession()->GetAccountId();
PlayerbotsDatabase.Execute("DELETE FROM playerbot_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_links WHERE (account_id = {} AND linked_account_id = {}) OR (account_id = {} AND linked_account_id = {})",
accountId, linkedAccountId, linkedAccountId, accountId);
ChatHandler(player->GetSession()).PSendSysMessage("Account unlinked successfully.");

View File

@@ -219,9 +219,12 @@ public:
if (!player->GetSession()->IsBot())
return;
if (sPlayerbotAIConfig->playerbotsXPrate != 1.0)
if (!sRandomPlayerbotMgr->IsRandomBot(player))
return;
if (sPlayerbotAIConfig->randomBotXPRate != 1.0)
{
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->playerbotsXPrate));
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
}
}
};

View File

@@ -49,6 +49,7 @@
#include "UpdateTime.h"
#include "World.h"
#include "RandomPlayerbotFactory.h"
#include <WorldSessionMgr.h>
struct GuidClassRaceInfo {
ObjectGuid::LowType guid;
@@ -355,7 +356,43 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
PERF_MON_TOTAL,
onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal");
if (availableBotCount < maxAllowedBotCount)
bool realPlayerIsLogged = false;
if (sPlayerbotAIConfig->disabledWithoutRealPlayer)
{
if (sWorldSessionMgr->GetActiveAndQueuedSessionCount() > 0)
{
RealPlayerLastTimeSeen = time(nullptr);
realPlayerIsLogged = true;
if (DelayLoginBotsTimer == 0)
{
DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay;
}
}
else
{
if (DelayLoginBotsTimer)
{
DelayLoginBotsTimer = 0;
}
if (RealPlayerLastTimeSeen != 0 && onlineBotCount > 0 &&
time(nullptr) > RealPlayerLastTimeSeen + sPlayerbotAIConfig->disabledWithoutRealPlayerLogoutDelay)
{
LogoutAllBots();
LOG_INFO("playerbots",
"Logout all bots due no real player session.");
}
}
if (availableBotCount < maxAllowedBotCount &&
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer)))
{
AddRandomBots();
}
}
else if (availableBotCount < maxAllowedBotCount)
{
AddRandomBots();
}
@@ -391,7 +428,11 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
}
}
uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100;
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0;
uint32 maxNewBots = onlineBotCount < maxAllowedBotCount &&
(sPlayerbotAIConfig->disabledWithoutRealPlayer == false ||
(realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer))
? maxAllowedBotCount - onlineBotCount
: 0;
uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots);
if (!availableBots.empty())
@@ -432,6 +473,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
if (!loginBots)
break;
}
DelayLoginBotsTimer = 0;
}
}
@@ -1563,7 +1606,12 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
// Override with values from config
for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) {
zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second};
}
}
void RandomPlayerbotMgr::PrepareTeleportCache()
@@ -1627,8 +1675,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
uint32 level = (min_level + max_level + 1) / 2;
WorldLocation loc(mapId, x, y, z, 0);
collected_locs++;
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel;
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel; l++)
for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel;
l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel; l++)
{
if (l < 1 || l > maxLevel)
{

View File

@@ -206,6 +206,8 @@ private:
time_t BgCheckTimer;
time_t LfgCheckTimer;
time_t PlayersCheckTimer;
time_t RealPlayerLastTimeSeen = 0;
time_t DelayLoginBotsTimer;
time_t printStatsTimer;
uint32 AddRandomBots();
bool ProcessBot(uint32 bot);

View File

@@ -813,6 +813,10 @@ void PlayerbotFactory::InitPetTalents()
void PlayerbotFactory::InitPet()
{
Pet* pet = bot->GetPet();
if (!pet && bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
return;
if (!pet)
{
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
@@ -861,7 +865,8 @@ void PlayerbotFactory::InitPet()
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
{
bot->GetPetStable()->CurrentPet.value();
auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS
// bot->GetPetStable()->CurrentPet.value();
// bot->GetPetStable()->CurrentPet.reset();
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
@@ -1738,7 +1743,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (incremental && oldItem)
{
float old_score = calculator.CalculateItem(oldItem->GetEntry());
float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId());
if (bestScoreForSlot < 1.2f * old_score)
continue;
}
@@ -2200,33 +2205,15 @@ void PlayerbotFactory::InitSkills()
//uint32 maxValue = level * 5; //not used, line marked for removal.
bot->UpdateSkillsForLevel();
auto SafeLearn = [this](uint32 spellId)
{
if (!bot->HasSpell(spellId))
bot->learnSpell(spellId, false, true); // Avoid duplicate attempts in DB
};
// Define Riding skill according to level
if (bot->GetLevel() >= 70)
bot->SetSkill(SKILL_RIDING, 300, 300, 300);
else if (bot->GetLevel() >= 60)
bot->SetSkill(SKILL_RIDING, 225, 225, 225);
else if (bot->GetLevel() >= 40)
bot->SetSkill(SKILL_RIDING, 150, 150, 150);
else if (bot->GetLevel() >= 20)
bot->SetSkill(SKILL_RIDING, 75, 75, 75);
else
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
// Safe learning of mount spells
bot->SetSkill(SKILL_RIDING, 0, 0, 0);
if (bot->GetLevel() >= sPlayerbotAIConfig->useGroundMountAtMinLevel)
SafeLearn(33388); // Apprentice
bot->learnSpell(33388);
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastGroundMountAtMinLevel)
SafeLearn(33391); // Journeyman
bot->learnSpell(33391);
if (bot->GetLevel() >= sPlayerbotAIConfig->useFlyMountAtMinLevel)
SafeLearn(34090); // Expert
bot->learnSpell(34090);
if (bot->GetLevel() >= sPlayerbotAIConfig->useFastFlyMountAtMinLevel)
SafeLearn(34091); // Artisan
bot->learnSpell(34091);
uint32 skillLevel = bot->GetLevel() < 40 ? 0 : 1;
uint32 dualWieldLevel = bot->GetLevel() < 20 ? 0 : 1;

View File

@@ -3,6 +3,7 @@
#include <cstdint>
#include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "PlayerbotAI.h"
@@ -205,7 +206,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
}
}
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant)
void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount)
{
for (int s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s)
{
@@ -231,6 +232,10 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan
}
case ITEM_ENCHANTMENT_TYPE_STAT:
{
// for item random suffix
if (!enchant_amount)
enchant_amount = default_enchant_amount;
if (!enchant_amount)
{
break;
@@ -250,7 +255,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId)
// trinket
switch (spellId)
{
case 60764: // Totem of Splintering
case 60764: // Totem of Splintering
if (type_ & (CollectorType::SPELL))
return true;
break;
@@ -744,7 +749,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
{
//float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
// float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
int32 basePoints = effectInfo.BasePoints;
int32 randomPoints = int32(effectInfo.DieSides);
@@ -767,7 +772,7 @@ bool StatsCollector::CheckSpellValidation(uint32 spellFamilyName, flag96 spelFal
{
if (PlayerbotAI::Class2SpellFamilyName(cls_) != spellFamilyName)
return false;
bool isHealingSpell = PlayerbotAI::IsHealingSpell(spellFamilyName, spelFalimyFlags);
// strict to healer
if (strict && (type_ & CollectorType::SPELL_HEAL))

View File

@@ -66,7 +66,7 @@ public:
void Reset();
void CollectItemStats(ItemTemplate const* proto);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0);
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);

View File

@@ -9,6 +9,7 @@
#include "AiFactory.h"
#include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "PlayerbotAI.h"
@@ -59,7 +60,7 @@ void StatsWeightCalculator::Reset()
}
}
float StatsWeightCalculator::CalculateItem(uint32 itemId)
float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds)
{
ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId);
@@ -69,6 +70,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
Reset();
collector_->CollectItemStats(proto);
if (randomPropertyIds != 0)
CalculateRandomProperty(randomPropertyIds, itemId);
if (enable_overflow_penalty_)
ApplyOverflowPenalty(player_);
@@ -116,6 +120,53 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
return weight_;
}
void StatsWeightCalculator::CalculateRandomProperty(int32 randomPropertyId, uint32 itemId)
{
if (randomPropertyId > 0)
{
ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId);
if (!item_rand)
{
return;
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
{
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
if (enchant)
collector_->CollectEnchantStats(enchant);
}
}
else
{
ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropertyId);
if (!item_rand)
{
return;
}
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i)
{
uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0];
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
uint32 enchant_amount = 0;
for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k)
{
if (item_rand->Enchantment[k] == enchantId)
{
enchant_amount = uint32((item_rand->AllocationPct[k] * GenerateEnchSuffixFactor(itemId)) / 10000);
break;
}
}
if (enchant)
collector_->CollectEnchantStats(enchant, enchant_amount);
}
}
}
void StatsWeightCalculator::GenerateWeights(Player* player)
{
GenerateBasicWeights(player);
@@ -293,8 +344,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
}
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
@@ -303,7 +354,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
}
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
{
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
@@ -464,9 +515,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
// {
// weight_ *= 1.0;
// }
// double hand
if (proto->Class == ITEM_CLASS_WEAPON)
{
// double hand
bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON &&
!(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) &&
!(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass));
@@ -474,29 +525,41 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
if (isDoubleHand)
{
weight_ *= 0.5;
}
// spec without double hand
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (isDoubleHand &&
((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
{
weight_ *= 0.1;
// spec without double hand
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
{
weight_ *= 0.1;
}
}
// spec with double hand
// fury without duel wield, arms, bear, retribution, blood dk
if (!isDoubleHand &&
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
if (!isDoubleHand)
{
weight_ *= 0.1;
if ((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))
{
weight_ *= 0.1;
}
// caster's main hand (cannot duel weapon but can equip two-hands stuff)
if (cls == CLASS_MAGE ||
cls == CLASS_PRIEST ||
cls == CLASS_WARLOCK ||
cls == CLASS_DRUID ||
(cls == CLASS_SHAMAN && !player_->CanDualWield()))
{
weight_ *= 0.65;
}
}
// fury with titan's grip
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
@@ -505,15 +568,18 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{
weight_ *= 0.1;
}
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
{
weight_ *= 0.1;
}
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
{
weight_ *= 0.5;
}
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE))
{
@@ -559,12 +625,13 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
if (hitOverflowType_ & CollectorType::SPELL)
{
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
hit_current += player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
hit_current +=
player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176)
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus
hit_current += 3;
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus
hit_current += 3;
hit_overflow = SPELL_HIT_OVERFLOW;
@@ -657,7 +724,7 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
{
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
{
float armor_penetration_current/*, armor_penetration_overflow*/; //not used, line marked for removal.
float armor_penetration_current /*, armor_penetration_overflow*/; // not used, line marked for removal.
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
if (armor_penetration_current > 50)
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;

View File

@@ -28,18 +28,19 @@ class StatsWeightCalculator
public:
StatsWeightCalculator(Player* player);
void Reset();
float CalculateItem(uint32 itemId);
float CalculateItem(uint32 itemId, int32 randomPropertyId = 0);
float CalculateEnchant(uint32 enchantId);
void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; }
void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; }
void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; }
private:
private:
void GenerateWeights(Player* player);
void GenerateBasicWeights(Player* player);
void GenerateAdditionalWeights(Player* player);
void CalculateRandomProperty(int32 randomPropertyId, uint32 itemId);
void CalculateItemSetMod(Player* player, ItemTemplate const* proto);
void CalculateSocketBonus(Player* player, ItemTemplate const* proto);

View File

@@ -17,28 +17,28 @@
#include "ValueContext.h"
#include "WorldPacketActionContext.h"
#include "WorldPacketTriggerContext.h"
#include "RaidStrategyContext.h"
#include "RaidBwlActionContext.h"
#include "RaidBwlTriggerContext.h"
#include "RaidNaxxActionContext.h"
#include "RaidNaxxTriggerContext.h"
#include "RaidIccActionContext.h"
#include "RaidIccTriggerContext.h"
#include "RaidOsActionContext.h"
#include "RaidOsTriggerContext.h"
#include "RaidEoEActionContext.h"
#include "RaidVoATriggerContext.h"
#include "RaidOnyxiaActionContext.h"
#include "RaidOnyxiaTriggerContext.h"
#include "RaidVoAActionContext.h"
#include "RaidEoETriggerContext.h"
#include "RaidMcActionContext.h"
#include "RaidMcTriggerContext.h"
#include "RaidAq20ActionContext.h"
#include "RaidAq20TriggerContext.h"
#include "DungeonStrategyContext.h"
#include "WotlkDungeonActionContext.h"
#include "WotlkDungeonTriggerContext.h"
#include "raids/RaidStrategyContext.h"
#include "raids/blackwinglair/RaidBwlActionContext.h"
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/naxxramas/RaidNaxxActionContext.h"
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/onyxia/RaidOnyxiaActionContext.h"
#include "raids/onyxia/RaidOnyxiaTriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
#include "raids/aq20/RaidAq20TriggerContext.h"
#include "dungeons/DungeonStrategyContext.h"
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
@@ -75,6 +75,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
actionContexts.Add(new WotlkDungeonUPActionContext());
actionContexts.Add(new WotlkDungeonCoSActionContext());
actionContexts.Add(new WotlkDungeonFoSActionContext());
actionContexts.Add(new WotlkDungeonPoSActionContext());
actionContexts.Add(new WotlkDungeonToCActionContext());
triggerContexts.Add(new TriggerContext());
@@ -102,7 +103,8 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
triggerContexts.Add(new WotlkDungeonFosTriggerContext());
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
valueContexts.Add(new ValueContext());

View File

@@ -232,6 +232,20 @@ public:
}
};
class CollectItemsVisitor : public IterateItemsVisitor
{
public:
CollectItemsVisitor() : IterateItemsVisitor() {}
std::vector<Item*> items;
bool Visit(Item* item) override
{
items.push_back(item);
return true;
}
};
class ItemCountByQuality : public IterateItemsVisitor
{
public:

View File

@@ -19,6 +19,7 @@ PassiveMultiplier::PassiveMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "pa
allowedActions.push_back("nc");
allowedActions.push_back("reset botAI");
allowedActions.push_back("check mount state");
allowedActions.push_back("lfg");
}
if (allowedParts.empty())

View File

@@ -13,7 +13,7 @@ class PlayerbotAI;
class AcceptBgInvitationAction : public Action
{
public:
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitatio") {}
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitation") {}
bool Execute(Event event) override;
};

View File

@@ -22,8 +22,7 @@ bool AcceptInvitationAction::Execute(Event event)
std::string name;
packet >> flag >> name;
// Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
Player* inviter = ObjectAccessor::FindPlayerByName(name, true);
Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
if (!inviter)
return false;
@@ -36,11 +35,17 @@ bool AcceptInvitationAction::Execute(Event event)
return false;
}
if (bot->isAFK())
bot->ToggleAFK();
WorldPacket p;
uint32 roles_mask = 0;
p << roles_mask;
bot->GetSession()->HandleGroupAcceptOpcode(p);
if (!bot->GetGroup() || !bot->GetGroup()->IsMember(inviter->GetGUID()))
return false;
if (sRandomPlayerbotMgr->IsRandomBot(bot))
botAI->SetMaster(inviter);
// else

Some files were not shown because too many files have changed in this diff Show More