Gameplay content

Shops and items

Objects people own, catalog items, shop inventories, and how a scene opens a shop.

Shops are text files: a shop lists what it sells and at what price, and items are defined once in a shared catalog so any shop can stock them. What people own is saved with the game, and scenes can give, take and check it.

warning

Purchases are paid from the player's cash (money) and selling pays into it. Both are written to the ledger under the mod that ships the .ulshop, so they show in the wallet's recent list ("Urban Threads: Medkit x2"). Prices are in dollars and display in the city's currency. What is bought is a real, saved item (below).

Owning items

Every person has an inventory of objects: an antique found at a flea market, a trophy, a ring given as a present. The player's is listed in the wallet with what it is worth.

lua-- an object nobody wrote a file for: describe it the first time you hand it out
Player:addItem("ul_antique_vase_3", 1, { name = "Ming-style vase", category = "Antique", value = 450,
	description = "Probably not Ming.", mod = "ul_Antiques" })

if Player:hasItem("engagement_ring") then
	Player:giveItem(Partner, "engagement_ring")       -- the partner owns it now
end

for _, item in ipairs(Player:getItems()) do
	if item.mod == "ul_Antiques" then
		total = total + item.value * item.count
		Player:removeItem(item.id, item.count)
	end
end
  • An id with an .ulitem file gets its name, category and worth from the file, whichever mod hands it out. Any other id is described by the table you pass to addItem; the engine remembers that description, so a later addItem(id) from a scene that only knows the id gets the same name.
  • value is the worth of one, in dollars. Passing it again re-appraises the ones already owned; setItemValue does the same without adding any.
  • Items are per person. giveItem moves them with their description and worth.
  • Every change raises the itemChanged event, so a mod can react to an item another mod gave out.
  • Things that are counted in bulk (a web shop's 200 units of stock) are better as a stat; items are for objects the player would want to see listed.

The calls are in the Actor API reference under Items.

Items: Items/*.ulitem

iniDisplayName: Energy Drink
Category: Consumable
Description: A quick jolt of caffeine and sugar.
IconId: Nightlife/energy_drink.png
Stackable: true
Value: 3
Key Meaning
(file name) The item's id: Items/energy_drink.ulitem is energy_drink
DisplayName, Description Shown in the shop
Category One of Weapon, Armor, Accessory, Jewelry, Cosmetics, Consumable, Electronics, Furniture, HomeDecor, Vehicle, Gift, Book, Tool, Misc
IconId A picture under your mod's Images/ folder
Stackable Whether several can be owned as one entry
Value Worth of one in dollars, what the inventory counts it at (a shop sells it at its own price)
Kind Optional: what it is within its category (car, van...), for questions such as Actor:ownsVehicle("car")
Rarity Optional: a tier of Items/*.ulrarity (see Clothing), shown in the shop
Stock, StockAmount Optional: buying it adds StockAmount (default 1) to that stat instead of putting an item in the inventory. Groceries work this way: Stock: stock_coffee / StockAmount: 15 is a bag of coffee, fifteen cups for the home Have a drink
MinAge Optional: not sold to anyone younger (18 on alcohol and cigarettes)
Storage Optional: where it is kept at home: a container kind of Items/storage.json (fridge, pantry, bathroom, wardrobe, drawer...), carry (stays with the player) or place (stays with the player, to be put down by hand). Without it, storage.json decides by id, stock stat and category. See Home storage
ShelfLife Optional: days after purchase before it is spoiled (0 = never). Without it, storage.json decides
Piece Optional: the room piece this item is when it stands in a room, as Set/Node of a set's catalog.ulset (GenCore/Product_Radio_DAB_Wood). Several, comma-separated or on several lines, and wildcards (GenCore/Product_Radio_*) map a family of meshes to one item. Taking such a piece in a room gives this item, and putting the item down shows the piece (the first exact one, else the first that matches). See Things in rooms

ul_Base/Items/Groceries/ holds the everyday goods (coffee, tea, juice, soda, wine, beer, cigarettes, fresh groceries, ready meals, snacks, vitamins, hair, skin and makeup products, pet food, books, films, flowers, gifts, greeting cards), and ul_Base/Shops/groceries_<venue>.ulshop sells them at each kind of venue: the supermarket, the corner shop, the chemist, the off-licence and so on. A mod adds a product with one .ulitem and an Item: line in its own .ulshop for that venue.

Clothing is not an item: garments are .ulcloth files (see Clothing), and a shop can stock them by their id too.

Things in rooms

The small things on the furniture (see Surface dressing) are objects. A click on one in a room opens a little menu:

  • Take: the player walks over and it goes into their inventory. The thing is gone from the room.
  • Pick up: they carry it in a hand (only pieces the catalog marks handheld, up to about 32 x 40 cm). Clicking it in the hand offers Put it down..., Put it back and Put it in the bag.
  • Buy (price), on a shop's own stock: the player walks over and buys it as at the till (below, Stock on the shelves).
  • At home, a click on a table top or a shelf offers Put ... here for each thing the player carries that has a room piece and fits there; the player walks over and puts it down where the click was.
  • In build mode, the OWNED tab lists the same things: see Build mode.
  • Put it down..., and a click on an item in the wallet that has a room piece: the thing follows the cursor, standing on the top or shelf of the furniture under it (the same measured surfaces the dressing uses) or on the floor. A click puts it there; a right click drops the idea.

What changed is saved with the game, per building and room: taken things stay gone, what was put down stays where it is. A venue puts out new cups after a few days (restock_days below); what came with a home never comes back by itself.

Which item a piece is

A piece named by an item's Piece: is that item. Every other piece is a generic room object, with the id piece:<Set>/<Node> (piece:GenCore/Product_Mug_Diner), described by Items/room_objects.json by kind, so the thousand product pieces need no file each:

json{
  "default":            { "category": "Misc", "value": 3, "restock_days": 2 },
  "Product":            { "category": "Misc", "value": 5 },
  "Product_Mug":        { "name": "Mug", "value": 4, "restock_days": 1 },
  "Product_Radio":      { "name": "Radio", "category": "Electronics", "value": 35, "restock_days": 7 },
  "Product_GoldBar":    { "name": "Gold bar", "category": "Jewelry", "value": 6000, "restock_days": -1 }
}
Key Meaning
<Category>_<Type> The piece's category and type in its set's catalog.ulset (Product_Mug). Read first
<Category> Then the category alone (Product, Decor, Lighting)
default Then this
name Only from the <Category>_<Type> entry. Without one, the type spaced out (CigarettePack is "Cigarette pack")
category An item category, as in .ulitem
value Worth of one, in dollars
icon A picture under a mod's Images/, used when the piece has no Thumbs/<Node>.png of its own beside its set
restock_days Days before a venue has a taken one again; negative = never

Each level only fills what the more specific one leaves out, and a later mod's room_objects.json overrides an earlier one per key, field by field. Keys starting with _ are comments.

Taking what is not yours

A thing's owner is player (in the player's home, or put down by them), home:<building id> (in someone else's home), shop:<shop id> (a shop's stock), venue (anywhere else) or public. Taking something that is not the taker's raises the propTaken event. The engine decides nothing more: a crime mod says whether it was theft, who saw it (Witness) and what follows.

Scripts reach the same things through Room, Actor:take and Actor:putDown.

Home storage

The inventory is what a person carries. Things kept at home live in its furniture: food in the fridge and the pantry, toiletries in the bathroom cabinet, clothes in the wardrobe and the drawers.

  • Buying. An item whose storage kind is a container kind (below) travels with the player, in the inventory, and remembers the day it was bought. Groceries with Stock: do the same: their portions count once they are home, not the moment they are paid for.
  • Arriving home. The player's bags are unpacked: each thing goes into a container of its kind with room, else down that kind's overflow chain (the kitchen counter, the table, a pile by the door). Clothes the player owns and is not wearing go to the wardrobe or the drawers.
  • Eating. The home actions still read and use up the stock stats (stock_meal, stock_snack...). For the player those stats are the portions stored at home: Have a meal is not offered with an empty fridge, and eating takes the oldest fresh portion. A mod that raises a stock stat without a purchase (Player:ModifyStat("stock_meal", 3)) puts that much food in the home (generic_items).
  • Expiry. Each stored lot keeps its purchase day. Past its shelf life it is spoiled: it is eaten only when nothing fresh is left, with the spoiled stat changes and the spoiledEaten event (item, stat); the player can bin it from the container's menu. Food standing in the room (on a counter, on interior shelves, or put down by hand) darkens as it ages.
  • Containers. A click on a fridge, a cupboard or a wardrobe in a home lists what is in it: Take one into the inventory, Bin what is spoiled, and, on a counter or a table, put something down. In someone else's home taking raises propTaken like any object that is not yours. Other people's homes are stocked once, the first time the player is there, from npc below.
  • Showing. Stored things with a room piece stand on the container's interior shelves (its catalog levels below the top) and on the levels of its drawers and door racks (parts[].levels), moving with them. Nothing stands taller inside than the container itself (a chest freezer's lid is a moving part, so its floor measures open to the ceiling). A container with no interior level (a bread bin, an under-bed box) or only a top level shows nothing inside: its menu lists what it holds.

Everything is saved in the slot. Changes raise storageChanged (container key, item id, delta).

Items/storage.json

json{
  "containers": [
    { "piece": "Appliance/Fridge", "kind": "fridge", "capacity": 40 },
    { "piece": "Storage/Cabinet", "room": "kitchen", "kind": "pantry", "capacity": 30 },
    { "piece": "Counter/KitchenUnit", "kind": "counter", "capacity": 12, "display": "top" }
  ],
  "item_kinds": { "stock:stock_meal": "fridge", "cosmetics": "bathroom", "default": "place" },
  "shelf_life": { "stock:stock_meal": 4, "consumable": 30, "default": 0 },
  "overflow": { "fridge": [ "counter", "table", "pile" ], "default": [ "pile" ] },
  "generic_items": { "stock_meal": "grocery_ready_meal" },
  "clothing": { "underwear": "drawer", "socks": "drawer", "default": "wardrobe" },
  "spoiled": { "effects": [ { "stat": "energy", "amount": -15 } ] },
  "decay": { "param": "baseColorFactor", "tint": [ 0.32, 0.30, 0.14 ], "slots": [ "Food" ], "curve": [ [ 1.0, 0.0 ], [ 0.2, 0.35 ], [ 0.0, 0.85 ] ] },
  "npc": {
    "empty_tags": [ "squat" ],
    "fridge": [ { "item": "grocery_ready_meal", "count": [ 1, 4 ], "chance": 0.8, "min_wealth": 0.0, "max_wealth": 1.0 } ]
  }
}
Key Meaning
containers Which home furniture holds things. piece is the catalog Category/Type (or Category) of the piece; match (a word of its node name) and room (a word of the room's name) narrow it; capacity counts packs and garments (0 = no limit); display: "top" stands things on its top (a counter) instead of inside. The first rule that matches wins; a later mod's rules are tried first
item_kinds The storage kind of an item without Storage:: by item id, then stock:<stat>, then item category (lower case), then default
shelf_life Days before an item without ShelfLife: is spoiled, looked up the same way. 0 = never
overflow Where a thing goes when no container of its kind has room, in order. pile is a heap by the door (nothing stands for it yet)
generic_items The item a stock stat's portions become when a mod raises the stat directly. By default, the first .ulitem with that Stock:
clothing The container kind for each clothing category (top, bottom, dress, outerwear, underwear, shoes, socks, headwear, accessory, default)
spoiled The stat changes for eating something spoiled
decay How stood food ages: the material vector param is blended toward base colour x tint by curve, a list of [freshness, amount] points (freshness 1 = just bought, 0 = at its date). slots: material slot name prefixes that age (Food matches Food, Food_B...); a piece with none of them ages whole
npc Other people's homes, by container kind: rows of item, count (a range of packs), chance, min_wealth, max_wealth (wealth is the room's decoration, 0 to 1). empty_tags: venue tags whose homes keep nothing

Every table but containers merges key by key across mods. Scripts read and change the player's home storage through Home.

Vehicles and the driving licence

A vehicle is an item of category Vehicle. ul_Base ships six in Items/Vehicles/ (vehicle_car, vehicle_electric_car, vehicle_van, vehicle_motorbike, vehicle_scooter, vehicle_bicycle), and a mod can hand out its own the usual way, with category = "Vehicle" and an id ending in its kind:

luaPlayer:addItem("ul_vans_camper_van", 1, { name = "Camper van", category = "Vehicle", value = 12000, mod = M })

Player:ownsVehicle()          -- any vehicle
Player:ownsVehicle("car")     -- a car: the .ulitem's Kind, else the id's last "_" part
Player:canDrive()             -- a licence and a motor vehicle (not a bicycle)
Player:hasDrivingLicence()
Player:setDrivingLicence(true) -- passed the test; false suspends it
  • The licence is the qualification ul_Base/driving_licence, so a job or a course can also ask Qualifications:has("ul_Base/driving_licence", person).
  • Owning a motor vehicle makes the map's travel times car times. The licence is not checked there, as in the legacy game.
  • Older mods that kept a car as the stat car_value or a licence as the stat driving_licence still count.

Shops: Shops/*.ulshop

iniShopName: Urban Threads
LocationTag: clothes
SellerPresetName:
OpeningHours: Mon - Sat: 09:00 - 20:00 / Sun: 10:00 - 18:00
BuybackRateMultiplier: 0.5
Item: leather_jacket, 2350, -1
Item: sunglasses, 550, 10
Item: Cute-Leggings_1_F, 40, -1
Key Meaning
(file name) The shop's id, used by Game:OpenShop
ShopName Title of the shop screen
LocationTag The venue type it belongs to (see Map and venues)
SellerPresetName A character preset for the seller
OpeningHours Text shown to the player
BuybackRateMultiplier Share of the price paid when the player sells an item back. A shop only takes what it sells itself (a supermarket will not buy a pistol), and 0 means it buys nothing
Checkout Optional: an action completed when the player leaves after paying. The grocery shops use shop_checkout (3 to 10 minutes at the till), and the scenes whose WHAT lists it play there now and then: the queue, the self-checkout, an ID check
Item: id, price, stock One line per item: a catalog item or clothing id, the price, and how many are in stock (-1 = unlimited). Each building of the venue type has its own stock, saved with the game
Restock: daily How often each building's shop fills up again: daily (the default), a number of days (Restock: 3), weekly, or never (an antiques dealer). Optionally how much of each limited item comes back: Restock: 2, 5 (five units) or Restock: 1, 50% (half the line's stock); without it, back to the full line
Furniture: Seating, Table/DiningTable or Furniture: all The generated furniture of FurnitureSets goes on sale, one item per piece, by lead category or Category/Type of the set's assemblies. See Furniture shops
FurnitureSets: GenHome, GenHome_Black The sets the furniture comes from (default GenHome)
FurniturePrice: Seating/Sofa=650, Bed=900, default=150 The price of a piece by Category/Type, then Category, then default (else 100)
Clothing: all or Clothing: Shoes, Socks The clothes of every enabled mod go on sale, all of them or only these categories (Top, Bottom, Dress, Outerwear, Underwear, Shoes, Socks, Headwear, Accessory). A garment added by any mod is on sale with no change to the shop
ClothingPrice: Top=35, Shoes=75, default=30 The price of a garment by category, for garments that set no Price: of their own

Stock on the shelves

In a shop, what it sells stands on its shelves: the items of the venue's .ulshop files that have a room piece (Piece: in their .ulitem) are put out on the empty gondolas, shelves and showcases, a run of one item after another, cheap things given more shelf than dear ones. The dressing does it with a STOCK = "shop" rule (ul_Base's shop.uldressing has one for every shop grammar), so the shelves look the same on every visit.

  • Buying an item, at the till or with Buy on the shelf, takes as many off the shelves, the ones nearest the buyer. They stay gone until the shop restocks (Restock:).
  • A limited line (Item: id, price, 4) never shows more than the building's shop has left.
  • Taking one without paying (Take) lowers the stock too, and raises propTaken with the owner shop:<shop id>: shoplifting is the crime mods' business.
  • Every purchase raises the itemBought event (shop, item, count, building).
  • Items without a piece (a garment, a lottery ticket) are sold from the shop screen only. To put a product on the shelves, give its .ulitem a Piece: line; ul_Base's groceries name the GenShop products (Piece: */Product_SixPack_*: * stands for any set, so a style variant of the set works too).
  • Displays with their goods modelled in (the Stocked gondolas, the bottle walls) are backdrop and never sell anything; a shop grammar that sells things should also place empty displays (Gondola_Double, Shelf_Product), as shop_convenience and shop_supermarket do.

