Goals & Quests (Zoo Goals MVP)
Status: BUILT — Option A (Lightweight "Zoo Goals") selected by the owner (human_question 21869, decision_topic zoo_quests_scope). DESIGN FINALIZED. Java implementation shipped under (see § Build Stat
Last updated: October 28, 2026
Zoo Simulator — Goals & Quests (Zoo Goals MVP)
Status: BUILT — Option A (Lightweight "Zoo Goals") selected by the owner (human_question 21869, decision_topic zoo_quests_scope). DESIGN FINALIZED. Java implementation shipped under (see § Build Status). The design task (22110) changed no game-engine code; added the engine code.
Created: 2026-06-13 (scoping proposal) · Finalized: 2026-06-18
Driver: player feedback 8748 + 8751 (both ZOO_SIMULATOR, same customer AMAUJE3...)
Sibling docs: index.md, simulation.md, management.md, economy.md, expansion-model.md, daily-goals.md
The Ask
Two feedback rows asked, in plain words, how to "quest" in Zoo Simulator:
| fb | text (verbatim) | status |
|---|---|---|
| 8748 | "I am joined the game so far I am a zoo simulator player how can I have a quest in zoo simulator" | VIEWED |
| 8751 | "how do I do a question zoo simulator I am really enjoying the game so far" ("do a question" = ASR for "do a quest") | QUEUED |
Both rows carry customer_id = amzn1.ask.account.AMAUJE3... — the same engaged new player asking twice in two days. That is a genuine, actionable signal (an enthusiastic new player wants a sense of direction and there is no surface that answers them), but it is thin demand — it argues for the smallest thing that satisfies the ask, not a content-heavy quest system.
The Real Gap: Legibility, Not a Missing System
Zoo Simulator already has deep, multi-track progression — the player simply cannot ask about it or see it framed as things to work toward:
| Existing progression track | Where it lives | Surfaced as a "goal"? |
|---|---|---|
| Overall Zoo Score 0–1000 (welfare 30% / satisfaction 40% / conservation 30%) | , ScoringPanel | No — shown as a number, never as "reach X next" |
| Conservation goals (premium animals, species diversity, habitat upgrades) | No | |
| Breeding milestones (first rare variant, generation depth) | rareVariantsDiscovered, highestGeneration |
No |
| Daily visitors record / attendance growth | lastDayVisitorCount, totalVisitors |
No |
| Zoo inspection pass (score ≥ 400 AND welfare ≥ 50) | No — fires as a random event, never as a stated target | |
| Monthly seasons + leaderboards (top-3 → Golden Tickets + cosmetics) | , management.md § Monthly Seasons | Partially — season status reads current rank, not framed as "a goal to chase" |
| Training behaviors, zone/theme buildout | training/construction services | No |
The player who asked "how do I quest" is really asking "what should I be working toward, and how do I know I'm making progress?" Every answer already exists in the simulation — it is just never spoken back as named, askable objectives. This is a discoverability/legibility gap, not a missing game system.
Decision: Lightweight "Zoo Goals" (Option A)
Ship a thin Goals surface that makes the existing progression legible and askable. Goals are dynamically computed from current ZooAccount state — there is no authored quest content, no content pipeline, and no quest state machine. The player asks "what are my goals" and hears the 3–5 most relevant next objectives, each with a concrete target, current progress, and a small Zoo Bucks reward on completion.
Chosen over a full quest system (Option B, deferred — see § Alternative Considered) because:
- Demand is thin — one new player asking twice. Build the smallest thing that answers them; don't commit to a content treadmill on a single-player signal.
- The progression already exists — the only missing piece is surfacing it. A goals readout closes exactly the gap the player hit.
- It does not foreclose a fuller quest system — lightweight goals are the natural Phase 1; authored quests can layer on top later (the goals readout becomes the "active quests" readout) with nothing undone.
- A full quest content pipeline would compete with seasons. expansion-model.md makes seasons the content engine (themed events + leaderboards + Heritage Mode). A second authored-content system would duplicate that and split content-production effort. Goals point at seasons rather than competing with them.
- Zoo-Bucks-only rewards keep it clear of the IAP funnel. The goals system never grants Golden Tickets — it points the player at the season ladder for GT, preserving the premium funnel.
Reconciliation with Daily Goals (the `daily` ritual) — READ THIS
Zoo Simulator already ships a separate "Daily Goals" surface (daily-goals.md, BUILT under ): themes the shared Daily framework (../../engine/daily-framework.md). The two surfaces are distinct systems and must stay distinct:
| Daily Goals (BUILT) | Zoo Goals (this doc) | |
|---|---|---|
| Nature | ONE rotating goal that resets at the UTC date boundary; daily login ritual + streaks | A standing list of 3–5 PERSISTENT progression objectives the player works toward over many days |
| Backed by | shared Daily framework () | standalone / — NOT a , NOT part of the Daily framework |
| Voice entry | daily / claim daily / start daily / daily streak (Tier 6 BASELINE_DAILY*) |
goals / zoo goals / quest / quests / objectives / what should I do / what now (ZOO_VIEW_GOALS) |
| Reward | soft Zoo Bucks base + Golden Tickets at the 7-day streak milestone | Zoo Bucks only — NEVER Golden Tickets |
Naming decision (locked). The word "goals" is shared in spoken English, but the two surfaces own different NLU keywords: the Daily ritual owns daily*; Zoo Goals owns goals/quest(s)/objectives. The originating feedback ("how do I quest") is about long-term direction, which is the persistent Zoo Goals surface — so the bare keyword goals routes to ZOO_VIEW_GOALS (persistent objectives), and daily stays bound to the Daily provider. To prevent player confusion, the Zoo Goals readout speaks of "objectives / what to work toward" and explicitly cross-references both the daily ritual ("Say daily for today's goal.") and the season ladder ("Say season status to chase golden tickets."). The build MUST NOT add any daily* synonym to ZOO_VIEW_GOALS, and MUST NOT modify .
Player-facing surface (voice)
A new action ZOO_VIEW_GOALS → , reached by NLU synonyms:
"what are my goals", "goals", "zoo goals", "what should I do", "what do I do next", "what now", "any quests", "do I have a quest", "is there a quest", "how do I quest", "objectives", "my objectives", "what should I work on"
(The player's own phrasings — "how can I have a quest" / "how do I do a quest" — must be covered; "quest" routes to the same goals readout. Mind the Tier 4 feed prefix-swallow gotcha and the existing Zoo NLU patterns in command-processing.md; none of these synonyms begin with "feed", but the rows must be inserted as direct_match=1 ZOO-context overrides, mirroring the way feedback was rescued from the feed swallow.)
Example readout (voice-rule compliant — no parens, brackets, slashes, or abbreviations; numbers spoken as words). The readout is one breath: the count and the next goal lead, the next goal carries its distance, two more are named once without their progress, and any beyond those are counted, "and 2 others" ():
"You have three goals to work toward. Next up: reach a zoo score of five hundred, with four hundred twenty seven so far. After that, own two zones and breed your first animal. They pay 500 to 1,500 Zoo Bucks. For Golden Tickets, place in the top three of this month's season, say season status. Say daily for today's goal."
Goals finished since the last check are paid before the readout in one line, however many there are: one goal is named ("Goal complete. You hired your first veterinarian. Here are 500 Zoo Bucks."), and more are counted ("Six goals finished since you last checked, worth three thousand Zoo Bucks, collected."). is that line.
Goal model (computed, not authored)
Each goal is a small record computed at read time and at advance-day:
| Field | Meaning |
|---|---|
goalKey |
Stable identifier, e.g. zoo_score_400, unique_species_10, first_rare_variant |
description |
Voice-friendly target line |
target |
The numeric/boolean threshold |
current |
Player's current value (read live from ZooAccount) |
zbReward |
Small Zoo Bucks payout on completion (see scaling) |
completed |
Whether already met + rewarded (idempotency, via completedGoalKeys) |
picks the 3–5 most relevant uncompleted goals for the player's current state: early-game players see "hire your first veterinarian" / "own three species"; advanced players see "reach zoo score eight hundred" / "discover five rare variants".
Goal catalog (MVP — ~10–12 templates, every source VERIFIED against `ZooAccount`)
No new mechanics. Each template reads an existing accumulator. Field provenance was verified against and the scoring services during the design pass — stored fields have a Lombok getter; computed values are not stored and must be derived at read time (reuse the scoring computation, do not duplicate it):
| Goal template | Source | Stored? | Tiers |
|---|---|---|---|
| Reach zoo score X | getHighestZooScore() (Integer) |
STORED | 250, 400 (inspection), 600, 800, 1000 |
| Reach N visitors in a day | getLastDayVisitorCount() (Integer) |
STORED | 25, 50, 100, 200, 400, 800 |
| Own N unique species | uniqueSpeciesCount — COMPUTED (local Set in ; iterate zones → habitats → animals) |
computed | 3, 5, 10, 15, 20, 25, 30, 35 |
| Breed your first rare variant / discover N | getRareVariantsDiscovered() (Integer) |
STORED | 1, 3, 7, 12, 20 |
| Breed to generation N | getHighestGeneration() (Integer) |
STORED | 2, 3, 5, 8, 12 |
| Own N zones | getZones().size() |
STORED (Map) | 2, 4, 6, 8, 12 |
| Hire your first veterinarian | getVeterinarians() (Integer) |
STORED | 1 (early-game) |
| Train N behaviors | getTotalBehaviorsTrained() (Long) |
STORED | 1, 5, 10, 25, 50, 100 |
| Breed N animals total | getTotalAnimalsBorn() (Long) |
STORED | 1, 10, 50, 100, 250, 500, 1000 |
| Pass a zoo inspection | getZooScore() ≥ 400 AND getAnimalWelfareScore() ≥ 50 |
STORED | boolean |
| Place top 3 in this month's season | season standing | n/a | informational pointer (no ZB; routes to season system for GT) |
| Commission N prestige megaprojects | getOwnedMegaprojects().size() |
STORED (Set) | 1, 3 (endgame Zoo Bucks sink; ties the ladder to the megaproject system) |
Computed-value note for the build:
uniqueSpeciesCountandpremiumAnimalCountare NOT stored onZooAccount— they are computed inside / . A goal that reads either must compute it fromgetZones() → getHabitats() → animals(factor a shared helper or reuse the scoring path; do not duplicate the logic). Zone count =getZones().size(); habitat count = sum ofzone.getHabitats().size().
Reward model — Zoo Bucks ONLY
- Small ZB payout per goal completion, scaled to difficulty (e.g. 500 ZB for early goals up to ~5,000 ZB for a zoo-score-1000 milestone). Modest by design — a direction nudge, not an economy lever.
- Reward sizing is deliberately front-loaded to early-game goals. The Zoo Bucks economy is heavily inflated for endgame players (the leaderboard dominance audit recorded a 24-billion-ZB top zoo — see economy.md § Monthly Leaderboard Seasons), so a flat ZB reward is meaningless to whales but meaningful to a new player whose starting balance is 5,000 ZB. That is the intended target: the goals surface is a new-player onboarding/direction aid, so the rewards that actually fire are the early ones. The system makes no attempt to be an economy lever for established players — it points them at the season ladder instead.
- Never Golden Tickets. The GT path is the season ladder; the goals readout points the player there ("place top three this season for golden tickets") but the goals system itself issues only Zoo Bucks. This protects the ISP/premium funnel exactly as the task requires.
- Reward grant is idempotent — each
goalKeypays once. MVP stores completed keys as aSet<String> completedGoalKeysonZooAccount(no new table — it serializes into the existing account JSON blob, matching the verifiedglossaryTermsExplained/ownedMegaprojectsSet<String>precedent: declare with@Builder.Default = new java.util.HashSet<>()). Granting uses.addZooBucks(account, amount, reason)(the in-sim soft-currency write-through path) — NOT /purchase_transactions(the audited Golden-Ticket path) and NOT (out-of-band admin grants).
Completion detection
- During advance-day Phase 7 cleanup, immediately after in (verified at ~line 297, before the
DailySimulationResultis built), checks every active goal; any newly-met, not-yet-rewarded goal adds itsgoalKeytocompletedGoalKeys, grants its ZB viaaddZooBucks, and queues a one-line announcement into the day summary: "Goal complete, you reached a zoo score of four hundred. Here are two thousand zoo bucks." - The day summary speaks the first three completions in full, four when there are exactly four. Past
that the rest roll into one line that says how many and what they paid together: "Plus 2 more
goals completed today, worth 2,750 Zoo Bucks." Every reward is heard, named or summed, and the
balance the summary speaks already includes them.
DailySimulationResult.goalCompletionscarries the paid goals, not their lines, so the roll-up can sum them. - Also checked on-demand when the player asks "what are my goals" (so a goal met outside advance-day still pays and is acknowledged on the same turn).
- The
completedGoalKeysmembership check guarantees a goal pays exactly once regardless of which path (advance-day vs on-demand) detects completion first.
Discovery (so the player finds it without being told)
- One-line nudge in the help menu and on first launch: "Say goals to see what to work toward next."
- Occasional post-advance-day trailer when the player has uncompleted goals.
- Onboarding starter line mentions it.
MVP build scope (the follow-up build epic's contract)
ZOO_VIEW_GOALSadded toFreeTextDeclarationAction; ; computing 3–5 active goals from state.- ~10–12 dynamic goal templates keyed off the existing accumulators in § Goal catalog (no new mechanics, no authored content). Reuse the scoring path for the two computed values.
Set<String> completedGoalKeysonZooAccountfor idempotent ZB rewards via — mirror theglossaryTermsExplained/ownedMegaprojectsdeclaration exactly.- Completion check after
calculateAllScoresin (Phase 7) + on-demand atviewGoals. - NLU rows (incl. "quest"/"quests"/"objectives" phrasings) as
direct_match=1ZOO-context overrides + help entry + onboarding nudge; respect thefeed-prefix gotcha + reverse-order options matching; do NOT touch thedailysurface. - Voice-rule compliance (no parens/brackets/slashes/abbreviations; numbers spoken as words).
- scenario_tests (authored via
add_scenario_test, hermetic): "what are my goals" returns a goals list; a goal completes once and pays Zoo Bucks once (idempotent — re-running does not re-pay); "quest" routes to the goals readout. Rebuild local backend + run the ZOO_SIMULATOR scenario suite green before release. - On ship, update this doc's status to BUILT with the shipped class names + scenario_test ids; do NOT run
design_sync(it deletes registry rows for docs it cannot see).
Estimated effort: ~1 epic. No new DB table, no new content pipeline, no GT exposure.
Discovery, Onboarding & Endgame Surfacing (RVF group 164)
The Goals system shipped correct but under-discovered (7 of 8 mid players never found it) with no endgame ladder and a thin daily-return loop. This pass made the existing progression legible across the whole session without adding a new system — findings F1, F4, F8, F9, F10 of RVF group 164:
- F8 discoverability.
goalsis now a first-class option on every after-action response () and on the returning-director launch (, spoken pointer + option). It is no longer a hidden verb. - F10 advance-day legibility. When an advance-day tick completes NO goal, the day summary closes
with a one-line next-goal teaser (, wired from
after
buildSummary) so every tick advances something legible. When a tick DOES complete a goal, the existing completion announcement already carries that payoff. - F9 while-you-were-away recap. The returning-director launch recaps the last simulated day
(attendance + earnings from the persisted
lastDay*snapshot) plus a count of animals needing care, then points at the next objective — giving the daily return loop a payoff and a hook. - F1 onboarding to a first animal. nudges a director who has built a
zone/habitat but owns no animal to
buy animal penguin; REFRAMES an advance-day on a zero-animal zoo (no dead-tick simulation) toward buying a first animal, so the acquire→visitors→revenue loop closes in the first session. Guarded by scenario_test 1862 (adjusted)- 2540 and / .
- F4 endgame ladder + "what to do next". The tier ladders now extend into endgame mastery tiers
(see the Goal catalog) so the richest zoos never hit a goal-ladder void, plus a
megaprojectstemplate; when the ladder IS exhausted, points the player at the two open-ended endgame surfaces — the monthly season ladder (season status) and prestige megaprojects (megaprojects). Endgame tier rewards are direction, not economy (see the reward model).
Alternative Considered: Full Quest System (Option B — DEFERRED)
A "real" quest system would add discrete authored quest content: named quests with narrative framing, possibly multi-step chains, possibly NPC quest-givers (Zoo already has the named-staff/visitor-highlight NPC sketch from ), a quest state machine, a content-authoring pipeline, and distinct quest rewards (still Zoo-Bucks-only per the constraint).
Why deferred:
- Thin signal. One player asking does not justify a content pipeline.
- Competes with seasons. expansion-model.md already commits seasons as the content engine; a parallel quest-content system duplicates that and splits content-production effort.
- Content treadmill. Authored quests need ongoing authoring forever to stay fresh; dynamic goals never go stale because they recompute from live state.
- Lightweight goals are a strict subset. If demand grows, authored quests layer cleanly on top of the goals surface. Nothing in Option A has to be undone.
If demand grows enough to revisit Option B, it should still route all rewards through Zoo Bucks (never Golden Tickets) and be sequenced AFTER the expansion-model season infrastructure (Phase 3) so quests and seasons share one content/reward spine rather than competing.
Build Status
| State | Detail |
|---|---|
| Scoping | DONE — (2026-06-13) |
| Owner decision | DONE — Option A (lightweight) chosen via human_question 21869 (decision_topic zoo_quests_scope) |
| Design finalized | DONE — (2026-06-18): this doc; field sources verified against source; daily-ritual reconciliation locked |
| Java implementation | BUILT — . New: , , action ZOO_VIEW_GOALS, Set<String> completedGoalKeys on ZooAccount, helper. Hook: Phase 7 (after calculateAllScores) + DailySimulationResult.goalCompletions + ; on-demand at viewGoals. Help/onboarding nudge in . Tests: JUnit (idempotent payout, never Golden Tickets), scenario_tests 2211/2212/2213 un-skipped and GREEN on prod and local as of 2026-06-23. ZOO scenario suite 336/336 green. Deploy-coupled NLU rows are now LIVE on prod and local (registry + handler + 21 one-shot inserted, NLU + actionHandler caches cleared, prod routing verified end-to-end; -06-23). The dark-on-prod gap is CLOSED (see § Deploy-Coupled NLU Rows). |
No game-engine code was changed by the design task (22110).
Deploy-Coupled NLU Rows (run at deploy, AFTER the code reaches prod)
STATUS — DONE 2026-06-23. The Java shipped to prod (commit 52c372abe4 is an ancestor of the prod-deployed SHA), so the INSERT block below was run on BOTH prod and local, the NLU + actionHandler caches were cleared on prod, scenario_tests 2211/2212/2213 were un-skipped, and prod routing was verified end-to-end (goals and quest both reach the goals readout). Kept below as the canonical record of what the deploy-coupled step does.
ZOO_VIEW_GOALS is a NEW action with a NEW handler, so its NLU rows are code-gated: they must
be inserted into prod only AFTER the Java ships, or prod's old-code servers would route goals to a
non-existent handler. The rows are already staged in the LocalDB stack (where the deploy gate runs);
the scenario_tests 2211/2212/2213 are skip_test=true until this insert lands. At deploy:
INSERT INTO game_process.process_action_registry (story_action, process_context, action_description)
VALUES ('ZOO_VIEW_GOALS','ZOO_SIMULATOR','ZOO SIMULATOR - View persistent progression goals');
INSERT INTO game_process.process_action_handlers
(story_action, game_type, handler_bean, handler_method, wrapper_order, requires_combat, requires_conversation, enabled, fallback_action, description)
VALUES ('ZOO_VIEW_GOALS','ZOO_SIMULATOR','zooGoalsHandler','viewGoals',100,0,0,1,'','Lightweight Zoo Goals readout with idempotent Zoo Bucks rewards');
INSERT INTO game_process.process_one_shot_values (process_context, opening_text, action_key, direct_match) VALUES
('ZOO_SIMULATOR','goals','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','zoo goals','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','view goals','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','my goals','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','show goals','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','list goals','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','check goals','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','current goals','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','what are my goals','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','objectives','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','my objectives','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','what should i work on','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','quest','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','quests','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','any quests','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','do i have a quest','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','is there a quest','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','how do i quest','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','what should i do','ZOO_VIEW_GOALS',1),('ZOO_SIMULATOR','what do i do next','ZOO_VIEW_GOALS',1),
('ZOO_SIMULATOR','what now','ZOO_VIEW_GOALS',1);
Then clear the prod NLU cache (DELETE /api/cache/nlu/clear on prod with Authorization: Ghost <admin-jwt>, or cache caffeine_clear_nlu target:'prod')
and un-skip scenario_tests 2211/2212/2213. The registry row also satisfies the
FreeTextDeclarationActionTest#testAllEnumValuesExistInProcessActionRegistry invariant for the new enum value.
Cross-References
- index.md — Zoo overview; Subsystem Design Docs table (this doc is a row)
- daily-goals.md + ../../engine/daily-framework.md — the SEPARATE
dailyritual this surface must not conflate with or break - management.md § Monthly Seasons — the existing season ladder the goals surface points at for Golden Tickets
- expansion-model.md — seasons as the content engine (the reason a parallel quest content pipeline is deferred)
- economy.md — Zoo Bucks vs Golden Tickets split + the leaderboard dominance audit (the reward-sizing rationale)
- — NLU pipeline + the
feed-prefix-swallow gotcha that any new Zoo NLU rows must respect - player feedback 8748 + 8751 (same customer AMAUJE3...) — the driver