Sitelet https://github.com/mod-playerbots/mod-playerbots/pull/2902
Skip to content

feat(arena): Give arena bots PvP talents and rating-scaled gear - #2902

Open
cfraser wants to merge 9 commits into
mod-playerbots:test-stagingfrom
cfraser:feature/pvp-loadout-swap
Open

cfraser wants to merge 9 commits into
mod-playerbots:test-stagingfrom
cfraser:feature/pvp-loadout-swap

Conversation

@cfraser

@cfraser cfraser commented Oct 6, 2026 •

Copy link
Copy Markdown

Overview

Random bots fight in arenas in their PvE spec and PvE gear: a leveling or raid build with no resilience, whatever the opponents' rating. A 2000-rated player and a fresh 80 meet the same bots.

With AiPlayerbot.PvpLoadoutSwap = 1 (off by default), a random bot entering an arena (rated or skirmish):

  • respecs in place to the PvP premade of its talent tree and applies its glyphs;
  • equips match gear scaled to the opponents' rating: a four-piece PvP set the rating allows, then the remaining slots chosen against per-role stat targets (hit, spell penetration, resilience, then the role's main stats), from vendor-sold PvP gear and the BiS table, keeping its own items where they are better; gemmed and enchanted towards the same targets;
  • gets its PvE talents, glyphs and gear back when the match ends, when it leaves, or after a crash.

The PvE items it takes off are saved as full copies (crafter, gems, enchants, random suffix, durability) and recreated afterwards, so a bot needs no free bag space and no item is lost in a crash.

Feature Evaluation

  • Minimum logic: one strategy, trigger, action and value. Gear choice is precomputed once per class, talent tree, level, faction and rating tier and shared by every bot in it; per bot only its own items are compared against it.
  • Processing cost:
    • Option off (default): nothing runs; the strategy is not added to bots.
    • Option on: every random bot carries the strategy; its trigger checks a cached value every 2 s and its multiplier one flag, and only bots in an arena do real work. Per bot per match: one respec, one gear plan, ~17 item creations and destructions, one async save, one restore. Building a cached loadout takes a few ms the first time a tier is needed. The gear pool is built once at startup on the world thread. No synchronous database access on map threads.

How to Test the Changes

  1. Apply the update SQL; set AiPlayerbot.PvpLoadoutSwap = 1. Optional: AiPlayerbot.PvpLoadoutAnnounce = 1 (bots say their spec, item level and rating in party chat in skirmishes) and AiPlayerbot.PvpLoadoutDebug = 1.
  2. Queue a skirmish (2v2 to 5v5) with random bots, or let bots auto-join rated arenas (AiPlayerbot.RandomBotAutoJoinBGRatedArena*Count); tested at 70-79 and 80, ratings 0 to ~1900.
  3. Before the gates open, inspect bots: PvP spec, PvP set and gear with gems and enchants. With debug on, each bot logs plans match gear, match gear for rating and match gems and enchants (totals against targets).
  4. After the match, inspect again: PvE talents, glyphs and gear back, enchants and gems intact.
  5. Restart the server mid-match: bots log back in with their PvE gear, no duplicates.

Impact Assessment

  • Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots?

      • No, not at all
      • Minimal impact (explain below)
      • Moderate impact (explain below)

    Off by default. When on, only bots in arenas do real work, once per match; every random bot carries a cached-value trigger check every 2 s and a multiplier check.

    pmon, 500 bots, BotActiveAlone = 100, smart scale off, rated arenas auto-joined, 103 minutes each: bot AI 1.627 ms per tick with the option off, 1.530 ms with it on (run-to-run noise). With it on, the feature's trigger, action and multiplier took 1.64 s of 951.9 s (0.17%, ~2.6 µs per tick): the trigger 0.5 µs and the multiplier 0.1 µs per call, the action 2.0 ms per call (max 3.7 ms), 360 calls across the arena entries in the window.

  • Does this change modify default bot behavior?

      • No
      • Yes (explain why)

    The feature itself is opt-in. Two shared-code changes apply to every bot: TalentSpec::ReadTalents now reads learned talents with HasTalent (it missed most ranks with HasSpell), and InitTalentsBySpecNo no longer stores the spec as the bot's chosen spec (ChangeTalentsAction, the caller choosing a spec, stores it).

  • Does this change add new decision branches or increase maintenance complexity?

      • No
      • Yes (explain below)

    New strategy, trigger, action and value, two playerbots tables, and ~2,600 lines in src/Mgr/PvpLoadout, PvpGearListMgr and the repository (3,300 in all, without tests). The decision logic is pure and unit-tested; shared code only gains small, default-off extension points.

