Creation Law — Shops
You know classic Diku shop tables / sedit. Neo has no sedit. A shop is a workspace prototype keyed by an existing mobile VID, edited with @shop …, reviewed with @examine shop, then committed with @save shop.
Hub: Creation Law. Build the keeper mobile first: Mobiles. Place it: Zones. Player-facing buy/list/auction flow: Shopkeepers.
Why a shop is not the mobile
The mobile is the body players talk to. The shop row (H2 table shopkeeper) is the trade config: home room, produce list, item types traded, hours, and keeper messages. When a mobile has Acts.SHOPKEEPER, runtime looks up ShopkeeperDB.at(mobile.vid()).
So the build order is: mobile (with SHOPKEEPER) → shop workspace on that same VID → zone place → zone reset. Creating @new shop before the mobile exists fails. Forgetting the Acts flag leaves a shop row that never handles list / buy.
Prerequisites
- An
npcprototype already@saved (same VID you will use for the shop) - On
npc_stats:@npc_stats flags acts shopkeeper(and usuallysentinelso the keeper stays put) - A room that already exists for
@shop room - Item prototypes you intend to
produces(optional slots — up to 10)
Step-by-step: first shop
1. Confirm the mobile (build it if needed — Mobiles):
> @load npc_stats 1210 Okay. > @npc_stats flags acts shopkeeper Okay. > @npc_stats flags acts sentinel Okay. > @examine npc_stats > @save npc_stats Okay. >
2. Open the shop draft — VID is the mobile VID (must be in your mobile range; mobile must exist; no second shop draft while one is open):
> @new shop 1210 Okay. >
Echoes you will see on failure (exact Immortality strings):
A new shop entry already exists in your buffer.—@examine shopthen@save shopor@clear shopA shop already exists for that <mobile-vid>.No mobile with vid <n> exists.That vid is not in your range.- Bad usage line in code prints
@new show <mobile-vid>(typo for@new shop).
3. Home room (why: Shopkeeper.check refuses @save when room is 0 — the room VID must already exist in RoomDB):
> @shop room 1200 Okay. >
4. What the keeper trades (ItemType flags via @shop trades; prefix ! to clear when the modifier supports it). At least one type is required to save:
> @shop trades weapon Okay. > @shop trades armor Okay. >
Bare @shop trades reprints @shop trades (!)<flag> plus the ItemType list from code.
5. Produce list (optional but usual — stock the keeper lists / sells at a fixed cost). Positions 1–10 (Shopkeeper.MAX_PRODUCES). Cost must be ≥ 1 gold for any filled slot or @save returns producing vid:… for 0gc:
> @shop produces 1 3100 500 Okay. > @shop produces 2 3101 50 Okay. >
Syntax: @shop produces <pos> <item-vid> <cost>. The produce setter does not verify the item prototype exists at edit time — @examine shows ItemDB.proper when the item is known.
6. Hours — hours1 is required to save (open1 must be non-zero). Both start and finish for hours1 must be positive integers, and start must be strictly less than finish. hours2 is optional and may only be set after hours1:
> @shop hours1 8 12 Okay. > @shop hours2 14 20 Okay. >
7. Keeper messages (one-line writers — all six are required by Shopkeeper.check before @save):
> @shop keeper_no_item_msg I do not have that item. > @shop customer_no_item_msg You do not seem to have that. > @shop customer_no_cash_msg You cannot afford that. > @shop keeper_no_buy_msg I do not buy that sort of thing. > @shop keeper_buy_msg That will be %d coins. > @shop keeper_sell_msg Sold. >
keeper_buy_msg is passed through String.format(…, price) on several buy paths — include a %d where you want the gold amount echoed (same idea as the default string in S.keeperBuyMsg).
8. Review and commit:
> @examine shop --- Shop --- MOBILE : 1210 ROOM : 1200 HOURS1 : 8 to 12 HOURS2 : 14 to 20 --- produces --- ... --- trades --- ... --- keeper has no item --- ... > @save shop Okay. >
If @save returns a short error from Shopkeeper.check, fix the field and save again — the draft stays open until save succeeds or you @clear shop. Exact check strings include: no room is set; no item types are set; no customerNoItemMsg set (also returned when keeper_no_item_msg is missing — code quirk); no customerNoCashMsg set; no keeperNoBuyMsg set; no keeperBuyMsg set; no keeperSellMsg set; open time must be set; producing vid:<n> for 0gc.
Place the keeper via a zone
Why: the shop row alone does not spawn the mobile. Zone reset loads the npc into a room; players then use list / buy / auction / … against that keeper (Shopkeepers).
> @load zone 1299 Okay. > @zone npc 1210 1200 Okay. > @examine zone > @save zone Okay. > zone reset Okay. >
''@shop'' field commands
From ModifySocial.cmdCrShop (real subcommands only):
room <room-vid>— home / shop room (room must exist)produces <pos> <item-vid> <cost>— produce slot 1–10trades (!)<ItemType>— types the keeper will handlekeeper_no_item_msg— one-line writercustomer_no_item_msg— one-line writercustomer_no_cash_msg— one-line writerkeeper_no_buy_msg— one-line writerkeeper_buy_msg— one-line writer (often with%d)keeper_sell_msg— one-line writerhours1 <start> <finish>— required windowhours2 <start> <finish>— optional second window
Workspace companions (same pattern as rooms/items): @new shop, @examine shop, @clear shop, @save shop, @load shop <vid>, @destroy shop <vid>.
Edit an existing shop
> @load shop 1210 Okay. > @shop produces 1 3100 450 Okay. > @examine shop > @save shop Okay. >
Vs classic shop OLC
You never enter a numbered shop editor. There is no sedit. You open a shop workspace on a mobile VID, set fields with @shop, @examine, @save. Trade behavior also needs Acts.SHOPKEEPER on the mobile and a zone placement.
Common mistakes
@new shopbefore the mobile prototype exists- Mobile without
flags acts shopkeeper - Second
@new shopwhile a shop draft is still open @save shopwithoutroom, trades,hours1, or all six messages- Produce cost 0 on a filled slot
- Building the shop but never
@zone npc+zone reset - Expecting
redit-style menus — workspace only
Code notes / gaps
Documented as facts from the Java tree (not guesses):
- Help-string typos:
createNewShopusage prints@new show <mobile-vid>; badproducesargs print@new shop produces …;deleteShopusage prints@delete shopwhile the verb is@destroy;@destroy's usage line omitsshopeven though the handler accepts it. Shopkeeper.check: whenkeeper_no_item_msgis missing it still returnsno customerNoItemMsg set(copy/paste quirk).hours2validation message says “ahead of the open1 finish-hour” but the comparison usesopen1()(the start).- Runtime (quick scan of
MobileScript):trades,produces/cost,keeper_no_item_msg,keeper_no_buy_msg, andkeeper_buy_msgare used.room,hours1/hours2,customer_no_item_msg,customer_no_cash_msg, andkeeper_sell_msgare required to save and stored, but no call sites were found that gate trading on hours/room or speak those three messages — treat as save requirements / future hooks unless you prove otherwise in a later code pass.
Related
- Mobiles — SHOPKEEPER act, npc layers
- Zones — place the keeper
- Creation Law — hub