@muhammedaksam/opentui-doom 0.3.0 → 0.3.5
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.
- package/README.md +29 -13
- package/package.json +2 -2
- package/src/doom-engine.ts +17 -0
- package/src/doom-input.ts +55 -44
- package/src/doom-mouse.ts +129 -0
- package/src/index.ts +36 -2
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- **Full DOOM gameplay** in your terminal
|
|
8
8
|
- **High-resolution rendering** using half-block characters (▀) for 2x vertical resolution
|
|
9
|
+
- **Mouse aiming** - Turn and fire with your mouse (enabled by default)
|
|
9
10
|
- **Keyboard input support** with WASD and arrow keys
|
|
10
11
|
- **Save/Load game** support - saves persist to `~/.opentui-doom/`
|
|
11
12
|
- **Sound effects and music** via mpv
|
|
@@ -68,20 +69,34 @@ Place the WAD file in the project root.
|
|
|
68
69
|
bun run dev -- --wad ./doom1.wad
|
|
69
70
|
```
|
|
70
71
|
|
|
72
|
+
To disable mouse aiming:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bun run dev -- --wad ./doom1.wad --mouse false
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Debug Mode
|
|
79
|
+
|
|
80
|
+
To run with debug logging enabled (outputs to `debug.log`):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bun run dev:debug -- --wad ./doom1.wad
|
|
84
|
+
```
|
|
85
|
+
|
|
71
86
|
## 🎮 Controls
|
|
72
87
|
|
|
73
|
-
| Action | Keys
|
|
74
|
-
| ----------------- |
|
|
75
|
-
| Move Forward/Back | W / S or ↑ / ↓
|
|
76
|
-
| Turn Left/Right | ← / →
|
|
77
|
-
| Strafe | A / D
|
|
78
|
-
| Fire | Ctrl
|
|
79
|
-
| Use/Open | Space
|
|
80
|
-
| Run | Shift
|
|
81
|
-
| Weapons | 1-7
|
|
82
|
-
| Menu | Escape
|
|
83
|
-
| Map | Tab
|
|
84
|
-
| Quit | Ctrl+C
|
|
88
|
+
| Action | Keys |
|
|
89
|
+
| ----------------- | ------------------ |
|
|
90
|
+
| Move Forward/Back | W / S or ↑ / ↓ |
|
|
91
|
+
| Turn Left/Right | Mouse or ← / → |
|
|
92
|
+
| Strafe | A / D |
|
|
93
|
+
| Fire | Left Click or Ctrl |
|
|
94
|
+
| Use/Open | Space |
|
|
95
|
+
| Run | Shift |
|
|
96
|
+
| Weapons | 1-7 |
|
|
97
|
+
| Menu | Escape |
|
|
98
|
+
| Map | Tab |
|
|
99
|
+
| Quit | Ctrl+C |
|
|
85
100
|
|
|
86
101
|
## 💾 Save Games
|
|
87
102
|
|
|
@@ -148,7 +163,8 @@ opentui-doom/
|
|
|
148
163
|
├── src/
|
|
149
164
|
│ ├── index.ts # Main entry point
|
|
150
165
|
│ ├── doom-engine.ts # WASM module wrapper
|
|
151
|
-
│
|
|
166
|
+
│ ├── doom-input.ts # Keyboard input mapping
|
|
167
|
+
│ └── doom-mouse.ts # Mouse input handling
|
|
152
168
|
├── doom/
|
|
153
169
|
│ ├── doomgeneric_opentui.c # Platform implementation
|
|
154
170
|
│ ├── doomgeneric/ # doomgeneric source (cloned during build)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muhammedaksam/opentui-doom",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Play DOOM in your terminal using OpenTUI's framebuffer rendering and doomgeneric WASM",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"typescript": "^5"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@opentui/core": "^0.1.
|
|
61
|
+
"@opentui/core": "^0.1.59"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=18",
|
package/src/doom-engine.ts
CHANGED
|
@@ -135,6 +135,23 @@ export class DoomEngine {
|
|
|
135
135
|
// Create .savegame directory for saves (DOOM looks here by default)
|
|
136
136
|
module.FS_createPath("/", ".savegame", true, true);
|
|
137
137
|
|
|
138
|
+
// Create default.cfg with WASD key bindings
|
|
139
|
+
// This is needed because we send character codes for WASD (to allow typing in save dialogs)
|
|
140
|
+
// Key codes: w=119, a=97, s=115, d=100
|
|
141
|
+
const defaultConfig = [
|
|
142
|
+
"key_up 119", // 'w' for forward
|
|
143
|
+
"key_down 115", // 's' for backward
|
|
144
|
+
"key_strafeleft 97", // 'a' for strafe left
|
|
145
|
+
"key_straferight 100", // 'd' for strafe right
|
|
146
|
+
].join("\n") + "\n";
|
|
147
|
+
const configArray = Array.from(new TextEncoder().encode(defaultConfig));
|
|
148
|
+
try {
|
|
149
|
+
module.FS_createDataFile("/", "default.cfg", configArray, true, false);
|
|
150
|
+
debugLog("Engine", "Created default.cfg with WASD key bindings");
|
|
151
|
+
} catch (e) {
|
|
152
|
+
debugLog("Engine", `Failed to create default.cfg: ${e}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
138
155
|
// Load existing saves from ~/.opentui-doom/ into virtual filesystem
|
|
139
156
|
const existingSaves = loadExistingSaves();
|
|
140
157
|
for (const [slot, data] of existingSaves) {
|
package/src/doom-input.ts
CHANGED
|
@@ -56,70 +56,74 @@ export const DoomKeys = {
|
|
|
56
56
|
const keyStates = new Map<string, boolean>();
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Map an OpenTUI key event to
|
|
59
|
+
* Map an OpenTUI key event to DOOM key code(s)
|
|
60
|
+
* Returns an array of key codes - for most keys this is a single code,
|
|
61
|
+
* but for WASD we return both the movement key AND the character
|
|
62
|
+
* so both gameplay movement and text input work.
|
|
60
63
|
*/
|
|
61
|
-
function mapKeyToDoom(key: KeyEvent): number
|
|
64
|
+
function mapKeyToDoom(key: KeyEvent): number[] {
|
|
62
65
|
const name = key.name?.toLowerCase() ?? "";
|
|
63
66
|
|
|
64
67
|
// Arrow keys
|
|
65
|
-
if (name === "up" || key.sequence === "\x1b[A") return DoomKeys.KEY_UPARROW;
|
|
66
|
-
if (name === "down" || key.sequence === "\x1b[B") return DoomKeys.KEY_DOWNARROW;
|
|
67
|
-
if (name === "left" || key.sequence === "\x1b[D") return DoomKeys.KEY_LEFTARROW;
|
|
68
|
-
if (name === "right" || key.sequence === "\x1b[C") return DoomKeys.KEY_RIGHTARROW;
|
|
69
|
-
|
|
70
|
-
// WASD movement
|
|
71
|
-
|
|
72
|
-
if (name === "
|
|
73
|
-
if (name === "
|
|
74
|
-
if (name === "
|
|
68
|
+
if (name === "up" || key.sequence === "\x1b[A") return [DoomKeys.KEY_UPARROW];
|
|
69
|
+
if (name === "down" || key.sequence === "\x1b[B") return [DoomKeys.KEY_DOWNARROW];
|
|
70
|
+
if (name === "left" || key.sequence === "\x1b[D") return [DoomKeys.KEY_LEFTARROW];
|
|
71
|
+
if (name === "right" || key.sequence === "\x1b[C") return [DoomKeys.KEY_RIGHTARROW];
|
|
72
|
+
|
|
73
|
+
// WASD movement - send BOTH movement key AND character
|
|
74
|
+
// Movement key ensures gameplay works, character ensures text input works
|
|
75
|
+
if (name === "w") return [DoomKeys.KEY_UPARROW, "w".charCodeAt(0)];
|
|
76
|
+
if (name === "s") return [DoomKeys.KEY_DOWNARROW, "s".charCodeAt(0)];
|
|
77
|
+
if (name === "a") return [DoomKeys.KEY_STRAFE_L, "a".charCodeAt(0)];
|
|
78
|
+
if (name === "d") return [DoomKeys.KEY_STRAFE_R, "d".charCodeAt(0)];
|
|
75
79
|
|
|
76
80
|
// Action keys
|
|
77
|
-
if (name === "space") return " ".charCodeAt(0); // Use
|
|
78
|
-
if (name === "return" || name === "enter") return DoomKeys.KEY_ENTER;
|
|
79
|
-
if (name === "escape") return DoomKeys.KEY_ESCAPE;
|
|
80
|
-
if (name === "tab") return DoomKeys.KEY_TAB;
|
|
81
|
-
if (name === "backspace") return DoomKeys.KEY_BACKSPACE;
|
|
81
|
+
if (name === "space") return [" ".charCodeAt(0)]; // Use
|
|
82
|
+
if (name === "return" || name === "enter") return [DoomKeys.KEY_ENTER];
|
|
83
|
+
if (name === "escape") return [DoomKeys.KEY_ESCAPE];
|
|
84
|
+
if (name === "tab") return [DoomKeys.KEY_TAB];
|
|
85
|
+
if (name === "backspace") return [DoomKeys.KEY_BACKSPACE];
|
|
82
86
|
|
|
83
87
|
// Fire (Ctrl) - but not Ctrl+C which should exit
|
|
84
|
-
if (key.ctrl && key.name !== "c") return DoomKeys.KEY_FIRE;
|
|
88
|
+
if (key.ctrl && key.name !== "c") return [DoomKeys.KEY_FIRE];
|
|
85
89
|
|
|
86
90
|
// Alt for strafe
|
|
87
|
-
if (key.meta || key.name === "alt") return DoomKeys.KEY_LALT;
|
|
91
|
+
if (key.meta || key.name === "alt") return [DoomKeys.KEY_LALT];
|
|
88
92
|
|
|
89
93
|
// Shift for run
|
|
90
|
-
if (key.shift) return DoomKeys.KEY_RSHIFT;
|
|
94
|
+
if (key.shift) return [DoomKeys.KEY_RSHIFT];
|
|
91
95
|
|
|
92
96
|
// Function keys
|
|
93
|
-
if (name === "f1") return DoomKeys.KEY_F1;
|
|
94
|
-
if (name === "f2") return DoomKeys.KEY_F2;
|
|
95
|
-
if (name === "f3") return DoomKeys.KEY_F3;
|
|
96
|
-
if (name === "f4") return DoomKeys.KEY_F4;
|
|
97
|
-
if (name === "f5") return DoomKeys.KEY_F5;
|
|
98
|
-
if (name === "f6") return DoomKeys.KEY_F6;
|
|
99
|
-
if (name === "f7") return DoomKeys.KEY_F7;
|
|
100
|
-
if (name === "f8") return DoomKeys.KEY_F8;
|
|
101
|
-
if (name === "f9") return DoomKeys.KEY_F9;
|
|
102
|
-
if (name === "f10") return DoomKeys.KEY_F10;
|
|
103
|
-
if (name === "f11") return DoomKeys.KEY_F11;
|
|
104
|
-
if (name === "f12") return DoomKeys.KEY_F12;
|
|
97
|
+
if (name === "f1") return [DoomKeys.KEY_F1];
|
|
98
|
+
if (name === "f2") return [DoomKeys.KEY_F2];
|
|
99
|
+
if (name === "f3") return [DoomKeys.KEY_F3];
|
|
100
|
+
if (name === "f4") return [DoomKeys.KEY_F4];
|
|
101
|
+
if (name === "f5") return [DoomKeys.KEY_F5];
|
|
102
|
+
if (name === "f6") return [DoomKeys.KEY_F6];
|
|
103
|
+
if (name === "f7") return [DoomKeys.KEY_F7];
|
|
104
|
+
if (name === "f8") return [DoomKeys.KEY_F8];
|
|
105
|
+
if (name === "f9") return [DoomKeys.KEY_F9];
|
|
106
|
+
if (name === "f10") return [DoomKeys.KEY_F10];
|
|
107
|
+
if (name === "f11") return [DoomKeys.KEY_F11];
|
|
108
|
+
if (name === "f12") return [DoomKeys.KEY_F12];
|
|
105
109
|
|
|
106
110
|
// Weapon selection (1-9, 0)
|
|
107
|
-
if (name >= "0" && name <= "9") return name.charCodeAt(0);
|
|
111
|
+
if (name >= "0" && name <= "9") return [name.charCodeAt(0)];
|
|
108
112
|
|
|
109
113
|
// Plus/minus for gamma/zoom
|
|
110
|
-
if (name === "+" || name === "=") return DoomKeys.KEY_EQUALS;
|
|
111
|
-
if (name === "-") return DoomKeys.KEY_MINUS;
|
|
114
|
+
if (name === "+" || name === "=") return [DoomKeys.KEY_EQUALS];
|
|
115
|
+
if (name === "-") return [DoomKeys.KEY_MINUS];
|
|
112
116
|
|
|
113
117
|
// Y/N for prompts
|
|
114
|
-
if (name === "y") return "y".charCodeAt(0);
|
|
115
|
-
if (name === "n") return "n".charCodeAt(0);
|
|
118
|
+
if (name === "y") return ["y".charCodeAt(0)];
|
|
119
|
+
if (name === "n") return ["n".charCodeAt(0)];
|
|
116
120
|
|
|
117
121
|
// Other letter keys (for cheats, etc)
|
|
118
122
|
if (name.length === 1 && name >= "a" && name <= "z") {
|
|
119
|
-
return name.charCodeAt(0);
|
|
123
|
+
return [name.charCodeAt(0)];
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
return
|
|
126
|
+
return [];
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
/**
|
|
@@ -145,9 +149,9 @@ export function createDoomInputHandler(options: DoomInputOptions) {
|
|
|
145
149
|
return;
|
|
146
150
|
}
|
|
147
151
|
|
|
148
|
-
const
|
|
152
|
+
const doomKeys = mapKeyToDoom(key);
|
|
149
153
|
|
|
150
|
-
if (
|
|
154
|
+
if (doomKeys.length === 0) return;
|
|
151
155
|
|
|
152
156
|
const keyId = key.name || key.sequence || "";
|
|
153
157
|
const wasPressed = keyStates.get(keyId) ?? false;
|
|
@@ -167,13 +171,18 @@ export function createDoomInputHandler(options: DoomInputOptions) {
|
|
|
167
171
|
// Key press - send if not already pressed, OR if it's a menu confirmation key
|
|
168
172
|
if (!wasPressed || isMenuConfirmKey) {
|
|
169
173
|
keyStates.set(keyId, true);
|
|
170
|
-
|
|
174
|
+
// Send all mapped keys (for WASD this includes both movement and character)
|
|
175
|
+
for (const doomKey of doomKeys) {
|
|
176
|
+
engine.pushKey(true, doomKey);
|
|
177
|
+
}
|
|
171
178
|
|
|
172
179
|
// For menu confirmation keys, immediately send release too
|
|
173
180
|
// since DOOM only cares about the keydown event
|
|
174
181
|
if (isMenuConfirmKey) {
|
|
175
182
|
setTimeout(() => {
|
|
176
|
-
|
|
183
|
+
for (const doomKey of doomKeys) {
|
|
184
|
+
engine.pushKey(false, doomKey);
|
|
185
|
+
}
|
|
177
186
|
keyStates.set(keyId, false);
|
|
178
187
|
}, 50);
|
|
179
188
|
return;
|
|
@@ -184,7 +193,9 @@ export function createDoomInputHandler(options: DoomInputOptions) {
|
|
|
184
193
|
const timer = setTimeout(() => {
|
|
185
194
|
if (keyStates.get(keyId)) {
|
|
186
195
|
keyStates.set(keyId, false);
|
|
187
|
-
|
|
196
|
+
for (const doomKey of doomKeys) {
|
|
197
|
+
engine.pushKey(false, doomKey);
|
|
198
|
+
}
|
|
188
199
|
keyTimers.delete(keyId);
|
|
189
200
|
}
|
|
190
201
|
}, 300);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOOM Mouse Input Handler
|
|
3
|
+
*
|
|
4
|
+
* Provides mouse-based turning and firing for DOOM.
|
|
5
|
+
* Mouse horizontal movement translates to left/right turning.
|
|
6
|
+
* Left click fires the weapon.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { DoomEngine } from "./doom-engine";
|
|
10
|
+
import { DoomKeys } from "./doom-input";
|
|
11
|
+
import { debugLog } from "./debug";
|
|
12
|
+
|
|
13
|
+
export interface DoomMouseOptions {
|
|
14
|
+
engine: DoomEngine;
|
|
15
|
+
sensitivity?: number; // Cells of movement before triggering turn (default: 2)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DoomMouseHandler {
|
|
19
|
+
onMouseMove: (x: number, y: number) => void;
|
|
20
|
+
onMouseDown: (button: number) => void;
|
|
21
|
+
onMouseUp: (button: number) => void;
|
|
22
|
+
reset: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a mouse handler that forwards mouse events to DOOM
|
|
27
|
+
*/
|
|
28
|
+
export function createDoomMouseHandler(options: DoomMouseOptions): DoomMouseHandler {
|
|
29
|
+
const { engine } = options;
|
|
30
|
+
|
|
31
|
+
let lastMouseX: number | null = null;
|
|
32
|
+
let isLeftMouseDown = false;
|
|
33
|
+
let currentTurnKey: number | null = null;
|
|
34
|
+
let releaseTimer: ReturnType<typeof setTimeout> | null = null;
|
|
35
|
+
|
|
36
|
+
const RELEASE_DELAY = 100; // Release key 100ms after last movement
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
/**
|
|
40
|
+
* Handle mouse movement - hold turn key while moving
|
|
41
|
+
*/
|
|
42
|
+
onMouseMove(x: number, _y: number): void {
|
|
43
|
+
if (lastMouseX === null) {
|
|
44
|
+
lastMouseX = x;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const delta = x - lastMouseX;
|
|
49
|
+
lastMouseX = x;
|
|
50
|
+
|
|
51
|
+
if (delta === 0) return;
|
|
52
|
+
|
|
53
|
+
const newTurnKey = delta > 0 ? DoomKeys.KEY_RIGHTARROW : DoomKeys.KEY_LEFTARROW;
|
|
54
|
+
|
|
55
|
+
// Clear any pending release
|
|
56
|
+
if (releaseTimer) {
|
|
57
|
+
clearTimeout(releaseTimer);
|
|
58
|
+
releaseTimer = null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If direction changed, release old and press new
|
|
62
|
+
if (currentTurnKey !== newTurnKey) {
|
|
63
|
+
if (currentTurnKey !== null) {
|
|
64
|
+
engine.pushKey(false, currentTurnKey);
|
|
65
|
+
}
|
|
66
|
+
engine.pushKey(true, newTurnKey);
|
|
67
|
+
currentTurnKey = newTurnKey;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Schedule release after delay (will be cancelled if more movement comes)
|
|
71
|
+
releaseTimer = setTimeout(() => {
|
|
72
|
+
if (currentTurnKey !== null) {
|
|
73
|
+
engine.pushKey(false, currentTurnKey);
|
|
74
|
+
currentTurnKey = null;
|
|
75
|
+
}
|
|
76
|
+
releaseTimer = null;
|
|
77
|
+
}, RELEASE_DELAY);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle mouse button press
|
|
82
|
+
*/
|
|
83
|
+
onMouseDown(button: number): void {
|
|
84
|
+
// Left click (button 0) = fire
|
|
85
|
+
if (button === 0 && !isLeftMouseDown) {
|
|
86
|
+
isLeftMouseDown = true;
|
|
87
|
+
engine.pushKey(true, DoomKeys.KEY_FIRE);
|
|
88
|
+
debugLog("Mouse", "Fire pressed");
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Handle mouse button release
|
|
94
|
+
*/
|
|
95
|
+
onMouseUp(button: number): void {
|
|
96
|
+
// Left click release = stop firing
|
|
97
|
+
if (button === 0 && isLeftMouseDown) {
|
|
98
|
+
isLeftMouseDown = false;
|
|
99
|
+
engine.pushKey(false, DoomKeys.KEY_FIRE);
|
|
100
|
+
debugLog("Mouse", "Fire released");
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reset mouse state (useful when window loses focus)
|
|
106
|
+
*/
|
|
107
|
+
reset(): void {
|
|
108
|
+
lastMouseX = null;
|
|
109
|
+
|
|
110
|
+
// Clear pending release timer
|
|
111
|
+
if (releaseTimer) {
|
|
112
|
+
clearTimeout(releaseTimer);
|
|
113
|
+
releaseTimer = null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Release any held turn key
|
|
117
|
+
if (currentTurnKey !== null) {
|
|
118
|
+
engine.pushKey(false, currentTurnKey);
|
|
119
|
+
currentTurnKey = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Release fire if held
|
|
123
|
+
if (isLeftMouseDown) {
|
|
124
|
+
isLeftMouseDown = false;
|
|
125
|
+
engine.pushKey(false, DoomKeys.KEY_FIRE);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "@opentui/core";
|
|
18
18
|
import { DoomEngine, DOOM_WIDTH, DOOM_HEIGHT } from "./doom-engine";
|
|
19
19
|
import { createDoomInputHandler, getControlsHelp } from "./doom-input";
|
|
20
|
+
import { createDoomMouseHandler, type DoomMouseHandler } from "./doom-mouse";
|
|
20
21
|
import { shutdownAudio } from "./doom-audio";
|
|
21
22
|
import { debugLog } from "./debug";
|
|
22
23
|
import { parseArgs } from "util";
|
|
@@ -35,6 +36,11 @@ const { values } = parseArgs({
|
|
|
35
36
|
short: "h",
|
|
36
37
|
default: false,
|
|
37
38
|
},
|
|
39
|
+
mouse: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
short: "m",
|
|
42
|
+
default: true,
|
|
43
|
+
},
|
|
38
44
|
},
|
|
39
45
|
});
|
|
40
46
|
|
|
@@ -48,7 +54,7 @@ Options:
|
|
|
48
54
|
-w, --wad Path to DOOM WAD file (default: doom1.wad)
|
|
49
55
|
-h, --help Show this help message
|
|
50
56
|
|
|
51
|
-
${getControlsHelp()}
|
|
57
|
+
${getControlsHelp()}${values.mouse ? '\n Mouse=Aim/Fire' : ''}
|
|
52
58
|
`);
|
|
53
59
|
process.exit(0);
|
|
54
60
|
}
|
|
@@ -130,6 +136,7 @@ let framebufferRenderable: FrameBufferRenderable | null = null;
|
|
|
130
136
|
let isExiting = false; // Flag to stop the game loop when exiting
|
|
131
137
|
let lastSaveSyncTime = 0; // Track when we last synced saves
|
|
132
138
|
const SAVE_SYNC_INTERVAL = 5000; // Sync saves every 5 seconds
|
|
139
|
+
let mouseHandler: DoomMouseHandler | null = null;
|
|
133
140
|
|
|
134
141
|
async function initDoom() {
|
|
135
142
|
try {
|
|
@@ -157,9 +164,12 @@ async function initDoom() {
|
|
|
157
164
|
renderer.root.add(framebufferRenderable);
|
|
158
165
|
|
|
159
166
|
// Add controls overlay
|
|
167
|
+
const controlsContent = values.mouse
|
|
168
|
+
? "DOOM | Ctrl+C to exit | WASD=Move Mouse=Aim Click=Fire"
|
|
169
|
+
: "DOOM | Ctrl+C to exit | Arrow/WASD=Move Space=Use Ctrl=Fire";
|
|
160
170
|
const controlsText = new TextRenderable(renderer, {
|
|
161
171
|
id: "controls",
|
|
162
|
-
content:
|
|
172
|
+
content: controlsContent,
|
|
163
173
|
position: "absolute",
|
|
164
174
|
left: 1,
|
|
165
175
|
top: 0,
|
|
@@ -176,6 +186,30 @@ async function initDoom() {
|
|
|
176
186
|
});
|
|
177
187
|
renderer.keyInput.on("keypress", inputHandler);
|
|
178
188
|
|
|
189
|
+
// Set up mouse handler if enabled
|
|
190
|
+
if (values.mouse) {
|
|
191
|
+
mouseHandler = createDoomMouseHandler({
|
|
192
|
+
engine: doomEngine,
|
|
193
|
+
sensitivity: 2, // Adjust for terminal cell size
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Attach mouse events to framebuffer
|
|
197
|
+
framebufferRenderable.onMouseMove = (event) => {
|
|
198
|
+
mouseHandler?.onMouseMove(event.x, event.y);
|
|
199
|
+
};
|
|
200
|
+
framebufferRenderable.onMouseDrag = (event) => {
|
|
201
|
+
mouseHandler?.onMouseMove(event.x, event.y);
|
|
202
|
+
};
|
|
203
|
+
framebufferRenderable.onMouseDown = (event) => {
|
|
204
|
+
mouseHandler?.onMouseDown(event.button);
|
|
205
|
+
};
|
|
206
|
+
framebufferRenderable.onMouseUp = (event) => {
|
|
207
|
+
mouseHandler?.onMouseUp(event.button);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
debugLog("Input", "Mouse look enabled");
|
|
211
|
+
}
|
|
212
|
+
|
|
179
213
|
// Start game loop
|
|
180
214
|
renderer.setFrameCallback(gameLoop);
|
|
181
215
|
|