lib/doom.sh

6.7 KB · 281 lines · bash

1#!/usr/bin/env bash
2# Terminal Doom via doom-ascii (https://github.com/wojciech-graj/doom-ascii)
3
4BRT_DOOM_DIR="${HOME}/.brt/doom"
5BRT_DOOM_BIN="${BRT_DOOM_DIR}/doom-ascii"
6BRT_DOOM_SRC="${BRT_DOOM_DIR}/src"
7BRT_DOOM_VERSION="0.3.1"
8BRT_DOOM_REPO="https://github.com/wojciech-graj/doom-ascii.git"
9BRT_FREEDOOM_URL="https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedoom-0.13.0.zip"
10BRT_DOOM_IWAD="${BRT_DOOM_DIR}/freedoom1.wad"
11
12brt_doom_scaling() {
13 local rows
14 rows=$(tput lines 2>/dev/null || echo 24)
15 if (( rows >= 40 )); then
16 echo 2
17 elif (( rows >= 28 )); then
18 echo 3
19 else
20 echo 4
21 fi
22}
23
24brt_doom_write_config() {
25 local cfg="${BRT_DOOM_DIR}/.default.cfg"
26 cat > "$cfg" <<'EOF'
27# brt -doom controls (DOS scancodes; see chocolate-doom default.cfg)
28use_mouse 1
29mouseb_fire 0
30mouseb_strafe 0
31mouseb_forward 0
32mouseb_speed 0
33grabmouse 1
34mouse_sensitivity 7
35
36# Move: WASD
37key_up 17
38key_down 31
39key_strafeleft 30
40key_straferight 32
41
42# Look / turn: arrow keys
43key_left 75
44key_right 77
45
46# Space = use (classic Doom has no jump)
47key_use 57
48
49# Right Shift = fire
50key_fire 54
51
52# Run on ]
53key_speed 27
54EOF
55}
56
57brt_doom_ensure_wad() {
58 local zip tmpdir
59
60 if [[ -f "$BRT_DOOM_IWAD" ]]; then
61 return 0
62 fi
63
64 brt_require curl
65 brt_require unzip
66
67 brt_info "Downloading Freedoom WAD (first run only)..."
68 zip=$(mktemp "${TMPDIR:-/tmp}/brt-freedoom.XXXXXX.zip")
69 tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/brt-freedoom.XXXXXX")
70
71 if ! curl -fsSL --progress-bar "$BRT_FREEDOOM_URL" -o "$zip"; then
72 rm -f "$zip"
73 rm -rf "$tmpdir"
74 brt_error "Failed to download Freedoom from ${BRT_FREEDOOM_URL}"
75 brt_info "Place doom1.wad or freedoom1.wad in ${BRT_DOOM_DIR}/"
76 return 1
77 fi
78
79 unzip -qq -o "$zip" -d "$tmpdir" || {
80 rm -f "$zip"
81 rm -rf "$tmpdir"
82 brt_error "Failed to extract Freedoom archive"
83 return 1
84 }
85
86 local wad
87 wad=$(find "$tmpdir" -name 'freedoom1.wad' -print -quit)
88 if [[ -z "$wad" ]]; then
89 rm -f "$zip"
90 rm -rf "$tmpdir"
91 brt_error "freedoom1.wad not found in archive"
92 return 1
93 fi
94
95 cp "$wad" "$BRT_DOOM_IWAD"
96 rm -f "$zip"
97 rm -rf "$tmpdir"
98 brt_ok "Installed ${BRT_DOOM_IWAD}"
99}
100
101brt_doom_find_wad() {
102 local wad="${1:-}"
103
104 if [[ -n "$wad" && -f "$wad" ]]; then
105 case "$wad" in
106 ~/*) wad="${HOME}/${wad#~/}" ;;
107 ~) wad="$HOME" ;;
108 esac
109 if command -v realpath &>/dev/null; then
110 realpath "$wad"
111 else
112 (cd "$(dirname "$wad")" && printf '%s/%s\n' "$(pwd)" "$(basename "$wad")")
113 fi
114 return 0
115 fi
116
117 if [[ -f "$BRT_DOOM_IWAD" ]]; then
118 printf '%s\n' "$BRT_DOOM_IWAD"
119 return 0
120 fi
121
122 for wad in "${HOME}/.brt/doom/doom1.wad" "${HOME}/.brt/doom/DOOM1.WAD" \
123 "${HOME}/.brt/doom/freedoom1.wad"; do
124 if [[ -f "$wad" ]]; then
125 printf '%s\n' "$wad"
126 return 0
127 fi
128 done
129
130 return 1
131}
132
133brt_doom_ensure_binary() {
134 if [[ -x "$BRT_DOOM_BIN" ]]; then
135 return 0
136 fi
137
138 brt_require git
139 brt_require make
140 brt_require cc
141
142 mkdir -p "$BRT_DOOM_DIR"
143
144 if [[ ! -d "${BRT_DOOM_SRC}/.git" ]]; then
145 brt_info "Fetching doom-ascii ${BRT_DOOM_VERSION} (first run only)..."
146 git clone --depth 1 --branch "${BRT_DOOM_VERSION}" "$BRT_DOOM_REPO" "$BRT_DOOM_SRC" \
147 || {
148 brt_error "Failed to clone doom-ascii"
149 return 1
150 }
151 fi
152
153 brt_info "Building doom-ascii..."
154 if ! make -C "$BRT_DOOM_SRC" -j"$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 2)"; then
155 brt_error "Failed to build doom-ascii (need Xcode CLT or gcc)"
156 return 1
157 fi
158
159 local built=""
160 for built in \
161 "${BRT_DOOM_SRC}/game/doom-ascii" \
162 "${BRT_DOOM_SRC}/_unix/game/doom-ascii" \
163 "${BRT_DOOM_SRC}/_unix/obj/doom-ascii"; do
164 if [[ -f "$built" ]]; then
165 cp "$built" "$BRT_DOOM_BIN"
166 chmod +x "$BRT_DOOM_BIN"
167 brt_ok "Built ${BRT_DOOM_BIN}"
168 return 0
169 fi
170 done
171
172 brt_error "Build finished but doom-ascii binary is missing"
173 return 1
174}
175
176brt_doom_term_restore() {
177 [[ -w /dev/tty ]] || return 0
178 printf '\033[?25h\033[0m\033[r' >/dev/tty 2>/dev/null || true
179 stty sane </dev/tty 2>/dev/null || true
180}
181
182brt_doom_on_exit() {
183 brt_doom_term_restore
184}
185
186brt_doom() {
187 local wad_arg="" scaling rc=0
188 local -a extra_args=()
189
190 while [[ $# -gt 0 ]]; do
191 case "$1" in
192 -h|--help)
193 cat <<EOF
194brt -doom — play Doom in the terminal
195
196Controls:
197 WASD Move (forward / back / strafe)
198 Mouse move Turn (if terminal/backend supports mouse reporting)
199 Left click Fire (if terminal/backend supports mouse reporting)
200 Arrow keys Turn fallback
201 Space Use doors and switches
202 Right Shift Fire fallback
203 ] Run
204 Esc Menu
205 Ctrl+C Quit
206
207Options:
208 --wad <file> Use a specific WAD (default: Freedoom, auto-downloaded)
209 --scaling <N> Display scale 2–4 (smaller = larger; default: auto)
210
211Requires: git, make, and a C compiler on first run.
212EOF
213 return 0
214 ;;
215 --wad)
216 wad_arg="${2:-}"
217 [[ -n "$wad_arg" ]] || { brt_error "--wad requires a path"; exit 1; }
218 shift 2
219 ;;
220 --scaling)
221 extra_args+=("-scaling" "${2:-}")
222 [[ -n "${2:-}" ]] || { brt_error "--scaling requires a number"; exit 1; }
223 shift 2
224 ;;
225 *)
226 if [[ -z "$wad_arg" && -f "$1" ]]; then
227 wad_arg="$1"
228 shift
229 else
230 brt_error "Unknown option: $1"
231 brt_info "Run: brt -doom --help"
232 exit 1
233 fi
234 ;;
235 esac
236 done
237
238 brt_doom_ensure_binary || exit 1
239 brt_doom_write_config
240
241 local iwad
242 if ! iwad=$(brt_doom_find_wad "$wad_arg"); then
243 brt_doom_ensure_wad || exit 1
244 iwad=$(brt_doom_find_wad "") || {
245 brt_error "No WAD file found"
246 exit 1
247 }
248 fi
249
250 local scaling_flag
251 scaling_flag=$(brt_doom_scaling)
252 if ((${#extra_args[@]} == 0)); then
253 extra_args=("-scaling" "$scaling_flag")
254 fi
255 extra_args+=("-chars" "block" "-kpsmooth" "8")
256
257 brt_info "WAD: ${iwad}"
258 brt_info "Renderer: unicode block cells (-chars block)"
259 brt_info "Controls: mouse move/click (when supported) · arrows/Right Shift fallback · WASD move · Ctrl+C quit"
260
261 trap 'brt_doom_on_exit' EXIT INT TERM
262
263 set +e
264 (
265 cd "$BRT_DOOM_DIR" || exit 1
266 exec "$BRT_DOOM_BIN" \
267 -iwad "$iwad" \
268 "${extra_args[@]}"
269 )
270 rc=$?
271 set -e
272
273 brt_doom_term_restore
274
275 if (( rc == 0 || rc == 130 || rc == 143 )); then
276 return 0
277 fi
278 brt_error "Doom exited with code ${rc}"
279 return "$rc"
280}
281