AI Assistance

Was AI assistance used while working on this change?

    • No
    • Yes (explain below)

Claude Code (Claude Opus 5.5) wrote the design notes and most of the code from my design decisions, iterated with me over in-game tests (skirmishes at 70-79 and 80, a crash mid-match) and ran simplification reviews. I reviewed the changes and tested them in game.

Code Provenance / Attribution

Was any code in this PR copied or adapted from a sister / upstream project (e.g. CMaNGOS playerbots, MaNGOS, another module)?

    • No, all code in this PR is original
    • Yes (name the project and the original author(s) below)

Final Checklist

    • Changes are understood and tested for server stability and performance impact.
    • Any new bot dialogue lines are translated.
    • New source files use the GPLv2 header.
    • Documentation updated if needed (Code comments, Conf comments, WiKi commands).
    • New and modified files do not introduce new compiler warnings.

Notes for Reviewers

  • Commits are grouped for review: a standalone talent fix, shared-code groundwork (no behaviour change), the pure model, gear selection, the swap, and unit tests (-DPLAYERBOTS_TESTS=ON, off by default; droppable).
  • The two shared-code commits stay in this PR rather than a separate one: the talent fix is what makes the PvE talent snapshot correct, and the groundwork only adds default-off extension points the feature uses. Each can be reviewed (or cherry-picked) on its own.
  • Known gaps, from data rather than code: the BiS table lacks level-70 tiers for some specs, and death knights have no TBC PvP sets, so 70-79 death knights keep most of their own armor. Casters never reach the spell penetration target.

cfraser and others added 7 commits October 6, 2026 02:47
graphify writes its knowledge graph and per-file cache to graphify-out/ in
whichever directory it runs on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TalentSpec::ReadTalents checked HasSpell for each talent rank, which misses
ranks whose spell the player does not hold as a learned spell, so a captured
talent link recorded only a few points. HasTalent reads the talent map the
player actually saves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No behaviour change; groundwork for gearing bots outside the factory:
- PlayerbotFactory: RemoveGlyphs and ApplyGlyph are extracted from
  InitGlyphs; IsBodyArmorSlot and GetPossibleInventoryTypeListBySlot are
  static.
- InitTalentsBySpecNo no longer stores the spec number as the bot's chosen
  spec; ChangeTalentsAction, the one caller choosing a spec, stores it.
- ApplyEnchantAndGemsNew can be limited to some items (onlyItems, counting
  jeweler's gems already on the others) and take an EnchantScorer that
  scores candidates in place of StatsWeightCalculator. The gem loop reuses
  the routine's calculator instead of building one per candidate.
- StatsWeightCalculator can take the role from the talent spec instead of
  the active strategies (roleBySpec).
- BisListMgr::GetTiers lists the BiS tiers for a class and tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure logic for the arena loadout swap, with no core types so it can be unit
tested without a server:
- PvpLoadoutRules: the item level curve, PvP premade per talent tree, vendor
  rating requirements, gear limits and rating eligibility, the rating tier a
  rating unlocks, the match rating (rated: opposing MMR; skirmish: real
  players first, then bots), the enter/apply/restore decision, and the
  snapshot with its item copies in the item_instance column formats.
- PvpLoadoutEp: equivalence points from a role profile's stat targets (hit
  and spell penetration stop counting at their targets, resilience drops to
  a low weight past it), the four-piece set choice, and biggest-gain-first
  selection over groups of slots with unique-item constraints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tier

- PvpGearListMgr builds the PvP gear pool once at startup: armor and
  weapons with resilience or an honor or arena point cost, sold by vendors
  spawned somewhere other than GM Island, with the personal rating their
  vendors require. Per class, PvP premade and level it ranks the pool with
  StatsWeightCalculator in PvP mode.
