veratown-prototype

Shop Region

Runtime configuration receipts

The runtime heartbeat reports the shared rules revision actually applied to shop access policy during a command or location interaction. Construction and a rules read alone are not adoption. A known new shared snapshot remains pending until that policy is applied, including disabled policies. Missing/invalid rules report a fixed safe error identifier and retain historical last-loaded details, without falling back to them to authorize purchases. The receipt does not prove that all existing quotes/favors use that revision, that itemsets were adopted, or that a BC appearance change succeeded.

The shop is a separate, room-configured region for handheld drink purchases, consented item releases, and self-service timed favors. All commands and quotes are private whispers. A member must be observed in the configured shop region to use the shop; the shop never reuses the casino location binding. Global and scoped shop help is shown only while the current shop.rules configuration is enabled. When disabled, all shop help entries are omitted; the command handlers still enforce their runtime policy.

The helperPresentation setting controls reviewed public Chat/Emote interactions; it is enabled by default and selects helper-a. Set helperPresentation.enabled to false to disable helper flavor. Interaction trigger keys and text live on helper-template documents in MongoDB, not in code or task payloads. The supervisor remains the transaction authority and always privately whispers the command result, including prices, balances, quote/accept codes, and future room codes. Helper tasks contain only a template ID and operational identifiers; they never contain the player’s command, response text, purchase details, or codes. The helper must be enabled, capable, and observed in the shop region, and the template must be enabled for that same region. If the helper is unavailable, the supervisor whisper remains the response; for an accepted drink, the existing supervisor emote is also used when helper unavailability is confirmed. An ambiguous helper enqueue does not trigger a second public emote.

Launch behavior

Each active favor has one replaceable deadline. Completion still reads current observed appearance; cancellation on stop or order finalization prevents a stale timer from treating a past order as successful.

Prices are deliberately small relative to the casino’s 20-credit daily entry grant: drinks cost 2–3 credits, the default release baseline is 2 credits per item, and the default five-minute favor pays 10 credits. Shop configuration bounds offer prices and payouts to 1–500 credits; a per-member daily favor cap, cooldown, and one-active-favor constraint apply. These are credits, not chips with real-world value.

The shop does not call the economy’s Hot Seat forfeitAvailable operation. An early stop forfeits only the favor’s unpaid reward; it never seizes a member’s existing balance.

Region and room configuration

At startup, shop.rules is inserted into prototypeGameConfig.rooms.main only if absent. The runtime does not create a region because its geometry must be chosen against the live room map.

  1. Use /bot world location create as a room admin to create a distinct, enabled region location with key shop and the shop’s map geometry. Do not reuse a casino location.
  2. Review shop.rules with /bot world config show room main.
  3. To enable adult offers, set adultsOnlyRoomConfirmed only after confirming the room policy. For a release offer, explicitly set release.enabled, allowed groups, price, and lock policy.
  4. Store any itemset favor definition as an enabled document in prototypeItemSets; the shop repository is read-only. The referenced set must contain no locks.

Room rules have schema version 1:

{
    "schemaVersion": 1,
    "enabled": true,
    "adultsOnlyRoomConfirmed": false,
    "regionLocationId": "shop",
    "helperPresentation": { "enabled": true, "agentId": "helper-a" },
    "drinks": [
        {
            "id": "house",
            "label": "House drink",
            "priceCredits": 2,
            "asset": "GlassFilled",
            "emote": "*The barkeep serves {name} a house drink.*"
        }
    ],
    "release": {
        "enabled": false,
        "pricePerItemCredits": 2,
        "allowedGroups": ["ItemArms", "ItemLegs", "ItemFeet"],
        "lockPolicy": "unlocked_only"
    },
    "favors": [
        {
            "id": "unclothed-5m",
            "kind": "unclothed",
            "label": "Stay unclothed for 5 minutes",
            "durationMs": 300000,
            "payoutCredits": 10,
            "requiredEmptyGroups": ["Cloth", "ClothLower"]
        }
    ],
    "limits": {
        "maxActiveFavorsPerMember": 1,
        "favorCooldownMs": 86400000,
        "maxFavorPayoutPerDay": 20,
        "acceptCodeTtlMs": 60000
    }
}

The supervisor inserts 24 reviewed example interactions into prototypeHelperTemplates with $setOnInsert; existing records and operator edits are never overwritten. The Admin Web Helper templates resource provides audited, version-checked create/read/update/delete operations. Set the optional triggerKey to one of the shop.* triggers listed in the helper-bot guide. Each template must be enabled and use the shop region, with type: "Chat" or "Emote"; helper-a must have the matching communication or emote capability. Helper presence and template location are revalidated before a task is queued, then the worker rechecks them before dispatch. Shop flavor is best-effort and may be skipped while the helper is busy; the private supervisor reply is unaffected.

shop.room_rental and shop.room_code templates are seeded as flavor examples for future work, but are not currently invoked and do not implement room rentals. Any future rental/room-code operation must stay in the supervisor-owned shop service and authorize retrieval against the renter’s member number before whispering the code. A public helper template must never include a room code.

To add an itemset favor, use kind: "itemset" with an itemSetKey, durationMs, and payoutCredits. All writes go through the existing revision-checked world config command and are validated before persistence. Invalid or missing rules fail closed. Favor itemsets must be fixed definitions: conditional variants are rejected along with locks and strip groups.

Commands

Commands work as /bot shop ... or !shop ... in the shop region:

Command Behavior
shop / shop help Location-scoped command help
shop list Lists available offers and prices
shop buy <drinkId> [memberNumber] Quotes a drink for self or a recipient
shop release [group\|all] [memberNumber] Quotes eligible releases; recipient consent is required
shop favor <favorId> Quotes a self-service favor
shop accept <code> Accepts a recipient-bound quote
shop stop Stops the caller’s active favor without payout
shop status Shows balance, active favor, and pending orders

Quotes expire quickly and are discarded when their buyer or recipient leaves the region. A rules revision change invalidates existing quote codes. Purchase reservations are reconciled against fresh BC appearance observations; the bot does not blindly retry an uncertain mutation.

Persistence and recovery

MongoShopRepository owns prototypeShopOrders in the selected runtime database. Purchase orders persist the buyer, recipient, exact item identities, price, rules revision, and lifecycle status. Favor orders persist the pinned terms, deadline, applied item identities, and terminal result. Stable operation IDs make reserve, settlement, refund, and payout replay-safe.

Active and unresolved orders have no TTL. Finalized orders receive an expiresAt and expire after 60 days. The repository creates a partial unique index that allows one applying, active, or releasing favor per member. After restart, active favors are voided because their evidence window was interrupted; bot-applied items are removed only when the member is observed again. This feature uses the shared appearance action and action journal and never patches BC snapshots optimistically.

The plug-in removes location and BC listeners before service shutdown; a failed command disposer cannot skip stopping the service. Partial startup releases registrations and reports the original error alongside any cleanup failure.