deadair 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ dist/
5
+ *.egg-info/
@@ -0,0 +1,136 @@
1
+ # DEAD AIR — design notes
2
+
3
+ > **Spoilers.** This document describes the mechanics, the escalation, and the
4
+ > shape of the endings. If you would rather play it cold, stop here.
5
+
6
+ ## What's implemented
7
+
8
+ **Act One: the park.** The game opens at the trailhead at 18:40 with 85
9
+ minutes of usable dusk and no cave in sight. Five locations, five people to
10
+ talk to, eleven things to examine — and nowhere near enough light to do all
11
+ of it. Talking costs 3 minutes, examining costs 2-4, walking costs 6-9. Close
12
+ work (a clue costing 4 minutes) cannot be done below 25% daylight at all, so
13
+ the ground you cover early is ground you actually get.
14
+
15
+ The helmet lamp starts **off** — it is daylight, and switching it on early
16
+ tells you nothing. `F` toggles it, and once the day is gone it buys back a
17
+ description layer the dusk took away. Underground `F` is refused: the lamp is
18
+ why you are alive down there.
19
+
20
+ This is deliberate teaching. The park runs exactly the mechanic the cave will
21
+ later charge you for — a light budget that buys you description — using a
22
+ resource you cannot die from spending. By the time the lamp starts draining
23
+ you already know what running out of light feels like.
24
+
25
+ You are cleared to descend once you have Trammell's brief and have spoken to
26
+ Wren's mother. Everything else in Act One is optional and none of it changes
27
+ a mechanic; it changes what you understand while a mechanic is happening to
28
+ you. See PAYOFFS.
29
+
30
+ **The bridge.** Taking the descent runs the night ground search past you in
31
+ three paragraphs, resets the clock, and puts you on the lip of the sink at
32
+ 02:14, which is where the game used to start.
33
+
34
+ **Light as a rendering budget.** Every room is written in three layers —
35
+ arm's length, the room, and beyond. As the cell drains you stop being *told*
36
+ what is there. Below 25% the far layer is gone; below 8% you get one sentence
37
+ about rock. Dimming the beam doubles your endurance and costs you a layer, so
38
+ the meter is a real decision rather than a countdown.
39
+
40
+ **Resources.** Lamp (burns per minute, two spare cells hidden in the cave),
41
+ air (CO₂ pools in the low passage; a fast crossing is survivable, loitering
42
+ is not), rope (60 m, spent permanently when you rig a pitch — and you need
43
+ 20 m in reserve for a route that closes behind you), radio signal (by depth).
44
+
45
+ **A cave that answers back.** Ambience is keyed to a hidden dread band driven
46
+ by depth and by what you have seen. Deep in, `L` stops returning cave noises
47
+ and starts returning *your own past actions* — the game logs what you did in
48
+ each room and plays it back at you from the wrong direction, hours late.
49
+
50
+ **Escalation with teeth.** The first half is procedure: rigging, survey tags,
51
+ radio checks that work. Things go wrong structurally, not with a jump scare —
52
+ the radio degrades, then degrades incorrectly, then is clear at sixty metres
53
+ of limestone and saying something impossible. Taking the find starts a
54
+ 115-minute pursuit clock, and the way you came in is no longer open.
55
+
56
+ **Seven endings.** Two ways out, five ways not to.
57
+
58
+ The two you survive resolve against the clock. Act Two starts at 02:14 and a
59
+ run is anywhere from twelve minutes (straight back out of the Letterbox) to
60
+ most of the night, so nothing about the exit can be written down in advance:
61
+ `SKY` and `ARRIVAL` in `state.py` are indexed by `Game._sky_band()`, which
62
+ knows roughly where September twilight falls at this latitude. Coming out
63
+ after Trammell's 06:00 turnaround adds a line, because you were given a
64
+ turnaround time in front of the family and the game should notice you missed
65
+ it. A helmet run at best speed is 2h43 and puts you out at 04:57.
66
+
67
+ **Graphics that carry the mechanic.** Every room has a viewport above the
68
+ prose, and it is not decoration. Scenes are described as signed-distance
69
+ fields and raymarched at runtime, then lit by your actual lamp — a headlamp
70
+ at the eye with a cone, inverse-square falloff, and a reach that scales with
71
+ what is left in the cell. Losing the far prose layer and watching the picture
72
+ pull in are the same event. Above ground the light is the sky instead, and it
73
+ goes out on the same schedule the dusk does.
74
+
75
+ ## Layout
76
+
77
+ deadair/content.py the park, the cave, the prose, the tables — pure data
78
+ deadair/state.py resources, hazards, dread, endings — pure logic
79
+ deadair/art.py scene geometry and the renderer — pure rendering
80
+ deadair/app.py Textual widgets, meters, viewport, the survey map
81
+
82
+ Content is fully separated from mechanics, so writing is edited without
83
+ touching the engine. A new room is a `Room(...)` in `ROOMS` plus an `Exit`
84
+ pointing at it; give it `mx`/`my` grid coordinates and it appears on the
85
+ survey map automatically, and a `Scene(...)` in `art.SCENES` under the same
86
+ id and it draws itself. A room with no scene falls back to a generic passage
87
+ rather than failing.
88
+
89
+ Act One rooms carry `people` (a `Person` with `beats` consumed one per ask)
90
+ and `clues` (a `Clue` costing minutes and granting a flag), and are tagged
91
+ `park` so they stay off the survey map and out of the passage count.
92
+ `Game.choices()` returns people, then clues, then exits as one numbered list;
93
+ in Act Two the first two are empty and it collapses to the exits, which is
94
+ what it always was.
95
+
96
+ ## The renderer
97
+
98
+ `art.py` has five scene kinds — `tube`, `chamber`, `hole`, `forest`, `void` —
99
+ and one shading pipeline. A `Scene` is about twenty numbers: passage radii,
100
+ bedding relief, chamber extents, tree and canopy counts, fog, lamp reach,
101
+ palette, camera tilt.
102
+
103
+ Notes for anyone changing it:
104
+
105
+ - The SDF is displaced by noise, which breaks the Lipschitz bound, so the
106
+ march steps at 0.62 of the reported distance underground. Above ground the
107
+ dominant surface is an exact plane and it strides at 0.92 in fewer steps —
108
+ worth about a second a frame.
109
+ - The march runs on a one-octave SDF and the normals are taken with two. All
110
+ the visible texture comes from the normals; the march only needs to know
111
+ roughly where the rock is.
112
+ - Chambers are smooth-minimum boxes. A hard corner reads instantly as
113
+ architecture rather than cave.
114
+ - Frames are cached on `(room, light band, sky band, w, h)` — quantised, or
115
+ the cache would never hit — rendered on a worker thread, and the rooms
116
+ reachable from where you are standing are drawn before you walk into them.
117
+ A forest frame costs about two seconds, a cave frame well under one.
118
+
119
+ ## Tuning
120
+
121
+ Constants at the top of `state.py`: `LAMP_BURN_HIGH`, `LAMP_BURN_LOW`,
122
+ `AIR_DRAIN`, `ROPE_TOTAL`, `PURSUIT_LIMIT`, `BYPASS_ROPE`, and for Act One
123
+ `DAYLIGHT_TOTAL`, `PARK_START`, `CLOSE_WORK`.
124
+
125
+ Act One is tuned so that a thorough player sees roughly two thirds of it.
126
+ Raising `DAYLIGHT_TOTAL` past about 110 lets you exhaust the park, which
127
+ costs the act its only source of pressure.
128
+
129
+ A clean run to the bottom and back is about 3 hours of game time and lands
130
+ you at the surface around 10–20% lamp.
131
+
132
+ `Game(seed=N)` makes the ambience deterministic for testing, and `--seed N`
133
+ threads one in from the shell — `deadair --seed 7`, or `./play --seed 7`. The
134
+ seed is held on the app, so `N` for a new run reseeds identically rather than
135
+ drifting. It fixes only the RNG draws; the cave, the prose and the endings
136
+ are not procedural.
deadair-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Austin Guidry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
deadair-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.5
2
+ Name: deadair
3
+ Version: 1.0.0
4
+ Summary: A first-person cave-horror game for the terminal, with scenes raymarched at runtime and lit by your own lamp.
5
+ Project-URL: Homepage, https://github.com/AustinGuidry/dead-air
6
+ Project-URL: Source, https://github.com/AustinGuidry/dead-air
7
+ Project-URL: Issues, https://github.com/AustinGuidry/dead-air/issues
8
+ Author: Austin Guidry
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: game,horror,interactive-fiction,terminal,textual,tui
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Games/Entertainment
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: numpy>=1.26
24
+ Requires-Dist: pillow>=10.0
25
+ Requires-Dist: textual-image>=0.13
26
+ Requires-Dist: textual>=8.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # DEAD AIR
30
+
31
+ A first-person cave-horror game for the terminal. You are a search-and-rescue
32
+ officer entering an unsurveyed sink in Piney Ridge National Park, nineteen
33
+ hours behind a solo caver who did not come out.
34
+
35
+ It opens above ground. You have one evening of light to walk the approach,
36
+ read the ground, and talk to the people who saw Wren Alcott last — and then
37
+ you go down the hole.
38
+
39
+ Every room draws itself. The scenes are raymarched at runtime and lit by your
40
+ actual lamp, so the picture loses reach and detail as the cell dies, for the
41
+ same reason the prose does.
42
+
43
+ Built with [Textual](https://textual.textualize.io/). Runs in kitty — the
44
+ graphics use the kitty protocol, and fall back to sixel and then to Unicode
45
+ half-blocks elsewhere, so it plays in any terminal and looks best in one that
46
+ can draw. Ghostty, WezTerm and Konsole speak the protocol too.
47
+
48
+ ## Install
49
+
50
+ Needs Python 3.12 or newer. [pipx](https://pipx.pypa.io) is the tidiest way —
51
+ it keeps the game and its dependencies in their own environment and puts
52
+ `deadair` on your PATH:
53
+
54
+ pipx install git+https://github.com/AustinGuidry/dead-air
55
+
56
+ Or with plain pip, ideally into a virtualenv:
57
+
58
+ pip install git+https://github.com/AustinGuidry/dead-air
59
+
60
+ Then, from anywhere:
61
+
62
+ deadair
63
+
64
+ Pass `--seed N` to fix the run's coin-flips — the same seed always gives you
65
+ the same cave noises. `--help` lists the options.
66
+
67
+ ## Running from a clone
68
+
69
+ If you would rather have the source to hand:
70
+
71
+ git clone https://github.com/AustinGuidry/dead-air
72
+ cd dead-air
73
+ python3 -m venv .venv
74
+ .venv/bin/pip install -r requirements.txt
75
+ ./play
76
+
77
+ ## Controls
78
+
79
+ | key | action |
80
+ |-------|--------|
81
+ | `1`–`9` | take the numbered action — someone to talk to, something to look at, or a way on |
82
+ | `L` | listen |
83
+ | `R` | radio basecamp |
84
+ | `X` | look again |
85
+ | `F` | switch the helmet lamp on or off |
86
+ | `D` | stop the beam down — half the burn rate, half the sight |
87
+ | `C` | swap in a spare cell |
88
+ | `N` | new run |
89
+ | `Q` | quit |
90
+
91
+ `F` is for above ground — you start in daylight with the lamp off, and the
92
+ evening does not wait for you. `D` and `C` are underground only, where the
93
+ lamp is not something you get to switch off.
94
+
95
+ Take your time in the first few rooms. The cave teaches you what it wants
96
+ before it asks you for anything.
97
+
98
+ You cannot see everything in the park before the light goes, and that is the
99
+ point of the park.
100
+
101
+ ## Design notes
102
+
103
+ Mechanics, escalation, module layout, and tuning constants are documented in
104
+ [DESIGN.md](DESIGN.md) — **that file spoils the game**, so read it only if you
105
+ are here to work on the code rather than to play.
106
+
107
+ ## License
108
+
109
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,81 @@
1
+ # DEAD AIR
2
+
3
+ A first-person cave-horror game for the terminal. You are a search-and-rescue
4
+ officer entering an unsurveyed sink in Piney Ridge National Park, nineteen
5
+ hours behind a solo caver who did not come out.
6
+
7
+ It opens above ground. You have one evening of light to walk the approach,
8
+ read the ground, and talk to the people who saw Wren Alcott last — and then
9
+ you go down the hole.
10
+
11
+ Every room draws itself. The scenes are raymarched at runtime and lit by your
12
+ actual lamp, so the picture loses reach and detail as the cell dies, for the
13
+ same reason the prose does.
14
+
15
+ Built with [Textual](https://textual.textualize.io/). Runs in kitty — the
16
+ graphics use the kitty protocol, and fall back to sixel and then to Unicode
17
+ half-blocks elsewhere, so it plays in any terminal and looks best in one that
18
+ can draw. Ghostty, WezTerm and Konsole speak the protocol too.
19
+
20
+ ## Install
21
+
22
+ Needs Python 3.12 or newer. [pipx](https://pipx.pypa.io) is the tidiest way —
23
+ it keeps the game and its dependencies in their own environment and puts
24
+ `deadair` on your PATH:
25
+
26
+ pipx install git+https://github.com/AustinGuidry/dead-air
27
+
28
+ Or with plain pip, ideally into a virtualenv:
29
+
30
+ pip install git+https://github.com/AustinGuidry/dead-air
31
+
32
+ Then, from anywhere:
33
+
34
+ deadair
35
+
36
+ Pass `--seed N` to fix the run's coin-flips — the same seed always gives you
37
+ the same cave noises. `--help` lists the options.
38
+
39
+ ## Running from a clone
40
+
41
+ If you would rather have the source to hand:
42
+
43
+ git clone https://github.com/AustinGuidry/dead-air
44
+ cd dead-air
45
+ python3 -m venv .venv
46
+ .venv/bin/pip install -r requirements.txt
47
+ ./play
48
+
49
+ ## Controls
50
+
51
+ | key | action |
52
+ |-------|--------|
53
+ | `1`–`9` | take the numbered action — someone to talk to, something to look at, or a way on |
54
+ | `L` | listen |
55
+ | `R` | radio basecamp |
56
+ | `X` | look again |
57
+ | `F` | switch the helmet lamp on or off |
58
+ | `D` | stop the beam down — half the burn rate, half the sight |
59
+ | `C` | swap in a spare cell |
60
+ | `N` | new run |
61
+ | `Q` | quit |
62
+
63
+ `F` is for above ground — you start in daylight with the lamp off, and the
64
+ evening does not wait for you. `D` and `C` are underground only, where the
65
+ lamp is not something you get to switch off.
66
+
67
+ Take your time in the first few rooms. The cave teaches you what it wants
68
+ before it asks you for anything.
69
+
70
+ You cannot see everything in the park before the light goes, and that is the
71
+ point of the park.
72
+
73
+ ## Design notes
74
+
75
+ Mechanics, escalation, module layout, and tuning constants are documented in
76
+ [DESIGN.md](DESIGN.md) — **that file spoils the game**, so read it only if you
77
+ are here to work on the code rather than to play.
78
+
79
+ ## License
80
+
81
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ from .app import main
2
+
3
+ if __name__ == "__main__":
4
+ main()