Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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.
107 changes: 107 additions & 0 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
@@ -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": {"<resource>": 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.
111 changes: 111 additions & 0 deletions docs/SPEC.md
Original file line number Diff line number Diff line change
@@ -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.