NPC routines
Where the people you know spend their day, written as small Lua scripts in AI/.
The people the player knows have lives: they sleep, work, grab coffee, go to the gym, follow their partner to a party. None of that is engine code. Each habit is a short script in a mod's AI/ folder, and the engine only runs them.
A new habit ("goes to church on Sunday mornings", "hangs out at the arcade after school") is a ten-line file. No mod needs to ask the engine for it.
How the scheduler works
- About once a second, up to 7 known people whose current plan has run out are given a new one.
- For each of them, the high-priority routines (
AI/high_priority/*.lua) are tried first in random order, then the normal ones (AI/*.lua) in random order. - The first routine that sends the person somewhere (
AI:goTo) wins; they stay there until the hour it gave. - If no routine places them, they go to work during working hours if they have a job, and home otherwise.
Every known person gets a home and a job the first time the game asks where they are, following the original game's rules:
- Home: with the player for a spouse, housemate or neighbour; otherwise in an NPC spouse's house, or a random house of the city (joining whoever already lives there).
- Job: the player's workplace for a colleague or boss, the campus for a student or lecturer; otherwise an office half the time, and a shift job at a shop or leisure venue the other half.
Both are a real building, so someone works in their office and lives in their house, not in every one at once. AI:building("home") and AI:building("work") still return the venue tag. Office hours come from the high_priority/work_office.lua routine in ul_Base (weekdays 9 to 17), shifts from work_hourly.lua; replace either to change them.
People who are not near the player are simulated as data only: routines move them between buildings without anyone having to be rendered.
Writing a routine
A routine file is the body of a function. Do not write function … end or return; just the statements:
lua-- AI/ynl_clubbing.lua: young, outgoing people go clubbing on weekend nights
local weekend = day_of_week == 6 or day_of_week == 7 -- Friday, Saturday
if weekend and WHEN >= 22 and AI:stat("age") < 30 and isOpen("nightclub") then
if Random(0, 100) < AI:stat("interpersonal") and AI:getVar("ynl_clubbed_thisweek") == 0 then
local club = findNearby(AI:building("current"), { "nightclub", "bar" })
AI:goTo(club, HoursElapsed + Random(3, 5))
AI:setVar("ynl_clubbed_thisweek", 1)
end
end
What a routine can see
| Name | What it is |
|---|---|
AI |
The person being planned for (see below) |
WHEN |
The hour of the day, 0–24 |
day_of_week |
1 = Sunday … 7 = Saturday |
HoursElapsed |
Game hours since the game started. goTo takes an absolute hour, so write HoursElapsed + n |
Random(a, b) |
A random number between a and b (not a whole number: compare with <) |
isOpen(tag) |
Whether venues of that type are open right now (opening hours) |
findNearby(from, { tags }) |
One of those venue types that exists in this city, "" if none |
The AI object
| Method | Returns / does |
|---|---|
AI:stat(name) |
A stat; "age" and "attractiveness" work even without a stat of that name |
AI:getVar(key), AI:setVar(key, value) |
The person's variables (the same ones Actor:getVar reads in scenes) |
AI:building("home" | "work" | "current") |
A venue tag, "" if they have none |
AI:goTo(tag, untilHour) |
Go there and stay until untilHour. An empty tag does nothing, so the next routine gets a turn |
AI:getUntil() |
When their current plan ends |
AI:partner() |
Their spouse, else who they are dating, as another AI object; nil if nobody |
AI:placeExists(tag) |
Whether this city has a building of that type |
Once a day, once a week
Variables ending in _today, _thisweek and _thismonth are reset to 0 every day, every Sunday and on the 1st of each month. Use them to stop a routine from firing again:
luaif AI:getVar("ynl_gym_today") == 0 then
-- …
AI:setVar("ynl_gym_today", 1)
end
High priority or normal
Put a routine in high_priority/ when it should beat everyday habits: sleeping, meals, shifts, following a partner. Everything else (coffee, shopping, church, the gym) is normal. Look at ul_Base/AI/ for the full set the base game ships.
A routine is identified by its tier and file name. A later mod's AI/high_priority/sleep.lua replaces ul_Base's; a file with that name that is empty, or holds only -- comments, switches it off. Any other name adds a routine next to the base set.
Per-person schedules
For one specific person, scenes can also write fixed schedule rules with Actor:addScheduleEntry(from, to, weekdays, tag) (see People).