@muhammedaksam/opentui-doom 0.3.5 → 0.3.6

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/src/doom-saves.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Save Game Manager for OpenTUI-DOOM
3
- *
3
+ *
4
4
  * Handles persistence of DOOM save games to ~/.opentui-doom/
5
5
  * DOOM uses 6 save slots (0-5) with files named doomsav{N}.dsg
6
6
  */
@@ -20,28 +20,28 @@ const SAVE_FILE_PATTERN = /^doomsav([0-5])\.dsg$/;
20
20
  * Ensure the save directory exists
21
21
  */
22
22
  export function ensureSaveDir(): void {
23
- if (!existsSync(SAVE_DIR)) {
24
- mkdirSync(SAVE_DIR, { recursive: true });
25
- debugLog("Saves", `Created save directory: ${SAVE_DIR}`);
26
- }
23
+ if (!existsSync(SAVE_DIR)) {
24
+ mkdirSync(SAVE_DIR, { recursive: true });
25
+ debugLog("Saves", `Created save directory: ${SAVE_DIR}`);
26
+ }
27
27
  }
28
28
 
29
29
  /**
30
30
  * Get the save game directory path
31
31
  */
32
32
  export function getSaveGameDir(): string {
33
- ensureSaveDir();
34
- return SAVE_DIR;
33
+ ensureSaveDir();
34
+ return SAVE_DIR;
35
35
  }
36
36
 
37
37
  /**
38
38
  * Get the path to a save file for a given slot (0-5)
39
39
  */
40
40
  export function getSaveFilePath(slot: number): string {
41
- if (slot < 0 || slot > 5) {
42
- throw new Error(`Invalid save slot: ${slot}. Must be 0-5.`);
43
- }
44
- return join(SAVE_DIR, `doomsav${slot}.dsg`);
41
+ if (slot < 0 || slot > 5) {
42
+ throw new Error(`Invalid save slot: ${slot}. Must be 0-5.`);
43
+ }
44
+ return join(SAVE_DIR, `doomsav${slot}.dsg`);
45
45
  }
46
46
 
47
47
  /**
@@ -49,73 +49,73 @@ export function getSaveFilePath(slot: number): string {
49
49
  * Returns a Map of slot number to file contents (Uint8Array)
50
50
  */