- Match loadouts are chosen once per class, talent tree, level, faction and
  rating tier and shared by every bot of that spec: a four-piece of the best
  set the rating allows, then the remaining slots by biggest marginal EP
  against the role profile, from the ranked PvP items and the PvE item of the
  highest BiS tier at or below the rating's item level that the bot can
  wear. Body armor is the heaviest type the bot is proficient in, and
  candidates need their weapon, armor or shield skill.
- PvpLoadoutStats reads items, enchants and bots into EP stats through
  StatsCollector, with ratings scaled to their level-80 equivalent at the
  bot's level, so the level-80 profile targets hold below 80.
- Config: AiPlayerbot.PvpLoadout* options, the four AiPlayerbot.PvpProfile.*
  role profiles, and the PvP premade per talent tree.
- PvpLoadoutLog: debug lines behind AiPlayerbot.PvpLoadoutDebug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…after

Random bots entering an arena (rated or skirmish) take a PvP loadout and
give it back when the match ends; opt-in with AiPlayerbot.PvpLoadoutSwap.
- On entry the bot snapshots its PvE talents and glyphs, respecs in place
  to the PvP premade of its talent tree, and plans its match gear for the
  opponents' rating: the cached loadout, keeping its own items where they
  are worth more against its measured hit, spell penetration and
  resilience.
- The PvE items the gear replaces are kept as copies of their persisted
  state (crafter, gems, enchants, random suffix, durability, charges) in
  playerbots_pvp_loadout_item, saved with the snapshot in one transaction.
  Only once that has committed are they destroyed and the match items
  created in their slots, so a bot needs no bag space and a crash at any
  point leaves either its items or their copies. Match gear is gemmed and
  enchanted towards the role's targets.
- At match end, on leaving, after a crash or relog, and before a factory
  refresh or randomize, the bot gets its talents and glyphs back, its match
  items are destroyed and its PvE items re-equipped or recreated (slot, else
  bags, else mail). The copies are deleted only once the restored inventory
  is saved.
- Engine: "pvp loadout" strategy, trigger, action and value; a multiplier
  stops equip upgrades while a loadout is held. AiFactory adds the strategy
  to random bots.
- Optional skirmish announcement to the bot's real teammates
  (AiPlayerbot.PvpLoadoutAnnounce, translated texts) and debug lines
  (AiPlayerbot.PvpLoadoutDebug). Every option is documented in
  playerbots.conf.dist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
playerbots_tests (-DPLAYERBOTS_TESTS=ON, off by default) covers the pure
PvpLoadoutRules and PvpLoadoutEp code: config parsing, rating requirements,
eligibility and tiers, the match rating, the swap decision, snapshot and
item copy formats, EP weights, set choice, and biggest-gain-first selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

📝 Walkthrough

Walkthrough

Adds configurable PvP gear selection and loadout scoring for random bots. The feature stores PvE item and talent snapshots, detects arena transitions, and applies or restores PvP talents and gear. It also adds announcement text, debug settings, repository callback processing, and tests for loadout rules, serialization, and transitions.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to f3388

The opt-in arena loadout feature has no established functional defect. The remaining issues are small comment-style changes required by project guidelines and can be fixed quickly before merging.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/Mgr/Item/PvpGearListMgr.h (1)

20-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Shorten the new comments that run longer than two lines. The path instructions allow comments longer than two lines only in exceptional cases. These comments are not exceptional.

  • src/Mgr/Item/PvpGearListMgr.h#L20-L24: reduce the class comment to the GM Island exclusion and cache scope in two lines.
  • src/Mgr/PvpLoadout/PvpLoadoutStats.h#L15-L18: keep only the level-80 rating-scaling quirk.
  • src/Ai/Base/Actions/BattleGroundJoinAction.cpp#L721-L723: keep only "armor cannot be equipped in combat".
  • src/Ai/Base/Actions/PvpLoadoutAction.h#L14-L16: state the save-then-equip ordering in two lines.

As per path instructions: "flag any new or changed non-exceptional comment longer than two lines."

Source: Path instructions

src/Ai/Base/Strategy/PvpLoadoutStrategy.cpp (1)

12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the comment that justifies the relevance by a competing action.

The path instructions flag any comment that justifies a relevance value by naming competing actions. ACTION_HIGH is a named band and needs no justification.
As per path instructions: "Flag bare floats that sit between bands and any comment that justifies a number by listing competing actions."

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: mod-playerbots/mod-playerbots/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 606adae2-7630-4382-9b67-875fa528f08c
📥 Commits

