Roadside Flower Stand
Status
Accepted; being built split-and-conquer, one checkpoint at a time. This is the first concrete design attached to the "Roadside stand" line in system-backlog.md (Business Expansion, status Explore) and to the business progression named in design/vision.md: "roadside stand to market stall to leased shop to branded boutique."
Checkpoint 1 (2026-08-09, 0.8.6-beta.2 candidate): vendor trigger and invitation — done. A Cup submission scoring at or above STALL.invitationScore (90) sets state.stall.invitationPending, which pauses the main tick loop (scheduleGameTimer skips ticking while it is true, independent of the ordinary pause flag) and shows a persistent, non-dismissible-by-backdrop modal with vendor dialogue referencing the actual score and medal. Accepting sets state.stall.unlocked; declining clears invitationPending without setting unlocked, so a future qualifying score re-invites. Covered by new assertions in prototype/smoke-test.cjs (trigger threshold, decline-then-retrigger, accept, and legacy-save migration for the new state.stall field).
Checkpoint 2 (2026-08-09, 0.8.7-beta.1 candidate): stock allocation, timed cycle, and settlement — done. The full v1 loop from the "First Slice" section below is implemented and verified end to end (real UI clicks, not just direct state calls): a 花摊 workspace tab appears once state.stall.unlocked, showing a per-material +/- stepper allocation UI built from getStallAllocatable() (only materials currently in stock, priced via getMaterialUnitPrice() which reuses the same harvest.coins / baseFlowers ratio the Cup market already uses for consistency). Starting a cycle (startStallCycle) deducts the sent stems immediately and rolls the entire outcome once, at cycle start, with Math.random — deterministic and save-stable exactly like the design called for, no re-roll risk on refresh. The cycle is real-wall-clock timed (STALL.cycleDurationMs, 30 minutes) rather than tick-based, so it progresses even while the player is away or the game is paused. Once the duration elapses, the tab shows a ! badge and the panel reveals a per-item sold/unsold breakdown and total revenue; settleStallCycle() pays out coins and returns unsold stems to inventory. Sell chance is a flat v1 constant (STALL.sellChance, 0.6) with no fame/season/holiday weighting yet, as scoped. Covered by new prototype/smoke-test.cjs assertions: pricing, allocatable-material filtering, rejecting an empty/invalid allocation, deterministic roll behavior via an injectable random function, inventory deduction and cycle creation, the ready-check gate (settling before the timer elapses is a no-op), payout and unsold-stem return on settlement, and legacy-save migration for the new cycle/completedCycles/totalRevenue fields.
Origin
Creator proposal, 2026-08-09: reaching a high score in the Spring Grower's Cup triggers a persistent-pause popup where a flower vendor NPC, having seen the player's prize flower at the competition, offers a consignment arrangement at their existing stall. The player sends flowers or bouquets on consignment; a new tab tracks a relative-time selling cycle; sell-through is initially random and simplified, with fame, seasonality, and named/award flowers as later refinements. Further systems (stall size, decorations, shop upgrades, staff) are explicitly deferred to keep the first loop small and working.
Why This Fits
design/vision.mdalready names this exact progression path and requires that "a player must always be able to close or downgrade the business and return to the greenhouse" — the guardrail below inherits that requirement directly.- Design Pillar 6 ("Complexity Is Earned Through Automation") wants a new system to arrive only after a prior loop is learned and produces a visible achievement. A Cup score gate is a clean, already-built way to satisfy that rather than inventing a new unlock condition.
- Design Pillar 4 ("Risk Creates Anticipation, Not Regret") and the Failure Contract in
design/event-system.mdrequire that a bad outcome still returns something and never touches the protected home greenhouse. The stand must inherit this, not reinvent it. design/progression-flows.md(Coin Reinvestment) already flags "a roadside stand" as a separate later candidate once the event preparation and central-tank sinks stop being enough — this proposal is that candidate becoming concrete.- The long relative-time cycle with no penalty for returning late is exactly the kind of idle-friendly mechanic the 2026-08-09 F12 pacing fix was chasing elsewhere in the game: a system that rewards leaving and returning instead of punishing it.
Trigger and Invitation
- Unlocks after a Spring Grower's Cup submission scores at or above a high threshold (creator suggested 90; gold medal is currently 85, so this reads as "an unusually strong gold," not an ordinary win — keeps the invitation rare and worth remembering).
- On qualifying, show a persistent-pause popup in the same family as the event invitation and wheel-reveal modals: a named vendor character, one or two lines about having seen the player's flower at the show, and an explicit accept/decline choice. Declining must be safe and reversible — the invitation should be able to reappear on a future qualifying score, not vanish forever, matching the "never a disappearing popup" rule in the event UX contract.
- Accepting unlocks a new persistent workspace destination alongside
温室 / 订单 / 图鉴 / 赛事. Naming and a compact identifying mark should follow the same convention F22 already established for种子库and温室商店— a fifth tab is real mobile navigation cost and should not reopen the discoverability problem that round fixed.
First Slice (Minimum Working Loop)
Deliberately small, matching how the Spring Cup itself first shipped as two pots and one prompt before anything else was added.
- Stock selection. From the stand tab, the player allocates currently-owned flower materials (and/or bouquets, if bouquets exist by the time this is built — otherwise raw material stems are enough for v1) into the day's consignment: which species, how many of each.
- Start the cycle. One button commits the allocation and starts a relative-time loop — a real-world duration on the order of "half an hour," not an in-game tick count, since this is explicitly not attached to active play the way Cup rounds are. No FOMO risk attaches to this timer: nothing is lost by returning late, only delayed, so it does not violate the "avoid real-time deadlines" pillar, which is about loss-under-deadline, not all use of a clock.
- Away state. The main greenhouse continues normally; the stand needs no attention and does not pause anything else, unlike Cup cultivation's optional home-garden pause.
- Settlement ready. When the cycle completes, the stand's workspace tab shows a badge/reminder in the same visual language as
eventWorkspaceBadge. Opening the tab reveals what sold. - Resolution (v1, deliberately simple). Each submitted item independently rolls whether it sold, using a flat probability with no fame, season, or holiday modifiers yet. Sold items pay out coins; unsold items return to inventory (or a small partial-compost consolation, mirroring the event market's unsold-stock rule) rather than disappearing. The roll must be made once and stored at cycle-start or cycle-completion, not recomputed on every render or refresh — reuse the exact save-stable pattern the Cup market's buyer variation already uses, so refreshing cannot reroll a bad result.
- Repeat. Starting a new cycle requires a fresh allocation; there is no auto-repeat in v1.
Explicitly Deferred From V1
- Weighted sell-through by fame, named/award-winning flowers, season, or real-world-adjacent holidays. The creator's own framing — "理论上有名的得奖的特殊的当季的花应该好卖" — is the intended direction, but v1 ships flat-random and earns the weighting system once the base loop is confirmed to work and feel worth returning to.
- Stall size / stock cap upgrades.
- Decorations or attractiveness modifiers.
- Shop tier upgrades (matches the vision.md ladder: stand to stall to leased shop to boutique).
- Staff (this would eventually be the first natural home for the "general greenhouse assistant" concept already scoped in
design/progression-flows.md, once this loop exists for them to run). - Bouquets/arrangements as a distinct sellable unit, if that system does not exist yet when this is built (Floral Arrangement Grid is still Explore-status in
system-backlog.md).
Open Questions Before Implementation
- Does the 90-score gate make this too rare to test quickly during development? May want a debug/dev-only bypass distinct from the real player-facing threshold.
- What exactly can be consigned in v1 — raw flower-material stems only, or does it need bouquets first? Raw stems are simpler and available today; bouquets are more thematically "consignment-worthy" but require a system that does not exist yet.
- Should declining the vendor's first invitation cost anything (a missed one-time bonus) or be purely neutral? Vision Pillar 4 favors neutral — declining should not read as a punished choice.
- Is 30 minutes the right cycle length, or should it scale with something (stock size, distance traveled, vendor tier later)? V1 can hardcode one value and revisit once real play data exists, the same way bloom duration shipped with one number before this session's simulation-driven revision.
- Where does this sit relative to the Cup's own six-harvest cooldown? If Cup entries are already gated by cooldown, does the stand need its own separate cadence limit, or does "you need a new qualifying score to get invited again" already provide enough of one?
Relationship to Existing Backlog
This proposal does not replace the system-backlog.md "Roadside stand" line; it gives it a first concrete shape. If accepted, update that row's status from Explore to Next/Accepted and point it at this document. design/vision.md's business progression (stand → stall → shop → boutique) and Directional Question 1 ("Does the shop remain optional, or does the game eventually become a business simulator?") remain the governing constraints for everything built on top of this first slice.