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)
Phase 2 — ACP CRUD ✅ done (38251abf)
Phase 3a — Player edit dropdown ✅ done
Phase 3b — Roster column ✅ done (dc9e58f9)
Phase 3c — Recruitment filter ⬜ todo
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
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)
- 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.
- Can a class have zero subclasses? Yes — opt-in per game. Plugins without specs simply have no rows.
- 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.
- Should recruitment specify spec or just class+role? Phase 3c — optional, gated on whether the game has specs.
- 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".
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)v200b4:bb_specializationstable +player_spec_idcolumn onbb_playersavathar\bbguild\model\games\rpg\specializationwithload/save/delete/get_for_classspecialization_provider_interface(separate fromgame_provider_interfaceso existing 9 plugins don't break)avathar.bbguild.specializationregisteredPhase 2 — ACP CRUD ✅ done (
38251abf)player_spec_idon referencing playersPhase 3a — Player edit dropdown ✅ done
player_spec_idgetter/setter + INSERT/UPDATE/SELECT wiring inplayer.php(1ea50aec)1ea50aec)1ea50aec)36f76317)SPEC/PLAYER_SPEC_EXPLAINlang keys moved tocommon.phpso both ACP and UCP have them (36f76317)Phase 3b — Roster column ✅ done (
dc9e58f9)player_specfor un-migrated rowsS_SHOWSPECtoggle hides the column entirely when the game has no specsresolve_spec()— 5 cases covering hit / miss / zero-id / no-data / empty-iconPhase 3c — Recruitment filter ⬜ todo
Phase 4 — Plugin spec seeding ⏳ in progress (1 of 9 plugins done)
Core hook landed in
b6bf7ff4:abstract_game_install::install_specs()is called frominstall(), default no-op. Plugins override and (optionally) implementspecialization_provider_interface.Each plugin opts in via
specialization_provider_interfaceand seeds its game's spec catalog. Tracked in the per-plugin sub-issues below.Phase 5 — Legacy data migration ⬜ todo
player_specmatches a known spec name → setplayer_spec_idplayer_speccolumn once migration is verifiedGame plugin sub-issues
Each plugin seeds specs for its game once core Phase 1+2 lands. Plugins without specs (e.g. Custom) opt out.
9ba1389)Proposed design
bb_specializationstable:Design decisions (resolved during Phase 1)
role_id. Games where a spec spans multiple roles can model that as multiple rows with the samespec_name.player_spec_idUINT (0 = unset) onbb_players. Legacy free-textplayer_speccolumn kept for backward compatibility, deprecated.specialization_provider_interfaceand return aclass_id => [spec rows]map; core's seeder walks the map.Terminology
Different games use different terms:
The UI label comes from
specialization_provider_interface::get_spec_label(); default is "Specialization".