Getting started

Load order and overrides

How the game decides which mod wins, and how to change the base game without editing it.

Every enabled mod is loaded, in order. When two mods provide the same thing, the one later in the load order wins. That single rule is how you change the base game: you never edit ul_Base, you ship a file that replaces or extends one of its files, and put your mod below it.

The load order

Mods/LoadOrder.txt holds one line per mod, folder_name|1 (enabled) or folder_name|0 (disabled), top to bottom:

textul_Base|1
ul_Pregnancy|1
ul_Health|1
yourname_nightlife|1
yourname_test|0
  • The game writes this file; players reorder and toggle mods in the Mods panel of the pause menu. You can also edit it with the game closed.
  • A newly discovered mod is enabled and appended at the end, so a freshly installed mod overrides what came before it.
  • Shipped modules are discovered before Mods/, so on a fresh install they sit on top.
  • ul_Base is locked: it is always first and cannot be disabled.
  • A mod deleted from disk disappears from the file the next time it is written.

What "wins" means, per kind of content

Not everything is replaced the same way. Some content is keyed by an id, some by a file path, and a few JSON files merge field by field.

Content Identified by A later mod…
Scenes (.ulscene) File name, without folder or extension replaces the whole scene
Actions (.ulaction) ACTION_UNIQUEID replaces the whole action
Stats (.ulstat) STAT_ID replaces the whole definition
Quests (.ulquest) QUEST_UNIQUEID replaces the whole quest
History events (.ulhistory) id redefines the event type completely
Majors (.ulmajors) Major name replaces that major
Images, meshes, animations Path relative to Images/, Meshes/, Animations/… replaces the file
Cities City name replaces the city
Map/amenities.json Venue tag merges: only the fields you write change
Map/player_panel.json Stat id adds to stats, removes what you list in remove
Map/city_styles.json Style name replaces that style
Map/vehicles.json Vehicle id replaces that vehicle; traffic merges per field; left_hand_cities adds
Look profiles (.ullook) Venue tag (tag / tags) merges: patches only the fields it sets
Lua libraries (Lua/*.lua) Nothing: every file runs runs after, so it can wrap or overwrite earlier globals
Activities (.ulactivity) Activity id adds ids or replaces them

Examples

Replace a base scene. ul_Base has Interactions/leading_to_scenes/ask_out.ulscene. Your mod ships Scenes/ask_out.ulscene: the folder does not matter, the name does, and yours now runs instead.

Rebalance an action. Copy the .ulaction, keep its ACTION_UNIQUEID, change what you want. Yours replaces it entirely, so copy every field you want to keep.

Change one venue's opening hours without copying the whole file:

json{
  "amenities": {
    "bakery": { "open": 5, "close": 13 }
  }
}

Saved as Map/amenities.json in your mod, this changes the bakery's hours and leaves every other field, and every other venue, as it was.

Retexture something. A mod that ships Images/Health/virus.png replaces ul_Health's icon of the same relative path.

modder

This is why the base game keeps its content in files you can shadow instead of lists in code. Opening hours, map icons and venue roll weights are all things we could have hardcoded; because they are files, "change the bakery's hours" is a four-line mod.

Namespaces

Anything identified by name lives in one namespace shared by every mod. Two unrelated mods that both ship date_start.ulscene replace each other; the log says so ('date_start' from ul_B replaces the one from ul_A), and mod/name (ul_A/date_start) still reaches the replaced one, in RunSceneScript, SCENE_ALWAYS or a quest step.

Prefix what you create:

Thing Good Risky
Mod folder yourname_nightlife nightlife
Scene file ynl_bouncer.ulscene bouncer.ulscene
Action / stat id ynl_order_shot, ynl_drunk order_shot, drunk
Lua global table Nightlife Config, Utils
Story global, person var ynl_met_bouncer met_bouncer

Only reuse a name on purpose, when you mean to replace something.

note

Only .ulscene files are scenes. Your libraries, routines and HUD scripts (.lua) live in their own folders and never clash with a scene name.

Depending on another mod

When your mod uses another mod's content (its stats, its Actor: methods, its globals):

  1. List it in MODULE_REQUIREMENTS. Your mod then only loads while that one does (the Mods list says Blocked and why otherwise), and the game keeps it below that one in the load order.
  2. For a soft dependency, guard the calls instead, so your mod degrades rather than breaks when the other one is missing:
luaif Game:isModEnabled("ul_Health") then
	Actor:infect("flu")
end

A soft dependency (extra content when the other mod is present) is that check without the requirement line.