Stage 07 — What's missing: requirements, no code
You will build: nothing, on purpose. This stage is the fade the almanac does at the end of
every curriculum — a skeleton with the bodies missing, then requirements with no code at
all. Each item below is a system the architecture doc already designed (saving "built
later", the reserved states, the day_advanced wire that doesn't exist yet) and that the
next version of the game needs. Write them yourself, against these requirements, and use the
shelves they point at as the general treatment.
This is also the stage where you stop being a tutorial's reader. Everything after this line is a project.
The requirements#
Harvest#
The UseItemState currently plants on an empty cell and does nothing on an occupied one
(stage 04's guard, _plots.has(cell) → return false). It needs to distinguish the two
occupied cases:
- Acting on a cell with a mature crop removes the plot and its view, and the crop goes
somewhere (the inventory, below). The removal is
Farm.harvest(cell) -> CropData— the data comes back out of the field, the view is freed, and_plotsloses the key. - Acting on a cell with an immature crop does nothing observable — but it should do nothing legibly: the 2D game's answer was a distinct state, and the state machine's diagram (stage 02) has room for it. Whether "too early" is a state, a warning, or a sound is a design call — make it and record it.
- The maturity test is
plot.stage == plot.crop.stage_meshes.size() - 1— the same "mature is a state, not a speed" fact from stage 06, now load-bearing.
The honesty test applies: after harvesting every crop on the field, _views is empty and
_plots is empty and replanting works — the farm is a machine that returns to zero.
Inventory#
The architecture doc's reserved node (Interfaces → Inventory, an empty Control) is where
this lands. Requirements, in the inventory shelf's order —
data first, grid last:
Inventory extends Resourceholding{CropData: int}— the 2D game's stage 06 shape, verbatim. It emitschangedon every mutation; it knows nothing about UI.- Harvest (above) is the only writer in this version:
Farm.harvestreturns the data, the player (or the farm, your call — the 2D game's call) adds it to the inventory. - The UI is an observer of
changed: a list of "Turnip ×3" lines, rebuilt on the signal. The UI never mutates the inventory; it renders it. When a second observer appears (a crafting panel, a sell price), the signal's shape is already right. - The never-mutate rule governs this directly: the inventory's keys
are the shared
CropDatadefinitions. Ten harvested turnips are one key with a count of ten — never ten keys, and never a mutated copy of the definition carrying the count.
A real day cycle#
Stage 06's day is a key press. The game's day is a clock:
TimeSystem— the project's first non-debug autoload (the architecture doc's deferral, cashed in): a day length in seconds (a design number, say 120), a running day counter, and the signal the architecture doc reserved —day_advanced— emitted once per day.Farmsubscribes:day_advanced→advance_day(). The central tick (stage 06) is now driven by a system instead of a key, and the key stays —Debug.register(KEY_F1, ...)now calls the sameadvance_dayas a skip action. Debug and the real clock coexist because they call the same door; that's the payoff of the central tick over the per-crop-listener alternative (the 2D game's decision 3, again).- The day counter is savable state (below) — a
TimeSystemthat rebuilds itself from nothing on load is the save & load shelf's opening audit failing in public.
Saving#
The architecture doc designed this in one paragraph; the requirements are that paragraph, enforced:
- What's saved:
_plotsas{cell: {crop id, stage, days_grown}}, the player's position andfacing, the day counter, the inventory's contents. A version field from day one — the 2D game's rule, because a save without a version is a migration you'll do at 2 a.m. instead of now. - What's not saved:
_views, the camera, any tween, the debug registry. Views are rebuilt by walking_plots— the stage 04 proof (delete every view, lose nothing) is the load path. - Where:
user://save.json(or.tres— the save & load shelf's second part argues the case; JSON keeps the save human-readable, which is worth a lot while the format is still changing). - The test: save at day 3 with a mixed field, quit, reload, and the field is identical
— same crops, same stages, same
days_grown(check the number, not just the mesh; two carrots at stage 1 can have differentdays_grownand the mesh won't show it). Then the third part's question: what breaks when you add a crop to the catalogue after a save exists? The version field is the answer; make the break first.
Shapeshift — the nested machine#
Stage 02's reserved ShapeshiftState is where the state machine shelf's fourth part earns its 3D form:
ShapeshiftStatecontains aStateMachinechild (possible becauseStateMachine extends State— stage 02's whole point). The inner machine runs the shapeshift's own states (which form? the design is yours — the requirement is only that the outer machine sees one state and the inner machine runs many).- Entering shapeshift must suspend the walk/run states' claims on the player's movement —
the inner machine's states call
player.movewith the shapeshift's parameters, and the outer machine'scurrentis the shapeshift, so nothing else ticks. Thefinishedsignal's shape (stage 02) is how the inner machine reports "done" back to the outertravel(&"IdleState"). SynthesizeState— the stage 02 trap, still script-less in the reference scene — gets a script or gets deleted. The machine's assertion (stage 02) only fires when no states register; a half-built state is the quiet kind. Choose loudly.
The closing self-review#
The survivor curriculum's stage 09 has the four questions;
they transfer unchanged. For every script in this project — State, StateMachine, the
five player states, Player, Farm, Plot, CropData, Crop, Debug — answer:
- What is this file's one job?
- What does it know about the rest of the game, and through which doors?
- What would break if this file were deleted?
- What would break if it were duplicated?
And the project's final honesty test, the one the 2D game's save stage set and this one
inherits: delete every Crop node and the player's Interfaces and the camera, and name
what information was lost. The answer is "none" — and saying it, with the deletion
actually run, is the difference between a farm you built and a farm you assembled.
The code reference is the map of the shipped shape — read it against
what you've built, and where it names something you didn't (the Phantom camera's damped
follow, the PhantomCameraHost's process priorities), that delta is your next sitting.