====== 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: [[immortal:creationlaw|Creation Law]]. Build the keeper mobile first: [[immortal:creationlaw:mobiles|Mobiles]]. Place it: [[immortal:creationlaw:zones|Zones]]. Player-facing buy/list/auction flow: [[player:gamelaw:shopkeepers|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 ''npc'' prototype already ''@save''d (same VID you will use for the shop)
* On ''npc_stats'': ''@npc_stats flags acts shopkeeper'' (and usually ''sentinel'' so 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 — [[immortal:creationlaw:mobiles|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 shop'' then ''@save shop'' or ''@clear shop''
* ''A shop already exists for that .''
* ''No mobile with vid exists.''
* ''That vid is not in your range.''
* Bad usage line in code prints ''@new show '' (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 (!)'' 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 ''. 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: 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 ([[player:gamelaw:shopkeepers|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 '' — home / shop room (room must exist)
* ''produces '' — produce slot 1–10
* ''trades (!)'' — types the keeper will handle
* ''keeper_no_item_msg'' — one-line writer
* ''customer_no_item_msg'' — one-line writer
* ''customer_no_cash_msg'' — one-line writer
* ''keeper_no_buy_msg'' — one-line writer
* ''keeper_buy_msg'' — one-line writer (often with ''%d'')
* ''keeper_sell_msg'' — one-line writer
* ''hours1 '' — required window
* ''hours2 '' — optional second window
Workspace companions (same pattern as rooms/items): ''@new shop'', ''@examine shop'', ''@clear shop'', ''@save shop'', ''@load shop '', ''@destroy shop ''.
===== 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 shop'' before the mobile prototype exists
* Mobile without ''flags acts shopkeeper''
* Second ''@new shop'' while a shop draft is still open
* ''@save shop'' without ''room'', 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: ''createNewShop'' usage prints ''@new show ''; bad ''produces'' args print ''@new shop produces …''; ''deleteShop'' usage prints ''@delete shop'' while the verb is ''@destroy''; ''@destroy'''s usage line **omits** ''shop'' even though the handler accepts it.
* ''Shopkeeper.check'': when ''keeper_no_item_msg'' is missing it still returns ''no customerNoItemMsg set'' (copy/paste quirk).
* ''hours2'' validation message says "ahead of the open1 finish-hour" but the comparison uses ''open1()'' (the start).
* Runtime (quick scan of ''MobileScript''): ''trades'', ''produces''/''cost'', ''keeper_no_item_msg'', ''keeper_no_buy_msg'', and ''keeper_buy_msg'' are used. ''room'', ''hours1''/''hours2'', ''customer_no_item_msg'', ''customer_no_cash_msg'', and ''keeper_sell_msg'' are **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 =====
* [[immortal:creationlaw:mobiles|Mobiles]] — SHOPKEEPER act, npc layers
* [[immortal:creationlaw:zones|Zones]] — place the keeper
* [[immortal:creationlaw:objects|Objects]] / [[immortal:creationlaw:items|Items]] — produce VIDs
* [[player:gamelaw:shopkeepers|Shopkeepers]] — player ''list'' / ''buy'' / ''auction'' / ''offer'' / ''value'' / ''identify''
* [[immortal:creationlaw|Creation Law]] — hub