Skip to content
Closed
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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 5
labels:
- "ci"
- "dependencies"
13 changes: 11 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Cache the GUT addon
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: addons/gut
key: gut-${{ hashFiles('tools/gut.version.json') }}
Expand All @@ -41,3 +41,12 @@ jobs:

- name: Run the headless smoke test
run: godot --headless --path . -s tools/smoke.gd

- name: Confirm the working tree stays clean
run: |
status=$(git status --porcelain)
if [ -n "$status" ]; then
echo "$status"
echo "Import generated untracked files. Track them or update .gitignore."
exit 1
fi
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project are listed here.
The format follows Keep a Changelog.
This project uses semantic versioning.

## [0.3.0] - 2026-08-03

Added

- Ranged combat with projectiles.
- Three new ranged monsters across the biomes.
- Line-of-sight checks so monsters cannot shoot through walls.
- Kiting behaviour that keeps ranged monsters at a distance.
- Unit tests for aim, blocking, and line of sight.
- Integration tests for firing, impact, and wall stops.

## [0.2.0] - 2026-08-03

Added
Expand Down
73 changes: 45 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Dungeonwright

A seeded dungeon crawler built with Godot and GDScript.
Every run builds a new dungeon that you can explore and finish.
A seeded dungeon crawler built with Godot.
Every run forges a new dungeon that you must conquer.

```
####.D.############
Expand All @@ -13,20 +13,29 @@ Every run builds a new dungeon that you can explore and finish.
##########.D..######
```

## What it is
## The dungeon

Dungeonwright generates a connected dungeon on every run.
You explore rooms and corridors.
You find keys, open locked doors, and reach the exit.
The same seed always builds the same dungeon.
Dungeonwright draws a new map for every run.
You explore rooms, corridors, and locked halls.
You collect keys, open doors, and reach the exit.
The same seed always forges the same dungeon.

## What this release adds

This release adds ranged combat.
Three new monsters attack from a distance.
They fire bolts, spit, and burning embers across rooms.
Close the gap or break line of sight to survive.
Dodgeable shots add tension to every corridor fight.

## Features

- A new map for every run, driven by a seed.
- Rooms, corridors, locked doors, and keys.
- Three biomes with different generation rules.
- Monsters with simple combat and balanced drops.
- Procedural pixel art with no bundled image files.
- Three biomes with distinct generation rules.
- Melee monsters and three new ranged monsters.
- Projectiles that stop at walls and locked doors.
- Procedural pixel art with no bundled images.
- A minimap, a health bar, and run summary overlays.
- Deterministic generation for replayable runs.
- Full gamepad support with analog movement.
Expand All @@ -35,17 +44,9 @@ The same seed always builds the same dungeon.

This release ships a playable demo.
The generator guarantees the exit is always reachable.
You can walk, fight, collect loot, and finish a run.
You can walk, fight, dodge, loot, and finish a run.
You can replay any run from its seed.

## This release

This release adds full gamepad support.
Every action has a gamepad binding.
Movement uses the left stick or the d-pad.
Analog input gets a deadzone and a diagonal speed cap.
Menus show the controls for the active device.

## Requirements

- Godot 4.6 or newer.
Expand Down Expand Up @@ -96,7 +97,7 @@ The tests run from the copy in `addons/gut`.

## How it works

The generator has a fixed pipeline.
The generator follows a fixed pipeline.
It places rooms, connects them with corridors, and carves the map.
It picks the farthest room as the exit.
It places doors on corridors and puts each key on the safe side.
Expand All @@ -106,13 +107,22 @@ The generation code is pure data.
It has no scene nodes, so tests run fast and deterministic.
The scene controller turns the map into a live game.

Ranged monsters keep their distance.
They fire only when they can see the hero.
A Bresenham walk checks every tile between them.
Walls and locked doors stop each projectile.

The projectile is a plain node.
It flies in a straight line toward its target.
It despawns after its range or on impact.

## Project layout

- `scripts/dungeon` holds the generator and map logic.
- `scripts/combat` holds stats, monsters, and loot tables.
- `scripts/input` holds the controls helper.
- `scripts/actors` holds the hero, monsters, and projectiles.
- `scripts/world` renders tiles and builds the minimap.
- `scripts/actors` holds the hero, monsters, and pickups.
- `scripts/input` holds the controls helper.
- `scripts/ui` builds the HUD and overlays.
- `scenes` holds the scene tree.
- `tests` holds the GUT suite.
Expand All @@ -124,31 +134,38 @@ A seed always produces the same map.
Doors never block the exit permanently.
Every key sits on the reachable side of its door.
Monsters never cross a locked door.
Projectiles never cross a wall or a locked door.

## Evaluation evidence

The suite has 68 tests.
It covers generation, biomes, combat, drops, pathfinding, and input.
All 68 tests pass in a headless run.
The suite has 85 tests.
It covers generation, biomes, combat, drops, pathfinding, input,
and ranged combat.
All 85 tests pass in a headless run.
A smoke test loads the game and spawns a fixed-seed run.
The smoke test also checks every action has a gamepad binding.
The smoke test also checks gamepad bindings and ranged rules.

## Roadmap

Done in this release:
- Ranged monsters and projectiles.

Done in earlier releases:
- Seeded generation, rooms, doors, and keys.
- Combat, monsters, and loot tables.
- Procedural art, a minimap, and run overlays.
- Full gamepad support.

Next up:
- Multi-floor descent and a depth counter.
- Ranged monsters and projectiles.
- Sound and music.
- More biomes and items.

## Limitations

The demo has three biomes.
Each run is a single floor.
All monsters use melee attacks.
Projectiles fly straight and never home in.
The game has no audio yet.

## License
Expand Down
16 changes: 16 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,25 @@ Attack range and facing arcs use tile math, not physics.

The player attacks in a facing arc.
Monsters chase, stalk, or hold ground according to their spec.
Ranged monsters fire projectiles and keep their distance.
Each monster rolls loot from a weighted `DropTable`.
Coins drop often, shards sometimes, potions rarely.

### Ranged combat

Ranged monsters follow a separate rule set in `MonsterSpec`.
They carry a minimum range, a fire range, and a bolt speed.
Each biome hosts one ranged monster type.

The monster aims only when it has line of sight.
A Bresenham walk in `Projectile.los_clear` checks every tile on the
segment. A wall or a locked door blocks the shot.

A `Projectile` node flies in a straight line.
It stops at walls and locked doors, and it despawns after its range.
The controller spawns bolts from a dedicated scene.
The pure rules live in static helpers, so tests run without a scene.

## Scene flow

The `Main` scene owns the game loop.
Expand Down
6 changes: 6 additions & 0 deletions scenes/actors/projectile.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3]

[ext_resource type="Script" path="res://scripts/actors/projectile.gd" id="1_projectile"]

[node name="Projectile" type="Node2D"]
script = ExtResource("1_projectile")
2 changes: 2 additions & 0 deletions scenes/main.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ script = ExtResource("2_view")

[node name="Effects" type="Node2D" parent="World"]

[node name="Projectiles" type="Node2D" parent="World"]

[node name="Player" parent="World" instance=ExtResource("3_player")]

[node name="Camera" type="Camera2D" parent="."]
Expand Down
Loading