Worked example
A complete mod that adds eight help pages: one business type, three furniture pieces and three products, all cross-linked. This is the example mod used to test the library, so the code below is known to work rather than written for the page.
It documents a Bike Shop that does not exist — no items, no business type, no assets, just help pages. That keeps the example about the help API and nothing else.
The mod
#nullable enable
using System;
using System.Linq;
using System.Threading.Tasks;
using BAModAPI;
using BigAmbitions.Modding.Help;
using UnityEngine;
[assembly: RegisterModClass(typeof(BikeShopDocs.BikeShopDocsMod))]
namespace BikeShopDocs
{
[ModEntryOnInitializationLoad]
public class BikeShopDocsMod : IModBigAmbitions
{
private const string OwnerId = "BikeShopDocs";
private const string BusinessSlug = "businesstypes-bikeshop";
// Registered as a batch; sorted alphabetically among the base game's furniture pages.
private static readonly HelpPage[] FurniturePages =
{
new("furniture-bikeshopbikerack", "bikeshop:itemname_bikerack"),
new("furniture-bikeshoprepairstand", "bikeshop:itemname_repairstand"),
new("furniture-bikeshoptoolwall", "bikeshop:itemname_toolwall")
};
// Registered with Append, so these keep the order written here — cheapest first —
// instead of being sorted by name.
private static readonly HelpPage[] ProductPages =
{
new("products-bikeshopcitybike", "bikeshop:itemname_citybike"),
new("products-bikeshopmountainbike", "bikeshop:itemname_mountainbike"),
new("products-bikeshoprepairservice", "bikeshop:itemname_repairservice")
};
public string[] RelativeAssetBundlePaths => Array.Empty<string>();
public Task OnLoadAsync(ModContext context)
{
if (!HelpApi.IsSupported)
{
context.Logger.Warn("Help API unsupported on this game version; pages skipped.");
return Task.CompletedTask;
}
HelpApi.PagesApplied += OnPagesApplied;
HelpApi.RegisterPage(OwnerId, HelpCategories.BusinessTypes,
BusinessSlug, "bikeshop:businesstype_bikeshop");
HelpApi.RegisterPages(OwnerId, HelpCategories.Furniture, FurniturePages);
HelpApi.RegisterPages(OwnerId, HelpCategories.GoodsAndServices, ProductPages,
HelpPageOrder.Append);
context.Logger.Info($"Registered {HelpApi.SlugsFor(OwnerId).Count()} help page(s).");
return Task.CompletedTask;
}
public Task OnUnloadAsync()
{
HelpApi.PagesApplied -= OnPagesApplied;
HelpApi.UnregisterOwner(OwnerId);
return Task.CompletedTask;
}
// Runs once the help window exists and pages have been merged in. Base-game pages are not
// knowable before this point, so link checks belong here rather than in OnLoadAsync.
private static void OnPagesApplied()
{
string[] targets = { "importers-contract", "businesstypes-headquarters" };
var missing = targets.Where(slug => !HelpApi.PageExists(slug)).ToArray();
if (missing.Length > 0)
Debug.LogWarning($"[BikeShopDocs] Dead links: {string.Join(", ", missing)}.");
}
}
}
asmdef
{
"name": "BikeShopDocs",
"rootNamespace": "BikeShopDocs",
"references": ["BAHelpApi"],
"overrideReferences": true,
"precompiledReferences": ["BigAmbitions.ModAPI.dll" /* plus any game DLLs you use */],
"autoReferenced": false,
"defineConstraints": ["BA_GAME_DLLS_IMPORTED"]
}
The text
Locales/en.json. Two keys per page — title, and body under help_<prefix>_content.
{
"bikeshop:businesstype_bikeshop": "Bike Shop",
"help_bikeshop:businesstype_bikeshop_content": "**Bike Shop** businesses operate out of retail buildings.\n\nCustomers browse the floor, buy a bicycle, and occasionally bring one back in for repair.\n\nThe business requires the following furniture to function:\n\n* [Bike Rack](furniture-bikeshopbikerack)\n* [Point of Sales](furniture-itemgrouppointofsale)\n\nProducts sold here:\n\n* [City Bike](products-bikeshopcitybike)\n* [Mountain Bike](products-bikeshopmountainbike)\n* [Repair Service](products-bikeshoprepairservice)",
"bikeshop:itemname_bikerack": "Bike Rack",
"help_bikeshop:itemname_bikerack_content": "**Bike Rack** displays bicycles for sale in a [Bike Shop](businesstypes-bikeshop).\n\n**Product Capacity:** 6 bicycles\n**Footprint:** 2.0m x 0.5m\n\nThe furniture can be purchased from the following locations:\n* [Ika Bohag](address:50 4s)\n* [Marquis Living](address:5 7a)",
"bikeshop:itemname_citybike": "City Bike",
"help_bikeshop:itemname_citybike_content": "**City Bike** is the everyday product of a [Bike Shop](businesstypes-bikeshop).\n\n**Wholesale price:** $180\n**Market price:** $420\n\nDisplayed on a [Bike Rack](furniture-bikeshopbikerack).\n\nCan be ordered from [Importers](importers-contract) in your [Headquarters](businesstypes-headquarters)."
}
What this produces
- Business Types → “Bike Shop”, alphabetically among the 24 base-game entries
- Furniture → three pages, alphabetically among the 522
- Goods and Services → three pages in written order, because of
HelpPageOrder.Append - Every
[text](slug)renders as a working link between pages - Every
[text](address:16 11s)opens that building on the city map - All titles and bodies follow the player’s language
Log output on a healthy run:
[HelpApi] Help system found; pages can be applied.
[BikeShopDocs] Registered 7 help page(s).
[HelpApi] Applied 7 mod help page(s).
Patterns worth copying
Reuse an item’s name key as the page prefix. "bikeshop:itemname_bikerack" is presumably
already the item’s display name, so the page title stays correct in every language with nothing to
maintain twice.
Namespace your slugs. furniture-bikeshopbikerack, not furniture-bikerack. Slugs are global
across every mod, and they are public API — other mods may link to yours.
Unregister on unload. HelpApi.UnregisterOwner(OwnerId) in OnUnloadAsync, so disabling your
mod removes its pages cleanly.
Check links from PagesApplied, not OnLoadAsync. Mods load at the main menu, before the help
window exists, so PageExists cannot report on base-game pages that early.