@ticktockbent/rogue-ts 0.1.0

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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/armor.d.ts +18 -0
  4. package/dist/armor.d.ts.map +1 -0
  5. package/dist/armor.js +84 -0
  6. package/dist/armor.js.map +1 -0
  7. package/dist/chase.d.ts +15 -0
  8. package/dist/chase.d.ts.map +1 -0
  9. package/dist/chase.js +152 -0
  10. package/dist/chase.js.map +1 -0
  11. package/dist/command.d.ts +12 -0
  12. package/dist/command.d.ts.map +1 -0
  13. package/dist/command.js +761 -0
  14. package/dist/command.js.map +1 -0
  15. package/dist/curses.d.ts +102 -0
  16. package/dist/curses.d.ts.map +1 -0
  17. package/dist/curses.js +47 -0
  18. package/dist/curses.js.map +1 -0
  19. package/dist/daemon.d.ts +43 -0
  20. package/dist/daemon.d.ts.map +1 -0
  21. package/dist/daemon.js +116 -0
  22. package/dist/daemon.js.map +1 -0
  23. package/dist/daemons.d.ts +47 -0
  24. package/dist/daemons.d.ts.map +1 -0
  25. package/dist/daemons.js +194 -0
  26. package/dist/daemons.js.map +1 -0
  27. package/dist/fight.d.ts +52 -0
  28. package/dist/fight.d.ts.map +1 -0
  29. package/dist/fight.js +508 -0
  30. package/dist/fight.js.map +1 -0
  31. package/dist/globals.d.ts +387 -0
  32. package/dist/globals.d.ts.map +1 -0
  33. package/dist/globals.js +935 -0
  34. package/dist/globals.js.map +1 -0
  35. package/dist/index.d.ts +11 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +9 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/init.d.ts +30 -0
  40. package/dist/init.d.ts.map +1 -0
  41. package/dist/init.js +159 -0
  42. package/dist/init.js.map +1 -0
  43. package/dist/io.d.ts +48 -0
  44. package/dist/io.d.ts.map +1 -0
  45. package/dist/io.js +248 -0
  46. package/dist/io.js.map +1 -0
  47. package/dist/list.d.ts +37 -0
  48. package/dist/list.d.ts.map +1 -0
  49. package/dist/list.js +109 -0
  50. package/dist/list.js.map +1 -0
  51. package/dist/main.d.ts +23 -0
  52. package/dist/main.d.ts.map +1 -0
  53. package/dist/main.js +207 -0
  54. package/dist/main.js.map +1 -0
  55. package/dist/misc.d.ts +53 -0
  56. package/dist/misc.d.ts.map +1 -0
  57. package/dist/misc.js +296 -0
  58. package/dist/misc.js.map +1 -0
  59. package/dist/monsters.d.ts +55 -0
  60. package/dist/monsters.d.ts.map +1 -0
  61. package/dist/monsters.js +211 -0
  62. package/dist/monsters.js.map +1 -0
  63. package/dist/move.d.ts +23 -0
  64. package/dist/move.d.ts.map +1 -0
  65. package/dist/move.js +270 -0
  66. package/dist/move.js.map +1 -0
  67. package/dist/new_level.d.ts +17 -0
  68. package/dist/new_level.d.ts.map +1 -0
  69. package/dist/new_level.js +173 -0
  70. package/dist/new_level.js.map +1 -0
  71. package/dist/pack.d.ts +47 -0
  72. package/dist/pack.d.ts.map +1 -0
  73. package/dist/pack.js +359 -0
  74. package/dist/pack.js.map +1 -0
  75. package/dist/passages.d.ts +23 -0
  76. package/dist/passages.d.ts.map +1 -0
  77. package/dist/passages.js +277 -0
  78. package/dist/passages.js.map +1 -0
  79. package/dist/potions.d.ts +13 -0
  80. package/dist/potions.d.ts.map +1 -0
  81. package/dist/potions.js +239 -0
  82. package/dist/potions.js.map +1 -0
  83. package/dist/rings.d.ts +13 -0
  84. package/dist/rings.d.ts.map +1 -0
  85. package/dist/rings.js +173 -0
  86. package/dist/rings.js.map +1 -0
  87. package/dist/rip.d.ts +22 -0
  88. package/dist/rip.d.ts.map +1 -0
  89. package/dist/rip.js +216 -0
  90. package/dist/rip.js.map +1 -0
  91. package/dist/rooms.d.ts +62 -0
  92. package/dist/rooms.d.ts.map +1 -0
  93. package/dist/rooms.js +432 -0
  94. package/dist/rooms.js.map +1 -0
  95. package/dist/save.d.ts +25 -0
  96. package/dist/save.d.ts.map +1 -0
  97. package/dist/save.js +565 -0
  98. package/dist/save.js.map +1 -0
  99. package/dist/scrolls.d.ts +13 -0
  100. package/dist/scrolls.d.ts.map +1 -0
  101. package/dist/scrolls.js +305 -0
  102. package/dist/scrolls.js.map +1 -0
  103. package/dist/sticks.d.ts +9 -0
  104. package/dist/sticks.d.ts.map +1 -0
  105. package/dist/sticks.js +379 -0
  106. package/dist/sticks.js.map +1 -0
  107. package/dist/things.d.ts +28 -0
  108. package/dist/things.d.ts.map +1 -0
  109. package/dist/things.js +307 -0
  110. package/dist/things.js.map +1 -0
  111. package/dist/types.d.ts +113 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +10 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/util.d.ts +36 -0
  116. package/dist/util.d.ts.map +1 -0
  117. package/dist/util.js +198 -0
  118. package/dist/util.js.map +1 -0
  119. package/dist/weapons.d.ts +22 -0
  120. package/dist/weapons.d.ts.map +1 -0
  121. package/dist/weapons.js +185 -0
  122. package/dist/weapons.js.map +1 -0
  123. package/package.json +31 -0
