BusinessBridge for mod creators

BusinessBridge offers your custom business type to the vanilla Big Ambitions AI, so rivals open it, it competes for district demand, and it survives save and reload.

Nothing is discovered automatically

BusinessBridge does not scan for custom businesses and cannot pick yours up on its own. You register your business type explicitly, and you supply the layouts and templates the AI is allowed to use. If you never call Register, your business type is never offered to the AI — nothing changes, and nothing breaks.

BusinessBridge knows nothing about any specific mod. The contract on these pages is the whole of it. Your business type keeps everything it already does for the player; BusinessBridge only adds the AI path.

Who this is for

Mod creators who add a custom Big Ambitions business type and want the game's AI competitors to be able to open it. If your mod adds products, items or systems but no business type, there is nothing here for you.

Download the SDK

PackageBusinessBridge SDK 0.1.0-beta.2
FileBusinessBridge-SDK-0.1.0-beta.2.zip (59.4 KB)
SHA-25653D9310F6DCCA52F7E51820B87AC9226CB76CA6450A70D6F1E80566D417FF38D

Download BusinessBridge SDK 0.1.0-beta.2

The SDK holds BusinessBridge.dll to build against, the public API source for reference, the full creator guide (CREATORS.md) and its licence. It is proprietary and governed by the LICENCE.txt inside it — not open source. Keep the DLL and the other SDK files out of public repositories, and link people here rather than rehosting the package.

Your players do not need the SDK. BusinessBridge itself, the mod they install, comes from the Steam Workshop.

Quick start

  1. Reference the BusinessBridge assembly from your .asmdef, which is what gives you load order.
  2. Register your business type, layouts and templates from your City-scope OnLoadAsync.
  3. Unregister from OnUnloadAsync.
  4. Check the RegistrationResult and read its messages.

What BusinessBridge does with a registration

  • It adds your templates and layouts to the lists the game already keeps, and takes them out again when you unregister or the city session ends. The game's own AI templates, and any other mod's, are left exactly as they are — nothing is replaced or rebuilt from scratch, and everything removed is something BusinessBridge added.
  • A registration that cannot work is rejected whole: nothing stored, nothing injected, no half-applied state. An individual template that cannot work is dropped with a message while the rest are kept.
  • Registering the same ProviderId and BusinessTypeName again replaces that registration rather than duplicating it.

1. Depend on BusinessBridge

The game's mod loader derives load order from assembly references, not from a manifest field. Reference the BusinessBridge assembly and the loader guarantees BusinessBridge loads before your mod:

{
  "name": "YourMod",
  "references": [ "BusinessBridge" ]
}

Register from your City-scope OnLoadAsync ([ModEntryOnCityLoad]). That runs after the game's layout cache is initialised and before the game-loaded callbacks — which is before new-game city generation, so your business type is on the list in time for a brand-new city.

Call BusinessBridgeApi.UnregisterProvider(yourProviderId) from OnUnloadAsync. The loader unloads mods in reverse load order, so your unregister runs before BusinessBridge tears its own session down.

2. A minimal complete integration

var result = BusinessBridgeApi.Register(new AiBusinessRegistration
{
    ProviderId = "YourMod",                                   // stable id of your mod
    BusinessTypeName = "yourmod:businesstype_yourshop",       // already registered with the game
    LayoutName = "YourShopImporters",                         // the logical AI layout name
    LayoutContents = layouts,                                 // built below; one entry per building size
    TemplateDonorBusinessType = "ba:businesstype_giftshop",   // vanilla type supplying the structure
    Templates =
    {
        new AiBusinessTemplate { BusinessName = "Green Door Dispensary", GoodsSource = AiBusinessGoodsSource.Import },
        new AiBusinessTemplate { BusinessName = "Canopy Cannabis Co.",   GoodsSource = AiBusinessGoodsSource.Import },
    },
});

Log(result.ToString());   // accepted/rejected, plus every decision BusinessBridge made

Register never throws. It returns a RegistrationResult with Accepted, TemplatesCreated, TemplatesActive and a Messages list.

3. The layouts

LayoutContents is the beta-2 transport: layout JSON in memory, typically read from a TextAsset in your own bundle, with nothing on disk.

const string LayoutName = "YourShopImporters";
var layouts = new List<AiBusinessLayout>();

var bundle = AssetService.GetBundle(context.ModId, YourBundleKey);
foreach (var size in new[] { "C1", "C2", "D2" })
{
    // Unique asset file name per size; identical names collide inside one AssetBundle.
    var asset = bundle.LoadAsset<TextAsset>($"Assets/Mods/YourMod/Layouts/{size}/yourshop_{size}_layout.json");
    if (asset == null || string.IsNullOrEmpty(asset.text)) continue;
    layouts.Add(new AiBusinessLayout(LayoutName, asset.text));   // the LayoutName, NOT asset.name
}

The asset's file name and the logical layout name are two separate things, and getting them confused is the one mistake worth warning about up front — Shipping layouts explains both transports, the naming rule and what is validated. The older LayoutFiles path (loose .json files on disk) still works unchanged.

4. Lifecycle

  • Register from City-scope OnLoadAsync. With a city session active, BusinessBridge applies the registration immediately.
  • Register outside a city session and it is stored instead, and applied at the next city load — the result says stored; applied at the next city load.
  • City unload (main menu, loading another save, quitting) removes what was applied. Registrations themselves are kept and re-applied on the next city load.
  • Unregister with BusinessBridgeApi.UnregisterProvider(providerId) from OnUnloadAsync. It removes every registration of that provider and returns how many business types were removed. It only ever touches your own.

Registering a business type covers every field, what is validated, and how to tell that it worked.

Release checklist for a BB-compatible mod

Before you ship a mod that registers with BusinessBridge:

  • Your .asmdef references BusinessBridge, so load order is guaranteed.
  • You register from City-scope OnLoadAsync and unregister from OnUnloadAsync.
  • You check BusinessBridgeApi.ContractStage and skip registration on a stage you do not know, rather than registering blindly.
  • RegistrationResult.Accepted is true in a clean run, and you log result.ToString() either way.
  • Every layout parses as a BusinessLayoutSet for your business type, and carries the right BuildingSize/BuildingVersion.
  • Each layout asset in your bundle has a unique file name, and you pass your LayoutName as AiBusinessLayout.Name rather than asset.name. (With LayoutFiles instead, each file on disk is named <LayoutName>.json.)
  • Your built AssetBundle actually contains every layout asset you expect — an asset-name collision can produce an empty bundle from a build that looked like it succeeded.
  • Your business type carries ba:businesstag_allowplayercreation and has a suitableBuildingType.
  • Every primary product of your business type is a demanded product.
  • Your AI business names are unique game-wide.
  • You have started a new game and confirmed an AI rival opens your business type, not just loaded an existing save.
  • You have confirmed behaviour after save and reload.
  • Your mod degrades cleanly when BusinessBridge is absent or inactive — the player's own use of your business type must not depend on it.
  • You know what your stores' logos look like, and are happy with the generated ones. See Logos and icons.

Reference integration

High Ambitions is the production integration BusinessBridge is proven against: it registers two custom business types (a Dispensary and a Smoke Shop), each with its own AI templates and one layout per supported building size, using LayoutContents and the assembly-reference dependency described above.

The fuller creator reference is CREATORS.md in the SDK, and always matches the SDK version it came with.

{# GoatCounter's own count.js, served from this site rather than from a third-party host. The context processor decides whether this page may be counted at all; see core/context_processors.py. Both scripts are deferred, so the settings file below runs first and has set window.goatcounter.referrer before count.js reads it. #}