Skip to content

Generated reference. This page mirrors Code/Licences/README.md, this module's source-of-truth documentation (Standards/02_DOCUMENTATION_STANDARDS.md §1). Edit it there, not here -- this file is regenerated on every docs build and any local edit is silently overwritten.

Licences

Purpose. A player-earned/purchased licence that gates carrying or using items of a restricted category — legacy never had this at all (Legacy/01_FEATURE_INVENTORY.md, "❌ … New"). Built as ROADMAP.md Milestone 14's own named build item, with a Firearms licence gating ItemCategory.Weapons as the exit criterion's own worked example. See Documentation/Architecture/28_LICENCES.md.

Responsibilities. - LicenceDefinition — one configured licence: Id, DisplayName, Cost, and GatedCategories (the ItemCategory values holding it permits). Content, not code, the exact shape Needs's NeedDefinition already established for NeedsConfiguration.Definitions. - LicenceConfiguration — the operator-tunable catalog, following 05_CONFIGURATION.md's per-module GameResource pattern. Seeded with one entry (firearms, cost 500, gating Weapons). PostLoad refuses a duplicate id and refuses two definitions gating the same category — every category has at most one owning licence. - ILicenceService / LicenceStore — the catalog reader (Definitions, FindDefinition, RequiredLicenceFor) and PurchaseAsync: the validate, grant, save, debit, publish sequence — in that order, and why, per LicenceStore.PurchaseAsync's own doc comment (mirrors PropertyStore.PurchaseAsync's ordering exactly). No dedicated persistence document: a held licence is Character-scoped state, persisted as part of CharacterDocument itself (Character.Licences/CharacterLicences, Characters), not a separate collection this module owns. - ItemLicenceGate — the actual gate: a plain, engine-agnostic static method (IsPermitted(ItemDefinition, Character, ILicenceService, out missingLicenceId)) answering whether a character may use an item, given its category. The real, wired call site is CharacterInventoryComponent.RequestUseItem (Inventories) — this milestone's exit criterion in code. - LicenceGranted — published once a purchase's grant-and-save has landed, mirroring Applejack.World.DoorPurchased's shape. - LicencesModule — registers LicenceConfiguration and ILicenceService.

Not responsible for. - Item templates, categories, or behaviours — Items owns ItemDefinition/ItemCategory/Equippable; this module only reads them. - Which categories are actually gated, beyond the seeded Firearms example — an operator edits LicenceConfiguration's asset to add more (e.g. an alcohol licence gating ItemCategory.Alcohol), no code change required. - PoliceWeapons/IllegalWeapons gating. PoliceWeapons is a job-loadout concept (once Jobs' per-job loadouts are real, that is the right gate, not a purchasable licence); IllegalWeapons is illegal by definition — no licence should ever legitimise carrying one. See 28_LICENCES.md §4 for the full reasoning. - An admin free-grant command (e.g. /grantlicence). CharacterLicences.Grant/Revoke are public and idempotent, the same shape PermissionSet.Grant/Revoke already has, so a future admin command can reuse them directly — not built this pass, since PurchaseAsync alone satisfies this milestone's "a way to purchase/grant a licence" build item. See "Future improvements". - Licence expiry or renewal. A held licence is permanent once granted, the same "no independent tick/decay" scope CharacterProgression already keeps for biography/attributes/ skills — deliberately minimal for this milestone; see "Future improvements".

Dependencies. Core (IServiceRegistry, IServiceResolver, IEventBus, IGameLog, IConfigurationInspector), Character (Character, CharacterId, ICharacterService, CharacterLicences), Economy (IEconomyService), Items (ItemCategory, ItemDefinition, for the configuration shape and the gate).

Public API. - ILicenceServiceDefinitions, FindDefinition(string), RequiredLicenceFor(ItemCategory), PurchaseAsync(Character, string). - LicenceDefinition, LicenceConfiguration. - ItemLicenceGate.IsPermitted(ItemDefinition, Character, ILicenceService, out string?). - LicenceGranted event. - Applejack.Characters.CharacterLicences (Character.Licences) — Held, Has, Grant, Revoke. Declared in Characters, not here, per ADR-0011 point 2's "own composed type alongside Character" precedent — this module reads it, it does not own it.

Events published. LicenceGranted — once PurchaseAsync has granted and saved.

Persistence. None owned directly — see LicenceStore's own header comment. A held licence persists as CharacterDocument.Licences (schema version 4, Characters).

Future improvements. - An admin free-grant command, once a fork actually needs "give this player a licence for free" — the underlying CharacterLicences.Grant/Revoke calls are already there. - Licence expiry/renewal, if a fork's design wants licences to lapse — would need its own timestamp field on CharacterLicences, following AccountModeration.ExpiresAtUtc's precedent, not invented from scratch. - Gating a second existing check beyond "use an item" (e.g. a vendor refusing to sell a restricted item to a licence-less buyer, once Vendors exists) — ItemLicenceGate is already general enough (ItemDefinition in, bool out) to reuse at a second call site with no redesign.