51
51
  export function loadExistingSaves(): Map<number, Uint8Array> {
52
- ensureSaveDir();
53
- const saves = new Map<number, Uint8Array>();
54
-
55
- try {
56
- const files = readdirSync(SAVE_DIR);
57
- for (const file of files) {
58
- const match = file.match(SAVE_FILE_PATTERN);
59
- if (match && match[1]) {
60
- const slot = parseInt(match[1], 10);
61
- const filePath = join(SAVE_DIR, file);
62
- try {
63
- const data = readFileSync(filePath);
64
- saves.set(slot, new Uint8Array(data));
65
- debugLog("Saves", `Loaded save slot ${slot}: ${data.length} bytes`);
66
- } catch (e) {
67
- debugLog("Saves", `Failed to read save file ${filePath}: ${e}`);
68
- }
69
- }
52
+ ensureSaveDir();
53
+ const saves = new Map<number, Uint8Array>();
54
+
55
+ try {
56
+ const files = readdirSync(SAVE_DIR);
57
+ for (const file of files) {
58
+ const match = file.match(SAVE_FILE_PATTERN);
59
+ if (match && match[1]) {
60
+ const slot = parseInt(match[1], 10);
61
+ const filePath = join(SAVE_DIR, file);
62
+ try {
63
+ const data = readFileSync(filePath);
64
+ saves.set(slot, new Uint8Array(data));
65
+ debugLog("Saves", `Loaded save slot ${slot}: ${data.length} bytes`);
66
+ } catch (e) {
67
+ debugLog("Saves", `Failed to read save file ${filePath}: ${e}`);
70
68
  }
71
- } catch (e) {
72
- debugLog("Saves", `Failed to list save directory: ${e}`);
69
+ }
73
70
  }
74
-
75
- debugLog("Saves", `Loaded ${saves.size} existing saves`);
76
- return saves;
71
+ } catch (e) {
72
+ debugLog("Saves", `Failed to list save directory: ${e}`);
73
+ }
74
+
75
+ debugLog("Saves", `Loaded ${saves.size} existing saves`);
76
+ return saves;
77
77
  }
78
78
 
79
79
  /**
80
80
  * Write a save game to disk
81
81
  */
82
82
  export function writeSave(slot: number, data: Uint8Array): boolean {
83
- ensureSaveDir();
84
- const filePath = getSaveFilePath(slot);
85
-
86
- try {
87
- writeFileSync(filePath, data);
88
- debugLog("Saves", `Wrote save slot ${slot}: ${data.length} bytes to ${filePath}`);
89
- return true;
90
- } catch (e) {
91
- debugLog("Saves", `Failed to write save slot ${slot}: ${e}`);
92
- return false;
93
- }
83
+ ensureSaveDir();
84
+ const filePath = getSaveFilePath(slot);
85
+
86
+ try {
87
+ writeFileSync(filePath, data);
88
+ debugLog("Saves", `Wrote save slot ${slot}: ${data.length} bytes to ${filePath}`);
89
+ return true;
90
+ } catch (e) {
91
+ debugLog("Saves", `Failed to write save slot ${slot}: ${e}`);
92
+ return false;
93
+ }
94
94
  }
95
95
 
96
96
  /**
97
97
  * Check if a save exists for a given slot
98
98
  */
99
99
  export function saveExists(slot: number): boolean {
100
- const filePath = getSaveFilePath(slot);
101
- return existsSync(filePath);
100
+ const filePath = getSaveFilePath(slot);
101
+ return existsSync(filePath);
102
102
  }
103
103
 
104
104
  /**
105
105
  * Read a save game from disk
106
106
  */
107
107
  export function readSave(slot: number): Uint8Array | null {
108
- const filePath = getSaveFilePath(slot);
109
-
110
- if (!existsSync(filePath)) {
111
- return null;
112
- }
113
-
114
- try {
115
- const data = readFileSync(filePath);
116
- return new Uint8Array(data);
117
- } catch (e) {
118
- debugLog("Saves", `Failed to read save slot ${slot}: ${e}`);
119
- return null;
120
- }
108
+ const filePath = getSaveFilePath(slot);
109
+
110
+ if (!existsSync(filePath)) {
111
+ return null;
112
+ }
113
+
114
+ try {
115
+ const data = readFileSync(filePath);
116
+ return new Uint8Array(data);
117
+ } catch (e) {
118
+ debugLog("Saves", `Failed to read save slot ${slot}: ${e}`);
119
+ return null;
120
+ }
121
121
  }
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
3
  * DOOM for OpenTUI
4
- *
4
+ *
5
5
  * Plays DOOM in your terminal using OpenTUI's framebuffer rendering.
6
- *
6
+ *
7
7
  * Usage: bun run dev -- --wad /path/to/doom1.wad
8
8
  */
9
9
 
@@ -54,7 +54,7 @@ Options:
54
54
  -w, --wad Path to DOOM WAD file (default: doom1.wad)
55
55
  -h, --help Show this help message
56
56
 
57
- ${getControlsHelp()}${values.mouse ? '\n Mouse=Aim/Fire' : ''}
57
+ ${getControlsHelp()}${values.mouse ? "\n Mouse=Aim/Fire" : ""}
58
58
  `);
59
59
  process.exit(0);
60
60
  }
@@ -67,42 +67,42 @@ const renderer = await createCliRenderer({
67
67
 
68
68
  // Handle graceful shutdown
69
69
  const cleanup = (signal?: string) => {
70
- debugLog('Exit', `cleanup called with signal: ${signal}`);
71
-
70
+ debugLog("Exit", `cleanup called with signal: ${signal}`);
71
+
72
72
  // Set flag to stop the game loop FIRST - this is critical
73
73
  isExiting = true;
74
- debugLog('Exit', 'isExiting set to true');
75
-
74
+ debugLog("Exit", "isExiting set to true");
75
+
76
76
  // Sync saves before exiting
77
77
  if (doomEngine) {
78
78
  try {
79
79
  doomEngine.syncSaves();
80
- debugLog('Exit', 'saves synced to disk');
80
+ debugLog("Exit", "saves synced to disk");
81
81
  } catch (e) {
82
- debugLog('Exit', `failed to sync saves: ${e}`);
82
+ debugLog("Exit", `failed to sync saves: ${e}`);
83
83
  }
84
84
  }
85
-
85
+
86
86
  // Clear the frame callback to stop DOOM from ticking
87
87
  try {
88
88
  renderer.setFrameCallback(null as any);
89
- debugLog('Exit', 'frame callback cleared');
89
+ debugLog("Exit", "frame callback cleared");
90
90
  } catch (e) {
91
- debugLog('Exit', `failed to clear frame callback: ${e}`);
91
+ debugLog("Exit", `failed to clear frame callback: ${e}`);
92
92
  }
93
-
93
+
94
94
  shutdownAudio();
95
- debugLog('Exit', 'shutdownAudio completed');
96
-
95
+ debugLog("Exit", "shutdownAudio completed");
96
+
97
97
  try {
98
98
  renderer.stop();
99
- debugLog('Exit', 'renderer.stop completed');
99
+ debugLog("Exit", "renderer.stop completed");
100
100
  } catch (e) {
101
- debugLog('Exit', `renderer.stop error: ${e}`);
101
+ debugLog("Exit", `renderer.stop error: ${e}`);
102
102
  }
103
-
103
+
104
104
  // Exit the process
105
- debugLog('Exit', 'calling process.exit(0)');
105
+ debugLog("Exit", "calling process.exit(0)");
106
106
  process.exit(0);
107
107
  };
108
108
 
@@ -164,7 +164,7 @@ async function initDoom() {
164
164
  renderer.root.add(framebufferRenderable);
165
165
 
166
166
  // Add controls overlay
167
- const controlsContent = values.mouse
167
+ const controlsContent = values.mouse
168
168
  ? "DOOM | Ctrl+C to exit | WASD=Move Mouse=Aim Click=Fire"
169
169
  : "DOOM | Ctrl+C to exit | Arrow/WASD=Move Space=Use Ctrl=Fire";
170
170
  const controlsText = new TextRenderable(renderer, {
@@ -190,9 +190,9 @@ async function initDoom() {
190
190
  if (values.mouse) {
191
191
  mouseHandler = createDoomMouseHandler({
192
192
  engine: doomEngine,
193
- sensitivity: 2, // Adjust for terminal cell size
193
+ sensitivity: 2, // Adjust for terminal cell size
194
194
  });
195
-
195
+
196
196
  // Attach mouse events to framebuffer
197
197
  framebufferRenderable.onMouseMove = (event) => {
198
198
  mouseHandler?.onMouseMove(event.x, event.y);
@@ -206,13 +206,12 @@ async function initDoom() {
206
206
  framebufferRenderable.onMouseUp = (event) => {
207
207
  mouseHandler?.onMouseUp(event.button);
208
208
  };
209
-
209
+
210
210
  debugLog("Input", "Mouse look enabled");
211
211
  }
212
212
 
213
213
  // Start game loop
214
214
  renderer.setFrameCallback(gameLoop);
215
-
216
215
  } catch (error) {
217
216
  loadingText.content = `Error: ${error}`;
218
217
  loadingText.fg = RGBA.fromInts(255, 100, 100);
@@ -242,15 +241,15 @@ async function initDoom() {
242
241
  }
243
242
  }
244
243
 
245
- async function gameLoop(deltaMs: number) {
244
+ async function gameLoop(_deltaMs: number) {
246
245
  // Bail out immediately if we're exiting
247
246
  if (isExiting) return;
248
-
247
+
249
248
  if (!doomEngine || !framebufferRenderable) return;
250
249
 
251
250
  // Run DOOM tick
252
251
  doomEngine.tick();
253
-
252
+
254
253
  // Periodic save sync (every 5 seconds)
255
254
  const now = Date.now();
256
255
  if (now - lastSaveSyncTime > SAVE_SYNC_INTERVAL) {
@@ -270,7 +269,7 @@ async function gameLoop(deltaMs: number) {
270
269
  // Render to OpenTUI framebuffer using half-block characters
271
270
  // The upper half-block character (▀) uses foreground for top pixel, background for bottom
272
271
  for (let y = 0; y < fb.height; y++) {
273
- const srcY1 = Math.floor(y * 2 * scaleY); // Top pixel row
272
+ const srcY1 = Math.floor(y * 2 * scaleY); // Top pixel row
274
273
  const srcY2 = Math.floor((y * 2 + 1) * scaleY); // Bottom pixel row
275
274
 
276
275
  for (let x = 0; x < fb.width; x++) {