Skip to content

Add subclass/specialization support #331

@avandenberghe

Description

@avandenberghe

Summary

Classes in many games have specializations (subclasses) that determine how a class fulfills a role. For example, a WoW Mage has three specs: Arcane, Fire, and Frost — all DPS. A Druid can be Balance (DPS), Feral (DPS/Tank), Restoration (Healer), or Guardian (Tank).

Currently bbGuild has classes and roles but no layer between them. This means we can't express "this player plays a Frost Mage" — only "this player is a Mage" and separately "this player's role is DPS".

Phases

Implementation is broken into incremental phases so each one is independently shippable and reviewable.

Phase 1 — Foundation ✅ done (138dd5b2)

  • Migration v200b4: bb_specializations table + player_spec_id column on bb_players
  • Model avathar\bbguild\model\games\rpg\specialization with load/save/delete/get_for_class
  • Optional specialization_provider_interface (separate from game_provider_interface so existing 9 plugins don't break)
  • Service avathar.bbguild.specialization registered
  • Unit tests (9 cases / 39 assertions, all green)

Phase 2 — ACP CRUD ✅ done (38251abf)

  • Specializations panel on Edit Game page (after Classes)
  • Add Specialization form with class + role dropdowns
  • Edit / Delete handlers; delete clears player_spec_id on referencing players
  • Language keys (en)

Phase 3a — Player edit dropdown ✅ done

  • player_spec_id getter/setter + INSERT/UPDATE/SELECT wiring in player.php (1ea50aec)
  • ACP player add/edit form: spec dropdown filtered by selected class (1ea50aec)
  • Tests for spec accessor — 4 cases (1ea50aec)
  • UCP player edit: same dropdown (36f76317)
  • Shared SPEC / PLAYER_SPEC_EXPLAIN lang keys moved to common.php so both ACP and UCP have them (36f76317)

Phase 3b — Roster column ✅ done (dc9e58f9)

  • Optional Spec column in roster portal module (listing + grid views)
  • Spec resolution falls back to legacy free-text player_spec for un-migrated rows
  • S_SHOWSPEC toggle hides the column entirely when the game has no specs
  • Tests for resolve_spec() — 5 cases covering hit / miss / zero-id / no-data / empty-icon

Phase 3c — Recruitment filter ⬜ todo

  • Optional spec field on recruitment ad
  • Spec filter on recruitment listing

Phase 4 — Plugin spec seeding ⏳ in progress (1 of 9 plugins done)

Core hook landed in b6bf7ff4: abstract_game_install::install_specs() is called from install(), default no-op. Plugins override and (optionally) implement specialization_provider_interface.

Each plugin opts in via specialization_provider_interface and seeds its game's spec catalog. Tracked in the per-plugin sub-issues below.

Phase 5 — Legacy data migration ⬜ todo

  • Walk existing rows where free-text player_spec matches a known spec name → set player_spec_id
  • Drop player_spec column once migration is verified

Game plugin sub-issues

Each plugin seeds specs for its game once core Phase 1+2 lands. Plugins without specs (e.g. Custom) opt out.

Proposed design

bb_specializations table:

Column Type Description
spec_id UINT (PK, auto) Primary key
game_id VCHAR:10 Game identifier
class_id USINT FK to bb_classes
role_id USINT FK to bb_gameroles
spec_name VCHAR_UNI:100 Display name
spec_icon VCHAR:100 Icon filename
spec_order USINT Display ordering within class

Design decisions (resolved during Phase 1)

  1. Is a spec always tied to exactly one role? Yes — each row has a single role_id. Games where a spec spans multiple roles can model that as multiple rows with the same spec_name.
  2. Can a class have zero subclasses? Yes — opt-in per game. Plugins without specs simply have no rows.
  3. Should the player model track current spec? Yes — player_spec_id UINT (0 = unset) on bb_players. Legacy free-text player_spec column kept for backward compatibility, deprecated.
  4. Should recruitment specify spec or just class+role? Phase 3c — optional, gated on whether the game has specs.
  5. How do game plugins seed specs? Plugins implement specialization_provider_interface and return a class_id => [spec rows] map; core's seeder walks the map.

Terminology

Different games use different terms:

  • WoW: Specialization (spec)
  • FFXIV: Job (with base class)
  • GW2: Elite Specialization / Trait Line
  • SWTOR: Discipline
  • Generic: Subclass

The UI label comes from specialization_provider_interface::get_spec_label(); default is "Specialization".

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.2.0Milestone: New features

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions