Map and venues
The venue vocabulary, opening hours and roll weights, map icons, city styles and street traffic, all as JSON you can extend.
Every building on the map has a venue tag: cafe, gym, bar, hospital, home… Tags are the shared vocabulary of the whole game: actions and scenes list them in WHERE, rooms are built per tag, look profiles are chosen by tag. They are defined in Map/amenities.json.
amenities.json
json{
"miles_per_hour": 3.1,
"amenities": {
"bar": {
"group": "food",
"icon": "bar",
"open": 12,
"close": 4,
"name": "Bar",
"category": "retail",
"roll": 1,
"ensure": true
}
}
}
| Field | Meaning |
|---|---|
| (key) | The venue tag |
name |
Label for a building that has no name in OpenStreetMap |
group |
Map filter it is listed under: food, homes, leisure, services, shops, transport, work |
icon |
Map/Icons/Locations/<icon>.png |
open, close |
Opening hours, 0–24. close smaller than open wraps past midnight; 0/24 is always open |
category |
The building bucket the generator dresses it from: home, work, retail, leisure |
roll |
Weight of this tag when an untagged building of its bucket is dressed at random (0 = never rolled) |
ensure |
Every city gets at least one, even if the OSM data has none, so no content is unreachable |
count |
With ensure: how many the city gets (default 1). The copies are kept at least 600 m apart |
street |
"minor" places an ensured venue on a side street: a building whose nearest road is an ordinary street or a footway, with no main road or highway within 80 m. The back alley (alley, 3 per city) uses it; a city with no such building left gets the venue anywhere rather than not at all |
osm_buildings |
OSM building=* values that resolve to this tag when the building has no shop or amenity tag |
landmark |
{} makes the venue a landmark: one per city, on the largest building near the centre (never a home or a real OSM venue), and its pin always shows on the map. Optional "wall" and "roof" (hex colours) and "floors" give it a look of its own. A real OSM building with the tag (a town hall in London) is used instead |
miles_per_hour |
(top level) The player's walking speed; a car multiplies it by 6 |
How a building gets its tag: its OSM amenity=* or shop=* value if that is a known tag, else its building=* value through osm_buildings, else a weighted roll within its bucket.
The base game has two landmarks: the town hall (townhall, OSM's own tag; ul_CityHall's mayor and ul_Politics' registration happen there) and Broken Human Studio (broken_human_studio, the studio that makes the game: a dark tower with a red roof).
json"broken_human_studio": {
"group": "work", "icon": "broken_human_studio", "open": 9, "close": 22,
"category": "work", "roll": 0, "ensure": true,
"landmark": { "wall": "#3A3842", "roof": "#E8344A", "floors": 16 }
}
Changing and adding venues
The file merges field by field across mods: ship only what you change.
json{
"amenities": {
"bakery": { "open": 5, "close": 13 },
"ynl_speakeasy": {
"group": "food", "icon": "ynl_speakeasy", "name": "Speakeasy",
"open": 21, "close": 3, "category": "retail", "roll": 0.2, "ensure": true
}
}
}
A new tag needs, to be worth visiting: an icon (Map/Icons/Locations/ynl_speakeasy.png), a room recipe, and some actions with the tag in WHERE. If its strangers should be someone in particular (a back alley is not a bakery), a crowd profile.
Opening hours, roll weights and the "every city has one" guarantee are all data, so "make gyms rarer" or "open the bakery at 5" never needs a code change.
The base vocabulary
The base game defines about 180 tags. Some you will use often:
| Group | Tags |
|---|---|
| Homes | home, apartments, house, dormitory, hotel, motel |
| Food and drink | cafe, coffee, tea, restaurant, fast_food, diner, pizzeria, bar, pub, nightclub, lounge, biergarten |
| Leisure | cinema, theatre, arcade, bowling, casino, spa, gym, yoga, park, beach, stripclub, private_club |
| Services | bank, police, hospital, clinic, pharmacy, hairdresser, beauty, library, school, university, place_of_worship, laundry, dry_cleaning, shoe_repair, locksmith |
| Shops | supermarket, convenience, clothes, shoes, jewellery, electronics, books, florist, bakery, department_store |
| High-street trades | charity, second_hand, e-cigarette, frozen_food, baby_goods, cheese, wigs, knives, weapons (air rifles and airsoft; British law keeps firearms off the counter), fishing |
| Home and big-box stores | furniture, appliance, hifi, bed, kitchen, bathroom_furnishing, lighting, carpet, flooring, houseware, interior_decoration, hardware, doityourself, trade, wholesale |
| Work | office, commercial, industrial, warehouse, studio, lab, newsroom |
The full list is the keys of ul_Base/Map/amenities.json. Special WHERE values that are not venues: all, none, interaction, quest.
Map icons
Map/Icons/<Group>/<name>.png, looked up across mods (a later mod replacing the same path). Groups used by the base game: Locations (venue markers), Stats (player panel chips), Moods, History, Quest, Stock, UI, Markers, MapControls, Weather, Districts, Preferences. Mod UI pages use any group you like (MENU.icon, see Mod UI).
Use white glyphs on a transparent background: the HUD tints them.
City styles
Map/city_styles.json defines the colours of the city's buildings, roads and street lamps look. The player picks a style in the map settings; default is used until they do. A later mod replaces a style with the same name.
json{
"default": "model_white",
"styles": {
"ynl_neon": {
"use_osm_colours": false,
"walls": ["#1B1030", "#241540", "#2E1A52"],
"roofs": ["#3A2170", "#4B2A8C"],
"lamps": { "spacing": 2400, "lit_streets": 0.8, "radius": 1200, "color": "#FF4FD8", "intensity": 0.2 },
"roads": { "street": "#2A2A33", "major": "#23232B", "highway": "#1C1C22" }
}
}
}
| Field | Meaning |
|---|---|
use_osm_colours |
Keep OSM building:colour / roof:colour where tagged |
walls, roofs |
sRGB hex colours, one picked per building |
lamps |
Street lights after dusk: spacing (cm along a road), lit_streets (share of ordinary streets lit; major roads always are), radius (cm), color, intensity |
roads |
Carriageway colours: street, major, highway |
textured_roads |
true draws the photographed asphalt instead |
| look | Optional. The map's composed atmosphere, see below |
Buildings are always plain colour. Base styles: underlives (the default), night_blue, pastel, slate_teal, warm_sand, model_white, sunset. Test one with -CityStyle=<name>, and an hour with -LookHour=<0-23>.
The look block
Without look the map is lit by the plain day cycle and the weather. With it, the style decides what the map looks like at every hour: fixed exposure, a clear view up to a horizon fade, lit windows at night.
json"look": {
"shadows": true,
"skyline": { "share": 0.12, "min_span": 2200, "min_floors": 16, "max_floors": 48 },
"windows": { "share": 0.2, "lit": 0.7, "min_height": 1100, "color": "#FFD49A", "intensity": 12 },
"ground_color": "#DCD8D1",
"water_color": "#7FAAD8",
"park_color": "#355C3B",
"day": { "exposure": 1.5, "fog_color": "#B9BEC9", "fog_density": 0.02, "fog_start": 130000 },
"night": { "exposure": -1.0, "fog_color": "#0E0D14", "moon": 4.0, "fill": 0.8, "road_glow": 0.45 }
}
| Field | Meaning |
|---|---|
shadows |
Buildings cast shadows |
skyline |
A share of the blocks at least min_span cm across, with no height in the OSM data, become towers of min_floors to max_floors |
windows |
Lit windows after dark: share of the buildings at least min_height cm tall (taller ones likelier), lit share of their windows on, color, intensity |
tree_color |
Colour of the round tree crowns along streets and in parks. Five shades are derived from it (darker, yellower, bluer, olive) |
tree_colors |
Optional list of sRGB hex shades used instead of the derived ones, one picked per tree |
ground_color |
A plane under the whole city that runs far past it, so the map is not a plateau with an edge. Match the day fog_color to it and the horizon fades into the ground |
water_color, park_color |
Colours of the city's water and green areas (see Cities) |
day, night |
Blended by the sun: exposure (fixed EV bias), fog_color, fog_density, fog_start (cm of clear view before the horizon fade), fog_max, fog_falloff, fog_ambient, weather_fog (0-1, how much rain and fog may thicken it), sun_scale, sky_scale, moon (night key light), moon_color, fill (an unshadowed light from opposite the sun, lux: lifts the walls turned away from it), fill_color, road_glow, bloom, bloom_threshold, vignette |
Traffic: vehicles.json
Cars drive the streets around wherever the camera looks, keep to their lane, queue behind slower cars and turn at junctions. They are decoration: nothing in the game reads where a car is. Map/vehicles.json lists the models and sets the traffic; the models are loose .glb (or .fbx) files in your mod's Map/ folder, loaded at runtime like furniture.
json{
"traffic": { "cars_per_km": 28, "max_cars": 450, "speed_street": 850, "speed_major": 1150, "speed_highway": 1800, "hide_above": 70000, "shadows": true },
"vehicles": [
{ "id": "ynl_bus", "file": "Vehicles/ynl_bus.glb", "length": 1200, "yaw": 0, "weight": 1, "paint": "#E86147", "roles": ["traffic", "bus"] },
{ "id": "police", "weight": 0 }
],
"paints": [ { "color": "#F4F4F2", "weight": 18 }, { "color": "#17181C", "weight": 20 }, { "color": "#6E1820", "weight": 9 } ],
"left_hand_cities": ["Dublin"]
}
| Field | Meaning |
|---|---|
id |
The vehicle. A later mod with the same id replaces it |
file |
Model path, relative to the Map/ folder of the mod that lists it. The whole scene is merged into one mesh (body and wheels can be separate nodes) |
length |
Real length in cm; the model is scaled to it whatever unit it was authored in |
yaw |
Degrees to turn the model so it faces +X. glTF models facing +Z (Kenney's kit and most others) need 180 |
weight |
How often a car of the fleet is this model. 0 keeps it loaded but off the streets |
paint |
The model's own body colour (sRGB hex). Each car of it is repainted with one of paints, the body's shading kept. Leave it out to keep the model's livery (taxi, police…) |
paints |
(top level) The fleet's colours: { "color": "#17181C", "weight": 20 }. A later mod's list replaces the whole palette. Avoid beige, silver and mid greys: a car the colour of the road disappears from birdview |
roles |
traffic = drives around. Other names (taxi, police, ambulance…) are for systems that ask for a specific vehicle |
cars_per_km |
Density: cars per km of drivable road around the camera |
max_cars |
Size of the fleet (the cost ceiling) |
speed_street, speed_major, speed_highway |
cm/s per road class (850 ≈ 30 km/h). Each car varies a little around it |
hide_above |
Camera height in cm above which no car is drawn |
shadows |
Cars cast shadows |
left_hand_cities |
City map names where traffic keeps left. Lists from all mods add up |
traffic merges field by field across mods, so ship only what you change. Footpaths and service roads (OSM "other") carry no traffic. Keep models light (a few thousand triangles): hundreds are on screen at once. The base fleet is Kenney's Car Kit (CC0).
Who lives in the homes: housing.json
Every home on the map has occupants. A home gets them the first time anything looks at it (the player walks in, a scene calls Places:residence), and keeps them in the save from then on. The people are ordinary known NPCs whose home is that building, so they are in it when their day sends them home, and nowhere else.
Map/housing.json (in ul_Base; a mod overrides a field or adds a household kind by shipping its own):
| Field | Meaning |
|---|---|
occupied_share |
Share of homes with people in them (0.9) |
rent |
[min, max] monthly market rent in dollars, times the city's CostOfLiving / 100 |
married_chance, same_sex_couple_chance |
For the couple of a couple: true kind |
households |
Kinds by name: weight, adults [min, max], couple, grown_children [min, max] (adult children, 18 to 24, still living with their parents), ages [min, max], owner_occupier (chance the owner is the first adult, otherwise a landlord who lives elsewhere) |
Inside a home that is not the player's, the location is residential, not home: the legacy vocabulary already kept the two apart, so WHERE = { "home" } means the player's own home. Actions marked PRIVATE = true (see Actions) are offered there only when the player is welcome. No passers-by are generated in a home: the people in it are the ones who live there, and whoever came with the player.