@@ -0,0 +1,761 @@
1
+ /**
2
+ * Command dispatcher.
3
+ * Ported from command.c
4
+ *
5
+ * Handles all key input during the main game loop.
6
+ */
7
+ import { state, ESCAPE, NUMLINES, NUMCOLS, STAIRS, ISHUH, ISHALU, ISLEVIT, ISWEARING, R_SEARCH, R_TELEPORT, FLOOR, PASSAGE, DOOR, TRAP, POTION, SCROLL, RING, STICK, CALLABLE, INDEX, chat, flat, F_REAL, F_PASS, F_TMASK, pot_info, scr_info, ring_info, ws_info, MAXPOTIONS, MAXSCROLLS, MAXRINGS, MAXSTICKS, helpstr, tr_name, monsters as monsterTemplates, } from "./globals.js";
8
+ import { CTRL, rnd } from "./util.js";
9
+ import { msg, readchar, wait_for, status, getBackend } from "./io.js";
10
+ import { do_move, do_run } from "./move.js";
11
+ import { pick_up, drop, eat, get_item } from "./pack.js";
12
+ import { inventory } from "./pack.js";
13
+ import { look } from "./misc.js";
14
+ import { quit } from "./rip.js";
15
+ import { wield, missile } from "./weapons.js";
16
+ import { wear, take_off } from "./armor.js";
17
+ import { quaff } from "./potions.js";
18
+ import { read_scroll } from "./scrolls.js";
19
+ import { ring_on, ring_off } from "./rings.js";
20
+ import { do_zap } from "./sticks.js";
21
+ import { inv_name } from "./things.js";
22
+ // Direction deltas for movement keys
23
+ const dirMap = {
24
+ h: [0, -1],
25
+ j: [1, 0],
26
+ k: [-1, 0],
27
+ l: [0, 1],
28
+ y: [-1, -1],
29
+ u: [-1, 1],
30
+ b: [1, -1],
31
+ n: [1, 1],
32
+ };
33
+ // Uppercase = run in that direction
34
+ const runMap = {
35
+ H: "h", J: "j", K: "k", L: "l",
36
+ Y: "y", U: "u", B: "b", N: "n",
37
+ };
38
+ // Ctrl+dir = run until adjacent to something
39
+ const ctrlDirMap = {
40
+ [CTRL("H")]: "h", [CTRL("J")]: "j", [CTRL("K")]: "k", [CTRL("L")]: "l",
41
+ [CTRL("Y")]: "y", [CTRL("U")]: "u", [CTRL("B")]: "b", [CTRL("N")]: "n",
42
+ };
43
+ /**
44
+ * command: Read and execute a command.
45
+ * Returns true if the turn should end (monsters move).
46
+ */
47
+ export async function command() {
48
+ const backend = getBackend();
49
+ // Handle no_command (paralysis, etc.)
50
+ if (state.no_command > 0) {
51
+ state.no_command--;
52
+ if (state.no_command === 0) {
53
+ await msg("you can move again");
54
+ }
55
+ return true;
56
+ }
57
+ // If counting moves, continue the last direction
58
+ if (state.count > 0) {
59
+ state.count--;
60
+ return await executeDirection(state.last_dir);
61
+ }
62
+ state.after = true;
63
+ // If running, continue the run
64
+ if (state.running) {
65
+ return await executeDirection(state.runch);
66
+ }
67
+ // Draw surrounding area
68
+ await look(true);
69
+ await status();
70
+ backend.move(state.player.t_pos.y, state.player.t_pos.x);
71
+ backend.refresh();
72
+ // Pick up items if auto-pickup is pending
73
+ if (state.take !== "") {
74
+ const takeCh = state.take;
75
+ state.take = "";
76
+ if (!state.move_no_pickup) {
77
+ await pick_up(takeCh);
78
+ }
79
+ }
80
+ // Read the command
81
+ let ch = await readchar();
82
+ // C original: numeric prefix — read digits to build a count
83
+ if (ch >= "0" && ch <= "9") {
84
+ state.count = 0;
85
+ while (ch >= "0" && ch <= "9") {
86
+ state.count = state.count * 10 + (ch.charCodeAt(0) - "0".charCodeAt(0));
87
+ ch = await readchar();
88
+ }
89
+ state.count--;
90
+ }
91
+ // Check for movement
92
+ if (dirMap[ch]) {
93
+ const [dy, dx] = dirMap[ch];
94
+ state.last_dir = ch;
95
+ await do_move(dy, dx);
96
+ return state.after;
97
+ }
98
+ // Check for running
99
+ if (runMap[ch]) {
100
+ const dir = runMap[ch];
101
+ do_run(dir);
102
+ state.last_dir = dir;
103
+ const [dy, dx] = dirMap[dir];
104
+ await do_move(dy, dx);
105
+ return state.after;
106
+ }
107
+ // Check for ctrl+dir (run until adjacent)
108
+ const ctrlDir = ctrlDirMap[ch.charCodeAt(0)];
109
+ if (ctrlDir !== undefined) {
110
+ do_run(ctrlDir);
111
+ state.last_dir = ctrlDir;
112
+ const [dy, dx] = dirMap[ctrlDir];
113
+ await do_move(dy, dx);
114
+ return state.after;
115
+ }
116
+ // Other commands
117
+ switch (ch) {
118
+ case ".":
119
+ // Rest — do nothing, let monsters move
120
+ break;
121
+ case "s":
122
+ // Search for hidden doors/traps
123
+ await search();
124
+ break;
125
+ case ">":
126
+ // Go down stairs
127
+ await goDownStairs();
128
+ break;
129
+ case "<":
130
+ // Go up stairs (need amulet)
131
+ await goUpStairs();
132
+ break;
133
+ case "i":
134
+ // Inventory
135
+ state.after = false;
136
+ await inventory(state.player.t_pack, 0);
137
+ break;
138
+ case ",":
139
+ case "g":
140
+ // Pick up item
141
+ {
142
+ const pickCh = chat(state.player.t_pos.y, state.player.t_pos.x);
143
+ if (pickCh === FLOOR || pickCh === PASSAGE || pickCh === DOOR || pickCh === " ") {
144
+ await msg("nothing to pick up");
145
+ }
146
+ else {
147
+ await pick_up(pickCh);
148
+ }
149
+ }
150
+ break;
151
+ case "d":
152
+ // Drop item
153
+ await drop();
154
+ break;
155
+ case "e":
156
+ // Eat food
157
+ await eat();
158
+ break;
159
+ case "w":
160
+ // Wield weapon
161
+ await wield();
162
+ break;
163
+ case "W":
164
+ // Wear armor
165
+ await wear();
166
+ break;
167
+ case "T":
168
+ // Take off armor
169
+ await take_off();
170
+ break;
171
+ case "q":
172
+ // Quaff potion
173
+ await quaff();
174
+ break;
175
+ case "r":
176
+ // Read scroll
177
+ await read_scroll();
178
+ break;
179
+ case "P":
180
+ // Put on ring
181
+ await ring_on();
182
+ break;
183
+ case "R":
184
+ // Remove ring
185
+ await ring_off();
186
+ break;
187
+ case "z":
188
+ // Zap wand/staff
189
+ await do_zap();
190
+ break;
191
+ case "t":
192
+ // Throw something
193
+ await missile();
194
+ break;
195
+ case "f":
196
+ // Fight to death or near death
197
+ {
198
+ state.to_death = true;
199
+ const fightDir = await readchar();
200
+ if (dirMap[fightDir]) {
201
+ state.last_dir = fightDir;
202
+ const [dy, dx] = dirMap[fightDir];
203
+ await do_move(dy, dx);
204
+ }
205
+ }
206
+ break;
207
+ case "F":
208
+ // Fight to the death (kamikaze)
209
+ {
210
+ state.to_death = true;
211
+ state.kamikaze = true;
212
+ const fightDir = await readchar();
213
+ if (dirMap[fightDir]) {
214
+ state.last_dir = fightDir;
215
+ const [dy, dx] = dirMap[fightDir];
216
+ await do_move(dy, dx);
217
+ }
218
+ }
219
+ break;
220
+ case "m":
221
+ // Move without picking up
222
+ {
223
+ state.move_no_pickup = true;
224
+ const moveDir = await readchar();
225
+ if (dirMap[moveDir]) {
226
+ state.last_dir = moveDir;
227
+ const [dy, dx] = dirMap[moveDir];
228
+ await do_move(dy, dx);
229
+ }
230
+ state.move_no_pickup = false;
231
+ }
232
+ break;
233
+ case "c":
234
+ // Call/name an item
235
+ state.after = false;
236
+ await call_it();
237
+ break;
238
+ case "a":
239
+ // Repeat last command
240
+ state.again = true;
241
+ break;
242
+ case ")":
243
+ // Print current weapon
244
+ state.after = false;
245
+ if (state.cur_weapon !== null && state.cur_weapon._kind === "object") {
246
+ await msg("you are wielding %s (%s)", inv_name(state.cur_weapon, false), state.cur_weapon.o_packch);
247
+ }
248
+ else {
249
+ await msg("you are empty handed");
250
+ }
251
+ break;
252
+ case "]":
253
+ // Print current armor
254
+ state.after = false;
255
+ if (state.cur_armor !== null && state.cur_armor._kind === "object") {
256
+ await msg("you are wearing %s", inv_name(state.cur_armor, false));
257
+ }
258
+ else {
259
+ await msg("you are not wearing any armor");
260
+ }
261
+ break;
262
+ case "=":
263
+ // Print current rings
264
+ state.after = false;
265
+ {
266
+ let foundRing = false;
267
+ for (let hand = 0; hand < 2; hand++) {
268
+ const ring = state.cur_ring[hand];
269
+ if (ring !== null && ring._kind === "object") {
270
+ await msg("on %s hand: %s", hand === 0 ? "left" : "right", inv_name(ring, false));
271
+ foundRing = true;
272
+ }
273
+ }
274
+ if (!foundRing) {
275
+ await msg("not wearing any rings");
276
+ }
277
+ }
278
+ break;
279
+ case "@":
280
+ // Toggle stat_msg / print current stats
281
+ state.after = false;
282
+ state.stat_msg = !state.stat_msg;
283
+ await msg("terse stats %s", state.stat_msg ? "on" : "off");
284
+ break;
285
+ case "D":
286
+ // Recall discoveries
287
+ state.after = false;
288
+ await discovered();
289
+ break;
290
+ case "/":
291
+ // Identify character on screen
292
+ state.after = false;
293
+ await identify_char();
294
+ break;
295
+ case "^":
296
+ // Identify trap type
297
+ state.after = false;
298
+ await identify_trap();
299
+ break;
300
+ case "I":
301
+ // Inventory single item
302
+ state.after = false;
303
+ {
304
+ await msg("which item? ");
305
+ const itemCh = await readchar();
306
+ if (itemCh.charCodeAt(0) === ESCAPE) {
307
+ await msg("");
308
+ break;
309
+ }
310
+ let found = false;
311
+ let item = state.player.t_pack;
312
+ while (item !== null) {
313
+ if (item._kind === "object" && item.o_packch === itemCh) {
314
+ await msg("%s (%s)", inv_name(item, false), item.o_packch);
315
+ found = true;
316
+ break;
317
+ }
318
+ item = item.l_next;
319
+ }
320
+ if (!found) {
321
+ await msg("'%s' not in pack", itemCh);
322
+ }
323
+ }
324
+ break;
325
+ case "o":
326
+ // Options
327
+ state.after = false;
328
+ await set_options();
329
+ break;
330
+ case "S":
331
+ // Save game
332
+ state.after = false;
333
+ {
334
+ const { saveGame } = await import("./save.js");
335
+ const saveData = saveGame();
336
+ state._saveData = saveData;
337
+ await msg("game saved");
338
+ // Signal the game loop to exit cleanly
339
+ state.playing = false;
340
+ const { RogueExit } = await import("./types.js");
341
+ throw new RogueExit(0);
342
+ }
343
+ case "Q":
344
+ // Quit
345
+ state.after = false;
346
+ await msg("really quit?");
347
+ {
348
+ const answer = await readchar();
349
+ if (answer === "y" || answer === "Y") {
350
+ await quit();
351
+ }
352
+ else {
353
+ await msg("");
354
+ }
355
+ }
356
+ break;
357
+ case String.fromCharCode(CTRL("R")):
358
+ // Redraw screen
359
+ state.after = false;
360
+ if (state.stdscr !== null) {
361
+ backend.clearok(state.stdscr, true);
362
+ }
363
+ backend.refresh();
364
+ break;
365
+ case String.fromCharCode(CTRL("P")):
366
+ // Repeat last message
367
+ state.after = false;
368
+ if (state.huh !== "") {
369
+ await msg(state.huh);
370
+ }
371
+ break;
372
+ case "?":
373
+ // Help
374
+ state.after = false;
375
+ await help();
376
+ break;
377
+ case "v":
378
+ // Version
379
+ state.after = false;
380
+ await msg("rogue-ts version: 5.4.4-ts (Phase 8)");
381
+ break;
382
+ default:
383
+ // Unknown command
384
+ state.after = false;
385
+ if (state.terse) {
386
+ await msg("illegal cmd '%s'", ch);
387
+ }
388
+ else {
389
+ await msg("illegal command '%s'", ch);
390
+ }
391
+ state.count = 0;
392
+ break;
393
+ }
394
+ // C original: ring of searching auto-searches each turn
395
+ if (state.after && ISWEARING(R_SEARCH)) {
396
+ await search();
397
+ }
398
+ // C original: ring of teleportation randomly teleports
399
+ if (state.after && ISWEARING(R_TELEPORT) && rnd(50) === 0) {
400
+ const { find_floor, roomin } = await import("./rooms.js");
401
+ const { floor_at } = await import("./misc.js");
402
+ const heroPos = state.player.t_pos;
403
+ const newPos = { y: 0, x: 0 };
404
+ find_floor(null, newPos, false, true);
405
+ const backend = getBackend();
406
+ backend.mvaddch(heroPos.y, heroPos.x, floor_at().charCodeAt(0));
407
+ heroPos.y = newPos.y;
408
+ heroPos.x = newPos.x;
409
+ state.player.t_room = roomin(heroPos);
410
+ await msg("teleport!");
411
+ }
412
+ return state.after;
413
+ }
414
+ /**
415
+ * executeDirection: Execute movement in a saved direction.
416
+ */
417
+ async function executeDirection(dir) {
418
+ if (!dirMap[dir])
419
+ return false;
420
+ const [dy, dx] = dirMap[dir];
421
+ await do_move(dy, dx);
422
+ return state.after;
423
+ }
424
+ /**
425
+ * search: Look for hidden things around the hero.
426
+ */
427
+ async function search() {
428
+ const backend = getBackend();
429
+ const heroPos = state.player.t_pos;
430
+ // C original: confused search has probability of failure per cell, not total failure
431
+ for (let dy = -1; dy <= 1; dy++) {
432
+ for (let dx = -1; dx <= 1; dx++) {
433
+ if (dy === 0 && dx === 0)
434
+ continue;
435
+ const ny = heroPos.y + dy;
436
+ const nx = heroPos.x + dx;
437
+ if (ny <= 0 || ny >= NUMLINES - 1 || nx < 0 || nx >= NUMCOLS)
438
+ continue;
439
+ // Confused: each cell has a chance to fail
440
+ if ((state.player.t_flags & ISHUH) && rnd(5) !== 0)
441
+ continue;
442
+ const pp = INDEX(ny, nx);
443
+ if (!(pp.p_flags & F_REAL)) {
444
+ if (rnd(5) === 0) {
445
+ pp.p_flags |= F_REAL;
446
+ pp.p_ch = (pp.p_flags & F_PASS) ? PASSAGE : DOOR;
447
+ state.count = 0;
448
+ state.running = false;
449
+ if (!(state.player.t_flags & ISHALU)) {
450
+ backend.mvaddch(ny, nx, pp.p_ch.charCodeAt(0));
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
457
+ /**
458
+ * goDownStairs: Descend to the next level.
459
+ */
460
+ async function goDownStairs() {
461
+ if (chat(state.player.t_pos.y, state.player.t_pos.x) !== STAIRS) {
462
+ if (state.terse) {
463
+ await msg("no stairs");
464
+ }
465
+ else {
466
+ await msg("I see no way down");
467
+ }
468
+ return;
469
+ }
470
+ // C original: levit_check — levitating players can't use stairs
471
+ if (levit_check())
472
+ return;
473
+ state.level++;
474
+ state.seenstairs = false;
475
+ state._newLevel = true;
476
+ // C original: stairs don't give monsters a free turn
477
+ state.after = false;
478
+ }
479
+ /**
480
+ * goUpStairs: Ascend (only with the amulet, only at level 1).
481
+ */
482
+ async function goUpStairs() {
483
+ if (chat(state.player.t_pos.y, state.player.t_pos.x) !== STAIRS) {
484
+ if (state.terse) {
485
+ await msg("no stairs");
486
+ }
487
+ else {
488
+ await msg("I see no way up");
489
+ }
490
+ return;
491
+ }
492
+ if (!state.amulet) {
493
+ await msg("your way is magically blocked");
494
+ return;
495
+ }
496
+ // C original: levit_check — levitating players can't use stairs
497
+ if (levit_check())
498
+ return;
499
+ state.level--;
500
+ if (state.level <= 0) {
501
+ const { total_winner } = await import("./rip.js");
502
+ await total_winner();
503
+ return;
504
+ }
505
+ state._newLevel = true;
506
+ state.after = false;
507
+ }
508
+ /**
509
+ * call_it: Allow the player to name/call an item type.
510
+ */
511
+ async function call_it() {
512
+ const obj = await get_item("call", CALLABLE);
513
+ if (obj === null)
514
+ return;
515
+ const typeChar = String.fromCharCode(obj.o_type);
516
+ let infoTable = null;
517
+ let idx = obj.o_which;
518
+ switch (typeChar) {
519
+ case POTION:
520
+ infoTable = pot_info;
521
+ break;
522
+ case SCROLL:
523
+ infoTable = scr_info;
524
+ break;
525
+ case RING:
526
+ infoTable = ring_info;
527
+ break;
528
+ case STICK:
529
+ infoTable = ws_info;
530
+ break;
531
+ default:
532
+ // Can also name individual objects
533
+ await msg("what do you want to call it?");
534
+ {
535
+ const name = await readName();
536
+ if (name !== "") {
537
+ obj.o_label = name;
538
+ }
539
+ }
540
+ return;
541
+ }
542
+ await msg("what do you want to call it?");
543
+ const name = await readName();
544
+ if (name !== "" && infoTable !== null) {
545
+ infoTable[idx].oi_guess = name;
546
+ }
547
+ }
548
+ /**
549
+ * readName: Read a string from the player for naming items.
550
+ */
551
+ async function readName() {
552
+ let name = "";
553
+ for (;;) {
554
+ const ch = await readchar();
555
+ if (ch === "\n" || ch === "\r")
556
+ break;
557
+ if (ch.charCodeAt(0) === ESCAPE)
558
+ return "";
559
+ if (ch === "\b" || ch.charCodeAt(0) === 127) {
560
+ if (name.length > 0)
561
+ name = name.slice(0, -1);
562
+ continue;
563
+ }
564
+ if (name.length < 30) {
565
+ name += ch;
566
+ }
567
+ }
568
+ return name;
569
+ }
570
+ /**
571
+ * help: Display the help screen using the helpstr table.
572
+ */
573
+ async function help() {
574
+ const backend = getBackend();
575
+ backend.clear();
576
+ let row = 0;
577
+ for (const entry of helpstr) {
578
+ if (!entry.h_print)
579
+ continue;
580
+ let line;
581
+ if (entry.h_ch === 0) {
582
+ // Category header
583
+ line = entry.h_desc.replace(/^\t/, " ");
584
+ }
585
+ else {
586
+ const keyStr = entry.h_ch < 32
587
+ ? "^" + String.fromCharCode(entry.h_ch + 64)
588
+ : String.fromCharCode(entry.h_ch);
589
+ // h_desc is like "<dir>\tdescription" or "\tdescription"
590
+ const parts = entry.h_desc.split("\t");
591
+ const prefix = parts[0] || "";
592
+ const desc = parts[1] || "";
593
+ line = keyStr + prefix + " " + desc;
594
+ }
595
+ backend.mvaddstr(row, 0, line);
596
+ row++;
597
+ if (row >= NUMLINES - 1) {
598
+ backend.mvaddstr(NUMLINES - 1, 0, "--Press space to continue--");
599
+ backend.refresh();
600
+ await wait_for(" ");
601
+ backend.clear();
602
+ row = 0;
603
+ }
604
+ }
605
+ backend.mvaddstr(NUMLINES - 1, 0, "--Press space to continue--");
606
+ backend.refresh();
607
+ await wait_for(" ");
608
+ if (state.stdscr !== null) {
609
+ backend.clearok(state.stdscr, true);
610
+ }
611
+ backend.refresh();
612
+ }
613
+ /**
614
+ * discovered: Show what items have been discovered/identified.
615
+ */
616
+ async function discovered() {
617
+ const backend = getBackend();
618
+ const lines = [];
619
+ // Potions
620
+ for (let i = 0; i < MAXPOTIONS; i++) {
621
+ if (pot_info[i].oi_know) {
622
+ lines.push(`A potion of ${pot_info[i].oi_name} (${state.p_colors[i]})`);
623
+ }
624
+ }
625
+ // Scrolls
626
+ for (let i = 0; i < MAXSCROLLS; i++) {
627
+ if (scr_info[i].oi_know) {
628
+ lines.push(`A scroll of ${scr_info[i].oi_name} (${state.s_names[i]})`);
629
+ }
630
+ }
631
+ // Rings
632
+ for (let i = 0; i < MAXRINGS; i++) {
633
+ if (ring_info[i].oi_know) {
634
+ lines.push(`A ring of ${ring_info[i].oi_name} (${state.r_stones[i]})`);
635
+ }
636
+ }
637
+ // Sticks
638
+ for (let i = 0; i < MAXSTICKS; i++) {
639
+ if (ws_info[i].oi_know) {
640
+ const stickType = state.ws_type[i] || "staff";
641
+ lines.push(`A ${stickType} of ${ws_info[i].oi_name} (${state.ws_made[i]})`);
642
+ }
643
+ }
644
+ if (lines.length === 0) {
645
+ await msg("nothing discovered yet");
646
+ return;
647
+ }
648
+ backend.clear();
649
+ let row = 0;
650
+ for (const line of lines) {
651
+ backend.mvaddstr(row, 0, line);
652
+ row++;
653
+ if (row >= NUMLINES - 1) {
654
+ backend.mvaddstr(NUMLINES - 1, 0, "--Press space to continue--");
655
+ backend.refresh();
656
+ await wait_for(" ");
657
+ backend.clear();
658
+ row = 0;
659
+ }
660
+ }
661
+ backend.mvaddstr(NUMLINES - 1, 0, "--Press space to continue--");
662
+ backend.refresh();
663
+ await wait_for(" ");
664
+ if (state.stdscr !== null) {
665
+ backend.clearok(state.stdscr, true);
666
+ }
667
+ backend.refresh();
668
+ }
669
+ /**
670
+ * identify_char: Tell the player what a character on the screen represents.
671
+ */
672
+ async function identify_char() {
673
+ await msg("what do you want identified? ");
674
+ const ch = await readchar();
675
+ const charNames = {
676
+ "|": "wall of a room",
677
+ "-": "wall of a room",
678
+ ".": "floor",
679
+ "#": "passage",
680
+ "+": "door",
681
+ "^": "trap",
682
+ "%": "staircase",
683
+ "*": "gold",
684
+ ":": "food",
685
+ "!": "potion",
686
+ "?": "scroll",
687
+ ")": "weapon",
688
+ "]": "armor",
689
+ "=": "ring",
690
+ "/": "wand or staff",
691
+ ",": "the Amulet of Yendor",
692
+ "@": "you",
693
+ " ": "solid rock",
694
+ };
695
+ // Check if it's a monster letter
696
+ if (ch >= "A" && ch <= "Z") {
697
+ const monsterIndex = ch.charCodeAt(0) - "A".charCodeAt(0);
698
+ await msg("'%s': %s", ch, monsterTemplates[monsterIndex].m_name);
699
+ return;
700
+ }
701
+ const description = charNames[ch];
702
+ if (description !== undefined) {
703
+ await msg("'%s': %s", ch, description);
704
+ }
705
+ else {
706
+ await msg("'%s': unknown character", ch);
707
+ }
708
+ }
709
+ /**
710
+ * identify_trap: Show the type of an adjacent trap.
711
+ */
712
+ async function identify_trap() {
713
+ const { get_dir } = await import("./misc.js");
714
+ if (!await get_dir())
715
+ return;
716
+ const trapY = state.player.t_pos.y + state.delta.y;
717
+ const trapX = state.player.t_pos.x + state.delta.x;
718
+ if (trapY <= 0 || trapY >= NUMLINES - 1 || trapX < 0 || trapX >= NUMCOLS) {
719
+ await msg("no trap there");
720
+ return;
721
+ }
722
+ const pp = INDEX(trapY, trapX);
723
+ if (pp.p_ch !== TRAP) {
724
+ await msg("no trap there");
725
+ return;
726
+ }
727
+ const trapFlags = flat(trapY, trapX);
728
+ const trapType = trapFlags & F_TMASK;
729
+ const trapName = tr_name[trapType] || "a mysterious trap";
730
+ await msg("%s", trapName);
731
+ }
732
+ /**
733
+ * set_options: Allow the player to examine and set game options.
734
+ */
735
+ async function set_options() {
736
+ const backend = getBackend();
737
+ backend.clear();
738
+ backend.mvaddstr(0, 0, "Game options (press space to continue):");
739
+ backend.mvaddstr(2, 0, `terse: ${state.terse ? "on" : "off"}`);
740
+ backend.mvaddstr(3, 0, `fruit: ${state.fruit}`);
741
+ backend.mvaddstr(4, 0, `name: ${state.whoami}`);
742
+ backend.mvaddstr(5, 0, `jump: ${state.jump ? "on" : "off"}`);
743
+ backend.mvaddstr(NUMLINES - 1, 0, "--Press space to continue--");
744
+ backend.refresh();
745
+ await wait_for(" ");
746
+ if (state.stdscr !== null) {
747
+ backend.clearok(state.stdscr, true);
748
+ }
749
+ backend.refresh();
750
+ }
751
+ /**
752
+ * levit_check: Check if the player is levitating and can't use stairs.
753
+ * Returns true if the player is levitating (action blocked).
754
+ */
755
+ function levit_check() {
756
+ if (state.player.t_flags & ISLEVIT) {
757
+ return true;
758
+ }
759
+ return false;
760
+ }
761
+ //# sourceMappingURL=command.js.map