← Devblog

Overlays Become Script-Declared

Apothecary building info window showing employee count and an operational-status tooltip under the apothecary overlay

Last month the overlay menu moved to script. This time the overlays themselves follow. A city overlay — the data that says which walkers and buildings light up, which column type is drawn, and how tooltips read — can now be declared entirely in JavaScript with an [es=city_overlay] block, instead of requiring a hand-written C++ slot. The Apothecary health overlay is the first one migrated, and it now lives in its own overlays/apothecary.js.

What Changed

Before: A C++ Slot per Overlay

Every overlay needed a translation unit whose only job was to instantiate a typed slot:

// city_overlay_apothecary.cpp
city_overlay_t<OVERLAY_APOTHECARY> g_city_overlay_apothecary;

The data (walkers, buildings, column type, animation) was already in overlays.js, but the slot lived in the binary. Adding an overlay meant a new .cpp file and a recompile, and the script and engine sides had to be kept in lock-step by hand.

After: Declared in Script

The overlay is now a plain script declaration. The registry reads the named config section, validates the id against the overlay enum, allocates a city_overlay, fills it from the archive, stamps es_name, and drops it into the slot table — refusing duplicates and unknown ids along the way.

[es=city_overlay]
overlay_apothecary {
  id: OVERLAY_APOTHECARY
  title: "#overlay_apothecary"
  walkers: [FIGURE_HERBALIST]
  buildings: [BUILDING_APOTHECARY, BUILDING_ROADBLOCK],
  column_type: COLUMN_TYPE_POSITIVE
  column_anim: {pack:PACK_GENERAL, id:103}
}

The tooltip and column-height logic stays right next to the declaration, now reading and writing through city properties so the binding surface is uniform with the rest of the script API:

[es=(overlay_apothecary, get_column_height)]
function apothecary_building_column_height(ev) {
    var house = city.get_house(ev.bid)
    if (!house || house.population <= 0) {
        city.overlay_column_height = -1
        return
    }
    city.overlay_column_height = house.apothecary / 10
}

On script reload the registry clears the slots it owns and rebuilds them, so the hot-reload workflow applies to overlays just like it already does for building windows and the overlay menu.

Why This Matters

Overlays are now fully data-driven from script: their slot, their data, and their tooltip/column behaviour all live in one self-contained JS file. A modder can add a custom overlay for a new building type without adding a C++ file or recompiling the engine — declare it, write two handlers, press F5. Apothecary is the template; the remaining built-in overlays can migrate the same way, one file at a time.

← Camera Consolidation and a Growing Player Guide