diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..56da396 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to Windowstead + +Windowstead is a tiny Godot 4 desktop-overlay colony sim. Contributions are welcome — this is a small, focused project and easy to jump into. + +## Prerequisites + +- **Godot 4.2.2** (editor or command-line). The repo ships a Linux binary at `.tools/Godot_v4.2.2-stable_linux.x86_64` for quick local testing. +- A text editor or IDE with GDScript support. +- Git (for PRs). + +## Running locally + +```bash +# Using the shipped Linux binary +./.tools/Godot_v4.2.2-stable_linux.x86_64 --path . + +# Headless smoke test (automated) +./.tools/Godot_v4.2.2-stable_linux.x86_64 --headless --path . --script res://tests/test_runner.gd +``` + +## Project structure + +``` +project.godot # Godot project root +scenes/main.tscn # Main UI scene (grid, sidebar, HUD) +scripts/main.gd # Core game loop, rendering, worker AI +scripts/game_state.gd # Save/load autoload singleton +theme/theme.tres # Theme resources +tests/test_runner.gd # Headless smoke tests +export_presets.cfg # Linux/Windows/macOS export presets +``` + +The game is intentionally monolithic: one scene, two scripts. Everything lives in `main.gd` — the tick loop, worker task selection, rendering, and UI wiring. `game_state.gd` handles persistence (desktop `user://` or web `localStorage`). + +## Dev workflow + +1. **Branch from `main`.** Use descriptive names: `fix/issue-NN-short`, `feat/short-description`. +2. **Make one focused change.** Windowstead is small, but scope discipline keeps PRs reviewable. +3. **Smoke test before pushing.** Run the headless test at minimum. If you touch rendering or UI, launch the editor and verify the game runs. +4. **Open a PR.** Link the issue number in the PR body. + +## Testing + +The smoke test runs the game headless and exercises the save/load cycle: + +```bash +./.tools/Godot_v4.2.2-stable_linux.x86_64 --headless --path . --script res://tests/test_runner.gd +``` + +If your change touches persistence, add a corresponding test to `tests/test_runner.gd`. + +## PR process + +- PRs target `main`. +- Link the related issue in the PR description. +- Keep descriptions concise: what changed and why. +- No merge conflicts expected on `main` — pull before pushing. + +## Code style + +- GDScript idioms: `const`, `var`, `func`, `@onready` for node references. +- Use `String()` casts when concatenating with `+` or `%` formatting. +- Keep `main.gd` readable — it's ~800 lines of dense logic. Comments help. + +## What to work on + +Check the [issues](https://github.com/joryirving/windowstead/issues) for open work. Small fixes are great starting points. If you want to propose something new, open an issue first. diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..0c6d25f --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,107 @@ +# Design — Windowstead + +## Architecture + +Windowstead is a Godot 4 desktop-overlay colony sim built around a single scene and two scripts. + +### Scene graph + +``` +Control (root) +├── Backdrop (panel with transparent bg) +│ └── Margin → Root (HBoxContainer) +│ ├── Left (VBoxContainer) +│ │ ├── Title / Subtitle / Activity labels +│ │ ├── WorldPanel (PanelContainer) +│ │ │ └── WorldGrid (GridContainer — tile buttons) +│ │ │ └── WorldOverlay (Control — worker sprites) +│ │ └── SidebarScroll (ScrollContainer) +│ │ ├── Build buttons +│ │ ├── Save / New Game / Settings buttons +│ │ ├── Priority controls (rank up/down) +│ │ ├── Tick speed slider +│ │ ├── Dock side selector +│ │ ├── Focus mode toggle +│ │ └── Zoom slider +│ └── CrewList (VBoxContainer — worker status labels) +└── HudMenuButton / HudHint +``` + +The world grid is dynamically generated at runtime — `build_world()` creates a tile for each grid cell with icon, amount, progress, and worker-sprite sub-nodes. Workers are rendered as animated pixel-art sprites on `WorldOverlay`, interpolated between tiles each frame. + +### Scripts + +| Script | Role | +|--------|------| +| `scripts/main.gd` | Game loop, rendering, worker AI, UI wiring, state management (~800 lines) | +| `scripts/game_state.gd` | Autoload singleton for save/load (desktop `user://` + web `localStorage`) | + +### Persistence + +`game_state.gd` exposes `save_game()`, `load_game()`, `save_settings()`, `load_settings()`, and `clear_game()`. Desktop builds write JSON to `user://windowstead.save` and `user://windowstead.settings`. Web builds use `localStorage` via `JavaScriptBridge`. + +Save format is a single JSON dictionary with keys: `tick`, `harvested`, `resources`, `priority_order`, `workers`, `tiles`, `builds`, `next_build_id`, `events`, `save_version`. + +### Window behavior + +The window is borderless + always-on-top by default. Transparent window mode is enabled when the platform supports it; otherwise it falls back to a compact frameless window positioned near a screen edge. Three anchor modes: right, left, bottom — each with different grid dimensions, tile sizes, and sidebar layouts. + +## Data model + +### State dictionary + +``` +{ + "tick": int, + "harvested": {"wood": int, "stone": int, "food": int}, + "resources": {"wood": int, "stone": int, "food": int}, + "priority_order": ["build", "haul", "gather"], + "workers": [ + { + "name": String, + "pos": {"x": int, "y": int}, + "prev_pos": {"x": int, "y": int}, + "carrying": {"": int}, + "task": {"kind": String, "target": {"x": int, "y": int}, "resource": String, "build_id": int}, + "break_ticks": int + } + ], + "tiles": [ + {"kind": String, "amount": int, "resource": String, "build_kind": String} + ], + "builds": [ + {"id": int, "kind": String, "pos": {"x": int, "y": int}, + "delivered": {"wood": int, "stone": int}, "progress": float, "complete": bool} + ], + "next_build_id": int, + "events": [{"tick": int, "text": String}], + "save_version": int +} +``` + +### Tile kinds + +| Kind | Meaning | +|------|---------| +| `ground` | Empty, buildable | +| `tree` | Wood resource node (amount depletes) | +| `rock` | Stone resource node | +| `berries` | Food resource node | +| `stockpile` | Central resource hub | +| `foundation` | In-progress build | +| `hut` / `workshop` / `garden` | Completed structures | + +### Structure progression + +``` +hut (unlocked) → workshop (needs hut) → garden (needs workshop) +``` + +Each structure has build costs in wood and stone. Workers must haul resources to the stockpile first, then deliver to the build. + +## Save format + +- Versioned with `save_version` (currently 1). +- On load, version mismatch triggers a colony reset. +- Layout compatibility is checked: tile array size must match current grid, worker/build positions must be in bounds. +- Settings are stored separately from game state. diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..e9c6e2d --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,111 @@ +# Spec — Windowstead + +## Gameplay overview + +Windowstead is a tiny autonomous colony sim running as a desktop overlay. Two workers — **Jun** and **Mara** — manage resources, construct buildings, and keep the settlement fed. The player sets priorities and places buildings; the workers handle the rest. + +## Resource system + +Three resources: **wood**, **stone**, **food**. + +Resources exist in two pools: +- **Resources** — the player's stockpile (central storage). Workers haul here first. +- **Harvested** — tracked separately, used for the food economy. + +### Resource nodes + +The world grid is seeded with: +- **Trees** (wood) — 6 units each, deterministic placement via hash function +- **Rocks** (stone) — 5 units each +- **Berries** (food) — 4 units each + +Nodes deplete as workers gather. Empty nodes become `ground` tiles. + +### Ambient events + +Every 66 ticks (~1 minute at normal speed), a random event fires: +1. **Trail mix** — Food +2 from a neighbor +2. **Break** — A random worker takes a 6-tick break +3. **Supply drop** — A new resource node spawns on an empty tile + +## Economy / building costs + +| Structure | Wood | Stone | Unlock requirement | +|-----------|------|-------|-------------------| +| Hut | 6 | 2 | None (always available) | +| Workshop | 4 | 6 | Hut must be complete | +| Garden | 3 | 1 | Workshop must be complete | + +### Structure bonuses (on completion) + +| Structure | Bonus | +|-----------|-------| +| Hut | Food +1 | +| Workshop | Unlocks garden; +0.16 build speed to other structures | +| Garden | Food +3 | + +### Build process + +1. Player clicks a build button → placement mode activates +2. Player clicks an empty `ground` tile → build is queued +3. Workers follow priority order to complete builds: + - **Gather** resources from nodes + - **Haul** resources to stockpile + - **Deliver** resources to the build site + - **Build** — once all costs are delivered, workers spend ticks on progress + +Build speed: base 0.34 progress per tick. Workshop completion adds +0.16 to non-workshop builds. + +## Worker AI + +### Task priority + +Workers choose tasks by priority order, configurable via UI (rank up/down for each task type). Default: **build → haul → gather**. + +### Task selection + +For each priority level, workers: +1. Gather available tasks of that kind +2. Sort by Manhattan distance (closest first) +3. Pick the nearest task + +### Task types + +| Kind | Behavior | +|------|----------| +| **Gather** | Move to resource node, collect 1 unit, then haul to stockpile | +| **Haul** | Carry resource from stockpile to build site (if build exists) or back to stockpile | +| **Build** | Move to building site, add progress per tick | + +### Worker states + +- **Working** — executing a task +- **Idle** — no task assigned (rare — workers always find something to do) +- **Breaking** — `break_ticks` > 0, skipped each tick until it reaches 0 + +### Movement + +Workers move one tile per tick along the shortest Manhattan path. Position interpolation is animated on `WorldOverlay` using eased lerp between `prev_pos` and `pos`. + +## Tick system + +- **Base tick**: 0.9 seconds +- **Speed settings**: Slow (×1.6), Normal (×1.0), Fast (×0.65) +- **Focus mode**: ×2.5 multiplier (slows everything) +- **Event interval**: Every 66 ticks (~60 seconds at normal speed) + +## UI layout + +Three dock anchors (configurable): +- **Right** — 30×5 grid, sidebar on right (default) +- **Left** — 7×16 grid, sidebar on left (vertical orientation) +- **Bottom** — 30×5 grid, sidebar below (compact mode) + +Each anchor has different tile sizes, padding, and sidebar dimensions. + +## Save / load + +- Auto-saves every tick (via `persist()`). +- Manual save via UI button. +- Load checks version compatibility and layout bounds. +- New game clears all state and re-seeds the world.