Reviewing files that changed from the base of the PR and between cf1a2a2 and f3388f6.

📒 Files selected for processing (52)
  • .gitignore
  • conf/playerbots.conf.dist
  • data/sql/playerbots/updates/2026_10_03_00_playerbots_pvp_loadout.sql
  • data/sql/playerbots/updates/2026_10_04_00_ai_playerbot_pvp_loadout_announce_text.sql
  • mod-playerbots.cmake
  • src/Ai/Base/ActionContext.h
  • src/Ai/Base/Actions/BattleGroundJoinAction.cpp
  • src/Ai/Base/Actions/ChangeTalentsAction.cpp
  • src/Ai/Base/Actions/PvpLoadoutAction.cpp
  • src/Ai/Base/Actions/PvpLoadoutAction.h
  • src/Ai/Base/Strategy/PvpLoadoutStrategy.cpp
  • src/Ai/Base/Strategy/PvpLoadoutStrategy.h
  • src/Ai/Base/StrategyContext.h
  • src/Ai/Base/Trigger/PvpTriggers.cpp
  • src/Ai/Base/Trigger/PvpTriggers.h
  • src/Ai/Base/TriggerContext.h
  • src/Ai/Base/Value/PvpLoadoutValue.cpp
  • src/Ai/Base/Value/PvpLoadoutValue.h
  • src/Ai/Base/ValueContext.h
  • src/Bot/Factory/AiFactory.cpp
  • src/Bot/Factory/PlayerbotFactory.cpp
  • src/Bot/Factory/PlayerbotFactory.h
  • src/Bot/Factory/RandomPlayerbotFactory.cpp
  • src/Db/PlayerbotPvpLoadoutRepository.cpp
  • src/Db/PlayerbotPvpLoadoutRepository.h
  • src/Db/PlayerbotsDatabase.cpp
  • src/Db/PlayerbotsDatabase.h
  • src/Mgr/Item/BisListMgr.cpp
  • src/Mgr/Item/BisListMgr.h
  • src/Mgr/Item/PvpGearListMgr.cpp
  • src/Mgr/Item/PvpGearListMgr.h
  • src/Mgr/Item/StatsWeightCalculator.cpp
  • src/Mgr/Item/StatsWeightCalculator.h
  • src/Mgr/PvpLoadout/PvpLoadoutEp.cpp
  • src/Mgr/PvpLoadout/PvpLoadoutEp.h
  • src/Mgr/PvpLoadout/PvpLoadoutLog.h
  • src/Mgr/PvpLoadout/PvpLoadoutMgr.cpp
  • src/Mgr/PvpLoadout/PvpLoadoutMgr.h
  • src/Mgr/PvpLoadout/PvpLoadoutRules.cpp
  • src/Mgr/PvpLoadout/PvpLoadoutRules.h
  • src/Mgr/PvpLoadout/PvpLoadoutStats.cpp
  • src/Mgr/PvpLoadout/PvpLoadoutStats.h
  • src/Mgr/Talent/Talentspec.cpp
  • src/PlayerbotAIConfig.cpp
  • src/PlayerbotAIConfig.h
  • src/Script/Playerbots.cpp
  • tests/PvpLoadoutConfigTest.cpp
  • tests/PvpLoadoutCopyTest.cpp
  • tests/PvpLoadoutEpTest.cpp
  • tests/PvpLoadoutGearTest.cpp
  • tests/PvpLoadoutSnapshotTest.cpp
  • tests/PvpLoadoutStateTest.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

cfraser and others added 2 commits October 6, 2026 10:05
Comments longer than two lines are cut to two or fewer, and the relevance
comment that justified ACTION_HIGH by naming competing actions is removed,
as asked in the CodeRabbit review. No code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GCC rejects two warnings Clang does not report: the paired-slot loop copied
each pair (-Wrange-loop-construct), and two conditionals mixed the NULL_SLOT
enumerator with a uint8 (-Wextra). Iterate by reference and cast NULL_SLOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cfraser cfraser changed the title feat(Ai/BG): Give arena bots PvP talents and rating-scaled gear feat(arena): Give arena bots PvP talents and rating-scaled gear Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant