CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
Echo Garden — a Godot 4.6 peaceful plant-growing game. Players tend a 6×6 grid of cells, watering seeds through growth stages and discovering special seed types as rewards.
Commands
Run the game (desktop):
godot --path /Users/wenxinfang/game-lab/echo-garden
Headless web export → GitHub Pages:
cd /Users/wenxinfang/game-lab/echo-garden
./deploy.sh
deploy.sh exports the game, commits to ../github/wxfang.github.io/echo-garden/, and pushes. It checks for web export templates first and exits with instructions if missing.
Install web export templates (one-time, if missing):
python3 /tmp/fetch_web_templates.py # downloads ~84 MB via ZIP range requests
Or: open the Godot editor → Editor → Export Templates → Download and Install.
Export only (no push):
godot --headless --export-release "Web" /path/to/out/index.html --path .
Architecture
All game logic lives in two scripts. There are no scene hierarchies, assets, or autoloads — the entire UI is built in code.
garden_cell.gd (class_name GardenCell, extends Button)
Self-contained cell node. Owns its state and all visuals.
- State:
stage: int(0–5, usesStageenum),waters: int,seed_type: int,neighbor_support: int - Visuals: Background color via
StyleBoxFlatoverrides set in_refresh(). Plant shapes drawn in_draw()using onlydraw_circle,draw_line,draw_rect— no fonts or child nodes. Water progress is adraw_rectbar at the bottom. - Signals:
wants_plant(emitted when EMPTY cell is clicked — garden decides what seed to plant),acted(cell, event, message)(emitted after every state change) plant(type)vsplant_silent(type):plant()emitsacted;plant_silent()is used by the garden for spreading and does not emit.effective_waters_needed(): ReturnsWATERS_NEEDED[stage]minus 1 ifneighbor_support >= 2(minimum 1). Garden setsneighbor_supportafter each turn.- Font warning: Do NOT use
Labelchild nodes orButton.textfor game symbols in web export. Godot subsets fonts at build time and strips dynamically-assigned glyphs. All plant visuals must go through_draw().
garden.gd (extends Control)
Root scene script. Owns the grid, seed inventory, and all game rules.
_cells: Array[GardenCell]— flat array of 36 cells, row-major (index = row * 6 + col).- Seed inventory:
_bag: Dictionary(SeedType → count). COMMON is unlimited (not tracked)._selected: intis the active seed type. Seed panel buttons appear lazily as seeds are discovered (_discovered: Array[bool]). - Turn flow: Every cell interaction calls
_on_cell_acted→ updates neighbor support → checks spreading/awakening (on “bloomed”) → checks milestone rewards. Multiple systems may overwrite_status.textin sequence; the last one wins. - Neighbor support:
_update_neighbor_support()scans all 36 cells after every action (O(36), trivially fast). Setscell.neighbor_supportand callscell._refresh()only when the value changes. - Spreading: On “bloomed”, each orthogonal empty neighbor has 30% chance of
plant_silent()with the source’s seed type. - Awakening: When all 36 cells are simultaneously
BLOOMED, a goldenTween-animated overlay flashes and seeds are awarded. Debounced by_last_awakening_turn(min 10 turns between triggers). - Reward milestones: Ember every 3rd harvest; Frost first time 5+ plants are ≥SPROUT; Twilight when any row has no EMPTY cells; Radiant 25% chance on harvesting a non-COMMON bloom.
Web Export Notes
export_presets.cfgtargetsres://garden.tscnand injects<script src="coi-serviceworker.js">viahtml/head_include.coi-serviceworker.jsinwxfang.github.io/echo-garden/addsCross-Origin-Opener-PolicyandCross-Origin-Embedder-Policyheaders via a service worker. GitHub Pages cannot set these headers natively; they are required for Godot’s audio worklet.- The
.wasmis ~36 MB. The.pckis small (~23 KB) because the project has no binary assets.