Scripts read and buy the stock of the shop the player is in with Room:stock and Room:buy.

Furniture shops

ul_Base/Shops/furniture.ulshop sells the generated home furniture at the furniture venue (the showroom):

iniShopName: Furniture store
LocationTag: furniture
Furniture: Seating, Table, Storage, Bed, Desk, Lighting, Rug, Plant
FurnitureSets: GenHome_Black, GenHome_White, GenHome
FurniturePrice: Seating/Sofa=650, Seating=140, Bed/Bed=700, Rug=120, default=150
Restock: never

Each single piece of those sets is an item asm:<Set>/<Assembly id> (asm:GenHome/G_Seating_Sofa_Three_Jersey), named after the piece, category Furniture (decor, rugs and plants: HomeDecor). Bought, it is delivered home: it is in the inventory, and build mode's OWNED tab lists it (see Build mode). The pieces on display in the showroom stay where they are.

Clothes shops

A shop with a Clothing: line opens in the fitting room rather than the item overlay: the player stands large beside the rails, a click tries a piece on, and what they wear but do not own yet is the basket, paid in one go. Leaving without buying puts their own clothes back. ul_Base ships clothes.ulshop (everything) and shoes.ulshop (shoes and socks), opened by the Buy apparel action.

lualocal bought = Game:showWardrobe(Player, "shop")      -- the clothes shop of this venue
local bought = Game:showWardrobe(Player, "shop:ul_boutique")   -- a named one
-- bought: the garment ids paid for, {} when nothing was

