---
description: GDScript naming, formatting, and Godot-idiomatic patterns (Godot docs style guide).
globs: "**/*.gd"
alwaysApply: true
---

# GDScript style (Neon Sprawl)

Follow the [Godot GDScript style guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html). Prefer clarity and consistency with existing scripts in the repo.

CI also enforces **`gdlint`** and **`gdformat`** (see `gdlintrc` and `.github/workflows/gdscript.yml`) on **`client/scripts/`** and **`client/test/`**. When this guide and personal preference differ, follow the **linted** version below so pushed files pass CI.

## Naming

- **Functions, variables, signals:** `snake_case`.
- **Classes** (`class_name`, custom resources): `PascalCase`.
- **Constants** (`const`): `ALL_CAPS` when they are true fixed values; otherwise match Godot/node conventions used nearby.
- **“Private” / internal:** leading underscore `_like_this` for members meant as implementation detail (not enforced by the language).

## Types and API

- Add **static types** on parameters, locals, and return values where reasonable (`-> void`, `var x: int`, etc.).
- Prefer **`@export`** (Godot 4) over legacy `export` syntax.
- Use **`@onready`** for node references set after the scene tree is ready when that pattern fits.

## Formatting

- **One statement per line**; CI enforces **max line length = 100**.
- **Indentation:** use **tabs** for new Godot work (editor default). Do not mix tabs and spaces in the same file; match the file if it already uses spaces.
- **Blank lines:** separate functions with two blank lines; sparing single blank lines inside functions for logical groups.
- **Trailing commas** in multi-line collections/function args when it improves diffs.
- No trailing whitespace.

## Structure

- **`class_name`** only when the type must be referenced globally; otherwise anonymous `extends` is fine.
- Declaration order should match `gdlint` `class-definitions-order`:
  `@tool` → `class_name` → `extends` → file/class docstrings → `signal` → `enum` → `const` → static vars → `@export` vars → public vars → private vars (`_foo`) → `@onready` public vars → `@onready` private vars → everything else.
- Virtual overrides (`_ready`, `_process`, `_physics_process`, etc.): keep small; extract helpers with leading `_`.
- **Scene / main script shape:** For `client/`, follow [godot-client-script-organization](godot-client-script-organization.md) — thin `main.gd`, split picking, networking, and similar concerns into child scripts.

## CI-enforced lint rules

- **Names:** keep functions, local vars, loop vars, arguments, signals, and class vars in `snake_case`; keep classes / enums in `PascalCase`; keep enum elements and true constants in `ALL_CAPS`.
- **Signal callbacks:** `_on_Node_signal` style handlers are allowed; otherwise use normal `snake_case`.
- **Preload / load identifiers:** when storing a preloaded script or type-like handle, prefer `PascalCase` names such as `CameraStateScript`; keep ordinary constants in `ALL_CAPS`.
- Avoid duplicated `preload()`s for the same path in one file.
- Avoid bare expressions that are not assigned / returned / awaited.
- Remove unnecessary `pass`.
- Keep files reasonably small: CI enforces **max 1000 lines**, **max 20 public methods**, **max 10 function arguments**, and **max 6 returns** per function.
- Prefer early-return style. `gdlint` forbids `else` / `elif` branches that directly follow a `return`.
- Keep unused arguments intentional and named clearly if you truly need them.

## Local verification

- Before pushing GDScript changes, run the same tools CI uses:
  `pip install "gdtoolkit==4.5.0"`
  `gdlint client/scripts client/test`
  `gdformat --check client/scripts client/test`
- If formatting fails, run:
  `gdformat client/scripts client/test`
- Install the repo’s local **pre-push** hook from the repo root to enforce those checks automatically:
  `./scripts/install-git-hooks.sh`

## GdUnit test layout (AAA)

**Mandatory** for every **new or changed** test function in **`client/test/**/*.gd`** (GdUnit suites, `test_*` methods).

1. **`# Arrange`**, **`# Act`**, and **`# Assert`** — each appears **once** per test, in that order, as a full-line comment at the same indentation as the test body. **No blank line is required** after those comments; the next statement may follow immediately.

2. **Arrange** — Transports, clients, `enqueue`/`connect` setup, test data.

3. **Act** — The call(s) under test only (e.g. `request_cast`, `call` into SUT). No `assert_that` / `assert_*` in Act.

4. **Assert** — All expectations; parse or inspect results here when parsing exists only to support assertions.

`gdlint` / `gdformat` do not enforce AAA text; **review and agents** do. Match the spirit of server **`xut`** snippets: three labeled phases, one scenario per test.

## Comments

- Use `#` comments; document non-obvious **why**, not what the next line literally does.
- Doc comments: Godot 4 supports `##` above declarations for the in-editor help—use for public-ish APIs when helpful.

## Anti-patterns

- Avoid `get_node()` string paths repeated everywhere; use `@onready`, exports, or groups when scale demands it.
- Prefer typed collections and enums over “magic” string/int state when it stays readable.