"The clothes shop of this venue" is the one whose LocationTag is the venue's, else clothes.

The mannequins of a venue with a clothes shop wear its stock: one coordinated outfit each, which the player can buy in one click or try on in the fitting room (see Shop mannequins). A garment whose last one is sold comes off the mannequins too.

Opening a shop

A shop opens from a scene, usually one launched by an action:

lua-- Actions/shopping/ynl_browse_bar_merch.ulaction → SCENE_ALWAYS = "ynl_bar_merch"
RUN = function(Game, Player, CurrentCompanion)
	Game:OpenShop("ynl_bar_merch")
end,

The shop opens as an overlay over the current room. The player can browse, buy, sell and buy back what they sold in the same session.

Businesses the player owns

To put a business the player owns on the map (a food truck, a shop, an outlet), register it with Businesses. The legacy business simulation below is separate and driven from scenes. Game:startBusiness() takes over the one building the player is in, not every venue of its type; Game:StartBusiness(tag, ...) with a bare tag still keys a business by venue type:

Call Effect
Game:StartBusiness(tag, name, owner, salary) Start a business at a venue type
Game:StopBusiness(tag), Game:IsBusiness(tag) Close / check it
Game:startBusiness(), Game:stopBusiness() The venue the player is in becomes theirs (under the name Game:enterBusinessName() asked for just before), its staff kept on as employees; or stops being theirs
Game:AddEmployee(tag, person, salary), Actor:addEmployee(salary), Actor:removeEmployee() Staff
Game:OpenBusinessManagement(tag) Open the management overlay
Game:calculateRevenue(), calculateExpenses(), calculateWages(), calculateRent() Figures for the business the player is in
Game:ModifyReputation(n) Change its reputation

In a venue the player owns, the location reads "<tag>:business": Game:GetCurrentLocation() returns it, and an action whose WHERE lists "cafe:business" is offered there (the base game's hire, promote, train and close-down actions are written this way). The location card of an owned venue has a Manage entry that opens the management screen: the staff with their pay (raise or cut by ten percent, let go), the monthly accounts, the reputation and the takings, which come in by the day and are collected from there. These businesses are saved with the game.

The monthly figures are the legacy ones: rent is the city's cost of living times the length of the building's front, revenue is the rent times a factor from 0.1 to 5 that grows with reputation and head count (staff skills and brand do not enter it), and profit is revenue minus rent and wages. A new business with one employee usually runs at a loss until its reputation climbs.