1 /* Copyright (c) Shawn Betts, Ryan Yeske, 2001                    */
2 /* NetHack may be freely redistributed.  See license for details. */
3 
4 /*
5  * "Main" file for the lisp/emacs window-port.  This contains most of
6  * the interface routines.  Please see doc/window.doc for an
7  * description of the window interface.
8  */
9 
10 #ifdef MSDOS			/* from compiler */
11 #define SHORT_FILENAMES
12 #endif
13 
14 #include "hack.h"
15 #include "winlisp.h"
16 #include "func_tab.h"
17 
18 #include "dlb.h"
19 #ifdef SHORT_FILENAMES
20 #include "patchlev.h"
21 #else
22 #include "patchlevel.h"
23 #endif
24 
25 #define CMD_KEY 0
26 #define CMD_EXT 1
27 #define CMD_LISP 2 /* These are commands specific to the lisp port */
28 
29 /* from tile.c */
30 extern short glyph2tile[];
31 extern int total_tiles_used;
32 
33 typedef struct
34 {
35   anything identifier;
36   char accelerator;
37 } lisp_menu_item_t;
38 
39 /* An iterator for assigning accelerator keys. */
40 static char lisp_current_accelerator;
41 
42 /* Helper structures to map menu id's to nethack anything's */
43 static lisp_menu_item_t lisp_menu_item_list[1000];
44 static int lisp_menu_list_size = 1000;
45 static int lisp_menu_list_num;
46 
47 extern char *enc_stat[];
48 const char *hunger_stat[] = {
49 	"Satiated",
50 	"",
51 	"Hungry",
52 	"Weak",
53 	"Fainting",
54 	"Fainted",
55 	"Starved"
56 };
57 
58 typedef struct
59 {
60   char *name;
61   int type;
62   int cmd;			/* The command (a keystroke) */
63 }cmd_index_t;
64 
65 #ifndef C
66 #define C(c)		(0x1f & (c))
67 #endif
68 
69 /* Taken from cmd.c */
70 cmd_index_t cmd_index[] = { {"gowest", CMD_KEY, 'h'},
71 			    {"gowestontop", CMD_KEY, 'H'},
72 			    {"gowestnear", CMD_KEY, C('h')},
73 
74 			    {"gosouth", CMD_KEY, 'j'},
75 			    {"gosouthontop", CMD_KEY, 'J'},
76 			    {"gosouthnear", CMD_KEY,  C('j')},
77 
78 			    {"gonorth", CMD_KEY, 'k'},
79 			    {"gonorthontop", CMD_KEY, 'K'},
80 			    {"gonorthnear", CMD_KEY, C('k')},
81 
82 			    {"goeast", CMD_KEY, 'l'},
83 			    {"goeastontop", CMD_KEY, 'L'},
84 			    {"goeastnear", CMD_KEY, C('l')},
85 
86 			    {"gonorthwest", CMD_KEY, 'y'},
87 			    {"gonorthwestontop", CMD_KEY, 'Y'},
88 			    {"gonorthwestnear", CMD_KEY, C('y')},
89 
90 			    {"gonortheast", CMD_KEY, 'u'},
91 			    {"gonortheastontop", CMD_KEY, 'U'},
92 			    {"gonortheastnear", CMD_KEY, C('u')},
93 
94 			    {"gosouthwest", CMD_KEY, 'b'},
95 			    {"gosouthwestontop", CMD_KEY, 'B'},
96 			    {"gosouthwestnear", CMD_KEY, C('b')},
97 
98 			    {"gosoutheast", CMD_KEY, 'n'},
99 			    {"gosoutheastontop", CMD_KEY, 'N'},
100 			    {"gosoutheastnear", CMD_KEY, C('n')},
101 
102 			    {"travel", CMD_KEY, '_'},
103 
104 			    {"idtrap", CMD_KEY, '^'},
105 			    {"apply", CMD_KEY, 'a'},
106 			    {"remarm", CMD_KEY, 'A'},
107 			    {"close", CMD_KEY, 'c'},
108 			    {"drop", CMD_KEY, 'd'},
109 
110 			    {"ddrop", CMD_KEY, 'D'},
111 			    {"eat", CMD_KEY, 'e'},
112 			    {"engrave", CMD_KEY, 'E'},
113 			    {"fire", CMD_KEY, 'f'},
114 			    {"inv", CMD_KEY, 'i'},
115 
116 			    {"typeinv", CMD_KEY, 'I'},
117 			    {"open", CMD_KEY, 'o'},
118 			    {"set", CMD_KEY, 'O'},
119 			    {"pay", CMD_KEY, 'p'},
120 			    {"puton", CMD_KEY, 'P'},
121 
122 			    {"drink", CMD_KEY, 'q'},
123 			    {"wieldquiver", CMD_KEY, 'Q'},
124 			    {"read", CMD_KEY, 'r'},
125 			    {"remring", CMD_KEY, 'R'},
126 			    {"search", CMD_KEY, 's'},
127 
128 			    {"save", CMD_KEY, 'S'},
129 			    {"throw", CMD_KEY, 't'},
130 			    {"takeoff", CMD_KEY, 'T'},
131 			    {"simpleversion", CMD_KEY, 'v'},
132 			    {"history", CMD_KEY, 'V'},
133 
134 			    {"wield", CMD_KEY, 'w'},
135 			    {"wear", CMD_KEY, 'W'},
136 			    {"swapweapon", CMD_KEY, 'x'},
137 			    {"enter_explore_mode", CMD_KEY, 'X'},
138 			    {"zap", CMD_KEY, 'z'},
139 
140 			    {"cast", CMD_KEY, 'Z'},
141 			    {"up", CMD_KEY, '<'},
142 			    {"down", CMD_KEY, '>'},
143 			    {"whatis", CMD_KEY, '/'},
144 			    {"help", CMD_KEY, '?'},
145 
146 			    {"whatdoes", CMD_KEY, '&'},
147 			    {"sh", CMD_KEY, '!'},
148 			    {"discovered", CMD_KEY, '\\'},
149 			    {"null", CMD_KEY, '.'},
150 			    {"look", CMD_KEY, ':'},
151 
152 			    {"quickwhatis", CMD_KEY, ';'},
153 			    {"pickup", CMD_KEY, ','},
154 			    {"togglepickup", CMD_KEY, '@'},
155 			    {"prinuse", CMD_KEY, '*'},
156 			    {"countgold", CMD_KEY, '$'},
157 
158 			    {"kick", CMD_KEY, C('d')},
159 			    {"listspells", CMD_KEY, '+'},
160 			    {"redraw", CMD_KEY, C('r')},
161 			    {"teleport", CMD_KEY, C('t')},
162 			    {"callmon", CMD_KEY, 'C'},
163 			    {"fight", CMD_KEY, 'F'},
164 			    {"movenear", CMD_KEY, 'g'},
165 			    {"move", CMD_KEY, 'G'},
166 			    {"movenopickuporfight", CMD_KEY, 'm'},
167 			    {"movenopickup", CMD_KEY, 'M'},
168 			    {"showweapon", CMD_KEY, ')'},
169 			    {"showarmor", CMD_KEY, '['},
170 			    {"showrings", CMD_KEY, '='},
171 			    {"showamulet", CMD_KEY, '"'},
172 			    {"showtool", CMD_KEY, '('},
173 			    {"attributes", CMD_KEY, C('x')},
174 #ifdef REDO
175 			    {"again", CMD_KEY, DOAGAIN},
176 #endif /* REDO */
177 
178 			    /* wizard commands */
179 			    {"wiz_detect", CMD_KEY, C('e')},
180 			    {"wiz_map", CMD_KEY, C('f')},
181 			    {"wiz_genesis", CMD_KEY, C('g')},
182 			    {"wiz_identify", CMD_KEY, C('i')},
183 			    {"wiz_where", CMD_KEY, C('o')},
184 			    {"wiz_level_tele", CMD_KEY, C('v')},
185 			    {"wiz_wish", CMD_KEY, C('w')},
186 
187 			    /* wizard extended commands */
188 #ifdef WIZARD
189 			    {"light sources", CMD_EXT, 0},
190 			    {"seenv", CMD_EXT, 0},
191 			    {"stats", CMD_EXT, 0},
192 			    {"timeout", CMD_EXT, 0},
193 			    {"vision", CMD_EXT, 0},
194 #ifdef DEBUG
195 			    {"wizdebug", CMD_EXT, 0},
196 #endif /* DEBUG */
197 			    {"wmode", CMD_EXT, 0},
198 #endif /* WIZARD */
199 			    {"pray", CMD_EXT, 0},
200 			    {"adjust", CMD_EXT, 0},
201 			    {"chat", CMD_EXT, 0},
202 			    {"conduct", CMD_EXT, 0},
203 			    {"dip", CMD_EXT, 0},
204 
205 			    {"enhance", CMD_EXT, 0},
206 			    {"force", CMD_EXT, 0},
207 			    {"invoke", CMD_EXT, 0},
208 			    {"jump", CMD_EXT, 0},
209 			    {"loot", CMD_EXT, 0},
210 
211 			    {"monster", CMD_EXT, 0},
212 			    {"name", CMD_EXT, 0},
213 			    {"offer", CMD_EXT, 0},
214 			    {"quit", CMD_EXT, 0},
215 			    {"ride", CMD_EXT, 0},
216 
217 			    {"rub", CMD_EXT, 0},
218 			    {"sit", CMD_EXT, 0},
219 			    {"turn", CMD_EXT, 0},
220 			    {"twoweapon", CMD_EXT, 0},
221 			    {"untrap", CMD_EXT, 0},
222 
223 			    {"version", CMD_EXT, 0},
224 			    {"wipe", CMD_EXT, 0},
225 
226 			    /* Lisp port specific commands  */
227 			    {"options", CMD_LISP, 0},
228 
229 			    {0, CMD_KEY, '\0'} };
230 
231 /* This variable is set when the user has selected an extended command. */
232 static int extended_cmd_id;
233 
234 /* Interface definition, for windows.c */
235 struct window_procs lisp_procs = {
236   "lisp",
237   WC_COLOR|WC_HILITE_PET,
238   0L,
239   lisp_init_nhwindows,
240   lisp_player_selection,
241   lisp_askname,
242   lisp_get_nh_event,
243   lisp_exit_nhwindows,
244   lisp_suspend_nhwindows,
245   lisp_resume_nhwindows,
246   lisp_create_nhwindow,
247   lisp_clear_nhwindow,
248   lisp_display_nhwindow,
249   lisp_destroy_nhwindow,
250   lisp_curs,
251   lisp_putstr,
252   lisp_display_file,
253   lisp_start_menu,
254   lisp_add_menu,
255   lisp_end_menu,
256   lisp_select_menu,
257   genl_message_menu,
258   lisp_update_inventory,
259   lisp_mark_synch,
260   lisp_wait_synch,
261 #ifdef CLIPPING
262   lisp_cliparound,
263 #endif
264 #ifdef POSITIONBAR
265   lisp_update_positionbar,
266 #endif
267   lisp_print_glyph,
268   lisp_raw_print,
269   lisp_raw_print_bold,
270   lisp_nhgetch,
271   lisp_nh_poskey,
272   lisp_nhbell,
273   lisp_doprev_message,
274   lisp_yn_function,
275   lisp_getlin,
276   lisp_get_ext_cmd,
277   lisp_number_pad,
278   lisp_delay_output,
279 #ifdef CHANGE_COLOR	/* the Mac uses a palette device */
280   donull,
281   donull,
282 #endif
283   /* other defs that really should go away (they're tty specific) */
284   lisp_start_screen,
285   lisp_end_screen,
286   lisp_outrip,
287   genl_preference_update,
288 };
289 
290 /* macros for printing lisp output */
291 #define lisp_cmd(s,body)			\
292   do						\
293     {						\
294       printf("(nhapi-%s ",s);		\
295       body;					\
296       printf(")\n");				\
297     }						\
298   while (0)
299 /* #define lisp_cmd0(s) printf ("(nhapi-%s)\n", s) */
300 #define lisp_list(body)				\
301   do						\
302     {						\
303       printf("(");				\
304       body; 					\
305       printf(") ");				\
306     }						\
307   while (0)
308 
309 #define lisp_open printf("(")
310 #define lisp_close printf(") ")
311 #define lisp_quote printf("'")
312 #define lisp_dot printf(". ")
313 #define lisp_t printf("t ")
314 #define lisp_nil printf("nil ")
315 #define lisp_literal(x)				\
316   do						\
317     {						\
318       lisp_quote;				\
319       printf ("%s ", x);			\
320     }						\
321   while (0)
322 #define lisp_cons(x,y)				\
323   do						\
324     {						\
325       lisp_open;				\
326       x;					\
327       lisp_dot;					\
328       y;					\
329       lisp_close;				\
330     }						\
331   while (0)
332 #define lisp_int(i) printf("%d ",i)
333 #define lisp_long(i) printf("%ld", i)
334 #define lisp_coord(c) printf("'(%d,%d) ",c.x,c.y)
335 #define lisp_boolean(i) printf("%s ",i?"t":"nil")
336 #define lisp_string(s)					\
337   do							\
338      {							\
339        int nhi;						\
340        printf ("\"");					\
341        if (s)						\
342 	 for (nhi=0;nhi<strlen(s);nhi++)		\
343 	   {						\
344 	     if (s[nhi] == 34 				\
345 		 || s[nhi] == 92) putchar('\\');	\
346 	     putchar(s[nhi]);				\
347 	   }						\
348        printf("\" ");					\
349      }							\
350   while (0)
351 
352 static const char*
attr_to_string(attr)353 attr_to_string(attr)
354      int attr;
355 {
356   switch (attr)
357     {
358     case ATR_NONE:
359       return "atr-none";
360     case ATR_ULINE:
361       return "atr-uline";
362     case ATR_BOLD:
363       return "atr-bold";
364     case ATR_BLINK:
365       return "atr-blink";
366     case ATR_INVERSE:
367       return "atr-inverse";
368     default:
369       /* Should never happen. */
370       impossible ("Invalid attribute code.");
371       exit (EXIT_FAILURE);
372       break;
373     }
374 }
375 
376 static const char*
special_glyph_to_string(special)377 special_glyph_to_string(special)
378      unsigned special;
379 {
380   switch (special)
381     {
382     case MG_CORPSE:
383       return "corpse";
384     case MG_INVIS:
385       return "invis";
386     case MG_DETECT:
387       return "detect";
388     case MG_PET:
389       return "pet";
390     case MG_RIDDEN:
391       return "ridden";
392     }
393 
394   /* If it's a combination, just return special. */
395   if (special)
396     return "special";
397   else
398     return "none";
399 }
400 
401 static const char*
wintype_to_string(type)402 wintype_to_string(type)
403      int type;
404 {
405   switch (type)
406     {
407     case NHW_MAP:
408       return "nhw-map";
409     case NHW_MESSAGE:
410       return "nhw-message";
411     case NHW_STATUS:
412       return "nhw-status";
413     case NHW_MENU:
414       return "nhw-menu";
415     case NHW_TEXT:
416       return "nhw-text";
417     default:
418       fprintf (stderr, "Invalid window code\n");
419       exit (EXIT_FAILURE);
420       break;
421     }
422 }
423 
424 static const char*
how_to_string(how)425 how_to_string (how)
426      int how;
427 {
428   switch (how)
429     {
430     case PICK_NONE:
431       return "pick-none";
432     case PICK_ONE:
433       return "pick-one";
434     case PICK_ANY:
435       return "pick-any";
436     default:
437       impossible ("Invalid how value %d", how);
438     }
439   return NULL;
440 }
441 
442 static int
read_int(prompt,i)443 read_int (prompt, i)
444      const char* prompt;
445      int *i;
446 {
447   char line[BUFSZ];
448   int rv;
449   printf ("%s> ", prompt);
450   fflush(stdout);
451   fgets (line, BUFSZ, stdin);
452   rv = sscanf (line, "%d", i);
453   if (rv != 1) *i = -1;
454   return rv;
455 }
456 
457 static int
read_string(prompt,str)458 read_string (prompt, str)
459      const char* prompt;
460      char **str;
461 {
462   char* rv;
463   int len;
464   int size;
465   char tmp[BUFSZ];
466 
467   len = 0;
468   size = BUFSZ * 2;
469   *str = malloc (size);
470   (*str)[0] = '\0';
471 
472   printf ("%s> ", prompt);
473   fflush(stdout);
474   do
475     {
476       /* Read the string */
477       rv = fgets (tmp, BUFSZ, stdin);
478       if (rv == NULL)
479 	break;
480 
481       len += strlen (tmp);
482       if (len >= size - 1)
483 	{
484 	  size *= 2;
485 	  *str = realloc (*str, size);
486 	  if (*str == NULL)
487 	    panic ("Memory allocation failure; cannot get %u bytes", size);
488 	}
489       strcat (*str, tmp);
490     } while (tmp[strlen (tmp) - 1] != '\n');
491 
492   /* Did we read a string or error out? */
493   if (rv == NULL)
494     {
495       free (*str);
496       return -1;
497     }
498   else
499     {
500       /* chop the newline */
501       (*str) [strlen (*str) - 1] = '\0';
502       return 0;
503     }
504 }
505 
506 
507 static int
read_command(prompt,cmd,count)508 read_command (prompt, cmd, count)
509      const char *prompt;
510      char *cmd;
511      char *count;
512 {
513   char *buf;
514   int rv;
515   cmd[0] = '\0';
516   *count = 0;
517   if (read_string (prompt, &buf) == -1)
518     return -1;
519   rv = sscanf (buf, "%s %s", cmd, count);
520   free (buf);
521   if (rv != 2) *count = 0;
522   return rv;
523 }
524 
525 void
bail(mesg)526 bail(mesg)
527      const char *mesg;
528 {
529   clearlocks ();
530   lisp_exit_nhwindows (mesg);
531   terminate (EXIT_SUCCESS);
532   /*NOTREACHED*/
533 }
534 
535 void
win_lisp_init()536 win_lisp_init ()
537 {
538   /* Code to be executed on startup. */
539 }
540 
541 void
lisp_player_selection()542 lisp_player_selection ()
543 {
544 	int i, k, n;
545 	char pick4u = 'n', thisch, lastch = 0;
546 	char pbuf[QBUFSZ], plbuf[QBUFSZ];
547 	winid win;
548 	anything any;
549 	menu_item *selected = 0;
550 
551 	/* prevent an unnecessary prompt */
552 	rigid_role_checks();
553 
554 	/* Should we randomly pick for the player? */
555 	if (!flags.randomall &&
556 	    (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
557 	     flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
558 
559 	  pick4u = lisp_yn_function ("Shall I pick a character for you? [ynq] ", "ynq", 'y');
560 
561 	  if (pick4u != 'y' && pick4u != 'n')
562 	    {
563 	    give_up:	/* Quit */
564 	      if (selected) free((genericptr_t) selected);
565 	      bail((char *)0);
566 	      /*NOTREACHED*/
567 	      return;
568 	    }
569 
570 	}
571 
572 	(void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
573 			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
574 
575 	/* Select a role, if necessary */
576 	/* we'll try to be compatible with pre-selected race/gender/alignment,
577 	 * but may not succeed */
578 	if (flags.initrole < 0) {
579 	    char rolenamebuf[QBUFSZ];
580 	    /* Process the choice */
581 	    if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) {
582 		/* Pick a random role */
583 		flags.initrole = pick_role(flags.initrace, flags.initgend,
584 						flags.initalign, PICK_RANDOM);
585 		if (flags.initrole < 0) {
586 /* 		    lisp_putstr(BASE_WINDOW, 0, "Incompatible role!"); */
587 		    flags.initrole = randrole();
588 		}
589  	    } else {
590 		/* Prompt for a role */
591 		win = create_nhwindow(NHW_MENU);
592 		start_menu(win);
593 		any.a_void = 0;         /* zero out all bits */
594 		for (i = 0; roles[i].name.m; i++) {
595 		    if (ok_role(i, flags.initrace, flags.initgend,
596 							flags.initalign)) {
597 			any.a_int = i+1;	/* must be non-zero */
598 			thisch = lowc(roles[i].name.m[0]);
599 			if (thisch == lastch) thisch = highc(thisch);
600 			if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) {
601 				if (flags.initgend == 1  && roles[i].name.f)
602 					Strcpy(rolenamebuf, roles[i].name.f);
603 				else
604 					Strcpy(rolenamebuf, roles[i].name.m);
605 			} else {
606 				if (roles[i].name.f) {
607 					Strcpy(rolenamebuf, roles[i].name.m);
608 					Strcat(rolenamebuf, "/");
609 					Strcat(rolenamebuf, roles[i].name.f);
610 				} else
611 					Strcpy(rolenamebuf, roles[i].name.m);
612 			}
613 			add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, thisch,
614 			    0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED);
615 			lastch = thisch;
616 		    }
617 		}
618 		any.a_int = pick_role(flags.initrace, flags.initgend,
619 				    flags.initalign, PICK_RANDOM)+1;
620 		if (any.a_int == 0)	/* must be non-zero */
621 		    any.a_int = randrole()+1;
622 		add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , '*', 0, ATR_NONE,
623 				"Random", MENU_UNSELECTED);
624 		any.a_int = i+1;	/* must be non-zero */
625 		add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , 'q', 0, ATR_NONE,
626 				"Quit", MENU_UNSELECTED);
627 		Sprintf(pbuf, "Pick a role for your %s", plbuf);
628 		end_menu(win, pbuf);
629 		n = select_menu(win, PICK_ONE, &selected);
630 		destroy_nhwindow(win);
631 
632 		/* Process the choice */
633 		if (n != 1 || selected[0].item.a_int == any.a_int)
634 		    goto give_up;		/* Selected quit */
635 
636 		flags.initrole = selected[0].item.a_int - 1;
637 		free((genericptr_t) selected),	selected = 0;
638 	    }
639 	    (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
640 			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
641 	}
642 
643 	/* Select a race, if necessary */
644 	/* force compatibility with role, try for compatibility with
645 	 * pre-selected gender/alignment */
646 	if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
647 	    /* pre-selected race not valid */
648 	    if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) {
649 		flags.initrace = pick_race(flags.initrole, flags.initgend,
650 							flags.initalign, PICK_RANDOM);
651 		if (flags.initrace < 0) {
652 /* 		    lisp_putstr(BASE_WINDOW, 0, "Incompatible race!"); */
653 		    flags.initrace = randrace(flags.initrole);
654 		}
655 	    } else {	/* pick4u == 'n' */
656 		/* Count the number of valid races */
657 		n = 0;	/* number valid */
658 		k = 0;	/* valid race */
659 		for (i = 0; races[i].noun; i++) {
660 		    if (ok_race(flags.initrole, i, flags.initgend,
661 							flags.initalign)) {
662 			n++;
663 			k = i;
664 		    }
665 		}
666 		if (n == 0) {
667 		    for (i = 0; races[i].noun; i++) {
668 			if (validrace(flags.initrole, i)) {
669 			    n++;
670 			    k = i;
671 			}
672 		    }
673 		}
674 
675 		/* Permit the user to pick, if there is more than one */
676 		if (n > 1) {
677 		    win = create_nhwindow(NHW_MENU);
678 		    start_menu(win);
679 		    any.a_void = 0;         /* zero out all bits */
680 		    for (i = 0; races[i].noun; i++)
681 			if (ok_race(flags.initrole, i, flags.initgend,
682 							flags.initalign)) {
683 			    any.a_int = i+1;	/* must be non-zero */
684 			    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, races[i].noun[0],
685 				0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
686 			}
687 		    any.a_int = pick_race(flags.initrole, flags.initgend,
688 					flags.initalign, PICK_RANDOM)+1;
689 		    if (any.a_int == 0)	/* must be non-zero */
690 			any.a_int = randrace(flags.initrole)+1;
691 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , '*', 0, ATR_NONE,
692 				    "Random", MENU_UNSELECTED);
693 		    any.a_int = i+1;	/* must be non-zero */
694 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , 'q', 0, ATR_NONE,
695 				    "Quit", MENU_UNSELECTED);
696 		    Sprintf(pbuf, "Pick the race of your %s", plbuf);
697 		    end_menu(win, pbuf);
698 		    n = select_menu(win, PICK_ONE, &selected);
699 		    destroy_nhwindow(win);
700 		    if (n != 1 || selected[0].item.a_int == any.a_int)
701 			goto give_up;		/* Selected quit */
702 
703 		    k = selected[0].item.a_int - 1;
704 		    free((genericptr_t) selected),	selected = 0;
705 		}
706 		flags.initrace = k;
707 	    }
708 	    (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
709 			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
710 	}
711 
712 	/* Select a gender, if necessary */
713 	/* force compatibility with role/race, try for compatibility with
714 	 * pre-selected alignment */
715 	if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
716 						flags.initgend)) {
717 	    /* pre-selected gender not valid */
718 	    if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) {
719 		flags.initgend = pick_gend(flags.initrole, flags.initrace,
720 						flags.initalign, PICK_RANDOM);
721 		if (flags.initgend < 0) {
722 /* 		    lisp_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */
723 		    flags.initgend = randgend(flags.initrole, flags.initrace);
724 		}
725 	    } else {	/* pick4u == 'n' */
726 		/* Count the number of valid genders */
727 		n = 0;	/* number valid */
728 		k = 0;	/* valid gender */
729 		for (i = 0; i < ROLE_GENDERS; i++) {
730 		    if (ok_gend(flags.initrole, flags.initrace, i,
731 							flags.initalign)) {
732 			n++;
733 			k = i;
734 		    }
735 		}
736 		if (n == 0) {
737 		    for (i = 0; i < ROLE_GENDERS; i++) {
738 			if (validgend(flags.initrole, flags.initrace, i)) {
739 			    n++;
740 			    k = i;
741 			}
742 		    }
743 		}
744 
745 		/* Permit the user to pick, if there is more than one */
746 		if (n > 1) {
747 		    win = create_nhwindow(NHW_MENU);
748 		    start_menu(win);
749 		    any.a_void = 0;         /* zero out all bits */
750 		    for (i = 0; i < ROLE_GENDERS; i++)
751 			if (ok_gend(flags.initrole, flags.initrace, i,
752 							    flags.initalign)) {
753 			    any.a_int = i+1;
754 			    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, genders[i].adj[0],
755 				0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
756 			}
757 		    any.a_int = pick_gend(flags.initrole, flags.initrace,
758 					    flags.initalign, PICK_RANDOM)+1;
759 		    if (any.a_int == 0)	/* must be non-zero */
760 			any.a_int = randgend(flags.initrole, flags.initrace)+1;
761 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , '*', 0, ATR_NONE,
762 				    "Random", MENU_UNSELECTED);
763 		    any.a_int = i+1;	/* must be non-zero */
764 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , 'q', 0, ATR_NONE,
765 				    "Quit", MENU_UNSELECTED);
766 		    Sprintf(pbuf, "Pick the gender of your %s", plbuf);
767 		    end_menu(win, pbuf);
768 		    n = select_menu(win, PICK_ONE, &selected);
769 		    destroy_nhwindow(win);
770 		    if (n != 1 || selected[0].item.a_int == any.a_int)
771 			goto give_up;		/* Selected quit */
772 
773 		    k = selected[0].item.a_int - 1;
774 		    free((genericptr_t) selected),	selected = 0;
775 		}
776 		flags.initgend = k;
777 	    }
778 	    (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
779 			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
780 	}
781 
782 	/* Select an alignment, if necessary */
783 	/* force compatibility with role/race/gender */
784 	if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
785 							flags.initalign)) {
786 	    /* pre-selected alignment not valid */
787 	    if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) {
788 		flags.initalign = pick_align(flags.initrole, flags.initrace,
789 							flags.initgend, PICK_RANDOM);
790 		if (flags.initalign < 0) {
791 /* 		    lisp_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */
792 		    flags.initalign = randalign(flags.initrole, flags.initrace);
793 		}
794 	    } else {	/* pick4u == 'n' */
795 		/* Count the number of valid alignments */
796 		n = 0;	/* number valid */
797 		k = 0;	/* valid alignment */
798 		for (i = 0; i < ROLE_ALIGNS; i++) {
799 		    if (ok_align(flags.initrole, flags.initrace, flags.initgend,
800 							i)) {
801 			n++;
802 			k = i;
803 		    }
804 		}
805 		if (n == 0) {
806 		    for (i = 0; i < ROLE_ALIGNS; i++) {
807 			if (validalign(flags.initrole, flags.initrace, i)) {
808 			    n++;
809 			    k = i;
810 			}
811 		    }
812 		}
813 
814 		/* Permit the user to pick, if there is more than one */
815 		if (n > 1) {
816 		    win = create_nhwindow(NHW_MENU);
817 		    start_menu(win);
818 		    any.a_void = 0;         /* zero out all bits */
819 		    for (i = 0; i < ROLE_ALIGNS; i++)
820 			if (ok_align(flags.initrole, flags.initrace,
821 							flags.initgend, i)) {
822 			    any.a_int = i+1;
823 			    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, aligns[i].adj[0],
824 				 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
825 			}
826 		    any.a_int = pick_align(flags.initrole, flags.initrace,
827 					    flags.initgend, PICK_RANDOM)+1;
828 		    if (any.a_int == 0)	/* must be non-zero */
829 			any.a_int = randalign(flags.initrole, flags.initrace)+1;
830 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , '*', 0, ATR_NONE,
831 				    "Random", MENU_UNSELECTED);
832 		    any.a_int = i+1;	/* must be non-zero */
833 		    add_menu(win, NO_GLYPH, MENU_DEFCNT, &any , 'q', 0, ATR_NONE,
834 				    "Quit", MENU_UNSELECTED);
835 		    Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
836 		    end_menu(win, pbuf);
837 		    n = select_menu(win, PICK_ONE, &selected);
838 		    destroy_nhwindow(win);
839 		    if (n != 1 || selected[0].item.a_int == any.a_int)
840 			goto give_up;		/* Selected quit */
841 
842 		    k = selected[0].item.a_int - 1;
843 		    free((genericptr_t) selected),	selected = 0;
844 		}
845 		flags.initalign = k;
846 	    }
847 	}
848 	/* Success! */
849 /* 	lisp_display_nhwindow(BASE_WINDOW, FALSE); */
850 }
851 
852 /* Reads from standard in, the player's name. */
853 void
lisp_askname()854 lisp_askname ()
855 {
856   char *line;
857   lisp_cmd ("askname",);
858   read_string ("string", &line);
859   strncpy (plname, line, PL_NSIZ);
860   plname[PL_NSIZ-1] = '\0';
861   free (line);
862 }
863 
864 /* This is a noop for tty and X, so should it be a noop for us too? */
865 void
lisp_get_nh_event()866 lisp_get_nh_event ()
867 {
868 /*   lisp_cmd ("get-event",); */
869 }
870 
871 /* Global Functions */
872 void
lisp_raw_print(str)873 lisp_raw_print(str)
874      const char *str;
875 {
876   lisp_cmd ("raw-print", lisp_string (str));
877 }
878 
879 void
lisp_raw_print_bold(str)880 lisp_raw_print_bold(str)
881      const char *str;
882 {
883   lisp_cmd ("raw-print-bold", lisp_string (str));
884 }
885 
886 void
lisp_curs(window,x,y)887 lisp_curs(window, x, y)
888      winid window;
889      int x, y;
890 {
891   if (window == WIN_MAP)
892     lisp_cmd ("curs",
893 	      lisp_int (x);
894 	      lisp_int (y));
895   else if (window == WIN_STATUS)
896     {
897       /* do nothing */
898     }
899   else
900     lisp_cmd ("error", lisp_string("lisp_curs bad window"); lisp_int (window));
901 }
902 
903 /* Send the options to the lisp process */
904 static void
get_options()905 get_options()
906 {
907   lisp_cmd ("options",
908 	    lisp_boolean(iflags.cbreak);	/* in cbreak mode, rogue format */
909 	    lisp_boolean(iflags.DECgraphics);	/* use DEC VT-xxx extended character set */
910 	    lisp_boolean(iflags.echo);		/* 1 to echo characters */
911 	    lisp_boolean(iflags.IBMgraphics);	/* use IBM extended character set */
912 	    lisp_int(iflags.msg_history);	/* hint: # of top lines to save */
913 	    lisp_boolean(iflags.num_pad);	/* use numbers for movement commands */
914 	    lisp_boolean(iflags.news);		/* print news */
915 	    lisp_boolean(iflags.window_inited); /* true if init_nhwindows() completed */
916 	    lisp_boolean(iflags.vision_inited); /* true if vision is ready */
917 	    lisp_boolean(iflags.menu_tab_sep);	/* Use tabs to separate option menu fields */
918 	    lisp_boolean(iflags.menu_requested); /* Flag for overloaded use of 'm' prefix
919 						  * on some non-move commands */
920 	    lisp_int(iflags.num_pad_mode);
921 	    lisp_int(iflags.purge_monsters);	/* # of dead monsters still on fmon list */
922 /* 	    lisp_int(*iflags.opt_booldup);	/\* for duplication of boolean opts in config file *\/ */
923 /* 	    lisp_int(*iflags.opt_compdup);	/\* for duplication of compound opts in config file *\/ */
924 	    lisp_int(iflags.bouldersym);	/* symbol for boulder display */
925 	    lisp_coord(iflags.travelcc);	/* coordinates for travel_cache */
926 #ifdef WIZARD
927 	    lisp_boolean(iflags.sanity_check); /* run sanity checks */
928 	    lisp_boolean(iflags.mon_polycontrol); /* debug: control monster polymorphs */
929 #endif
930 	    );
931 }
932 
933 static void
generate_status_line()934 generate_status_line ()
935 {
936   /* Ripped from botl.c */
937   int hp, hpmax;
938 
939   hp = Upolyd ? u.mh : u.uhp;
940   hpmax = Upolyd ? u.mhmax : u.uhpmax;
941   if(hp < 0) hp = 0;
942 
943   printf ("(nhapi-update-status ");
944   lisp_quote;
945   printf ("(");
946   lisp_list (lisp_string ("name");
947 	     lisp_string (plname););
948   if (Upolyd)
949     {
950       lisp_list (lisp_string ("rank");
951 		 lisp_nil);
952       lisp_list (lisp_string ("monster");
953 		 lisp_string (mons[u.umonnum].mname));
954     }
955   else
956     {
957       lisp_list (lisp_string ("rank");
958 		 lisp_string (rank_of(u.ulevel, Role_switch, flags.female)););;
959       lisp_list (lisp_string ("monster");
960 		 lisp_nil);
961     }
962 
963   lisp_list (lisp_string ("St");
964 	     lisp_int (ACURR(A_STR)););
965   lisp_list (lisp_string ("Dx");
966 	     lisp_int (ACURR(A_DEX)););
967   lisp_list (lisp_string ("Co");
968 	     lisp_int (ACURR(A_CON)););
969   lisp_list (lisp_string ("In");
970 	     lisp_int (ACURR(A_INT)););
971   lisp_list (lisp_string ("Wi");
972 	     lisp_int (ACURR(A_WIS)););
973   lisp_list (lisp_string ("Ch");
974 	     lisp_int (ACURR(A_CHA)););
975   lisp_list (lisp_string ("Align");
976 	     if (u.ualign.type == A_CHAOTIC)
977 	     lisp_string ("Chaotic");
978 	     else if (u.ualign.type == A_NEUTRAL)
979 	     lisp_string ("Neutral");
980 	     else
981 	     lisp_string ("Lawful"););
982 
983 #ifdef SCORE_ON_BOTL
984   lisp_list (lisp_string ("Score");
985 	     lisp_int (botl_score()););
986 #endif
987 
988   if (In_endgame(&u.uz))
989     {
990       lisp_list (lisp_string ("Dungeon");
991 		 if (Is_astralevel(&u.uz))
992 		 lisp_string ("Astral Plane");
993 		 else
994 		 lisp_string ("End Game"););
995     }
996   else
997     {
998       lisp_list (lisp_string ("Dungeon");
999 		 lisp_string (dungeons[u.uz.dnum].dname););
1000     }
1001 
1002       lisp_list (lisp_string ("Dlvl");
1003 		 lisp_int (depth(&u.uz)););
1004 
1005   lisp_list (lisp_string ("$");
1006 	     lisp_long (u.ugold););
1007   lisp_list (lisp_string ("HP");
1008 	     lisp_int (hp););
1009   lisp_list (lisp_string ("HPmax");
1010 	     lisp_int (hpmax););
1011   lisp_list (lisp_string ("PW");
1012 	     lisp_int (u.uen););
1013   lisp_list (lisp_string ("PWmax");
1014 	     lisp_int (u.uenmax););
1015   lisp_list (lisp_string ("AC");
1016 	     lisp_int (u.uac););
1017 
1018   if (Upolyd)
1019     {
1020       lisp_list (lisp_string ("HD");
1021 		 lisp_int (mons[u.umonnum].mlevel););
1022     }
1023   else
1024     {
1025       lisp_list (lisp_string ("HD");
1026 		 lisp_nil);
1027     }
1028 
1029   lisp_list (lisp_string ("Level");
1030 	     lisp_int (u.ulevel););
1031 #ifdef EXP_ON_BOTL
1032   lisp_list (lisp_string ("XP");
1033 	     lisp_long (u.uexp););
1034 #endif
1035   lisp_list (lisp_string ("T");
1036 	     lisp_long (moves););
1037 
1038   if (Confusion)
1039     lisp_list (lisp_string ("confusion"); lisp_string ("Conf"));
1040   else
1041     lisp_list (lisp_string ("confusion"); lisp_nil);
1042 
1043   if (u.uhs != 1)
1044     lisp_list (lisp_string ("hunger"); lisp_string (hunger_stat[u.uhs]));
1045   else
1046     lisp_list (lisp_string ("hunger"); lisp_nil);
1047 
1048   if (Sick)
1049     {
1050       if (u.usick_type & SICK_VOMITABLE)
1051 	lisp_list (lisp_string ("sick"); lisp_string ("FoodPois"));
1052       if (u.usick_type & SICK_NONVOMITABLE)
1053 	lisp_list (lisp_string ("sick"); lisp_string ("Ill"));
1054     }
1055   else
1056     lisp_list (lisp_string ("sick"); lisp_nil);
1057 
1058   if (Blind)
1059     lisp_list (lisp_string ("blind"); lisp_string ("Blind"));
1060   else
1061     lisp_list (lisp_string ("blind"); lisp_nil);
1062 
1063   if (Stunned)
1064     lisp_list (lisp_string ("stunned"); lisp_string ("Stun"));
1065   else
1066     lisp_list (lisp_string ("stunned"); lisp_nil);
1067 
1068   if (Hallucination)
1069     lisp_list (lisp_string ("hallucination"); lisp_string ("Hallu"));
1070   else
1071     lisp_list (lisp_string ("hallucination"); lisp_nil);
1072 
1073   if (Slimed)
1074     lisp_list (lisp_string ("slimed"); lisp_string ("Slime"));
1075   else
1076     lisp_list (lisp_string ("slimed"); lisp_nil);
1077 
1078   if (near_capacity() > UNENCUMBERED)
1079     lisp_list (lisp_string ("encumbrance");
1080 	       lisp_string (enc_stat[near_capacity()]));
1081   else
1082     lisp_list (lisp_string ("encumbrance"); lisp_nil);
1083 
1084   printf (" ))\n");
1085 }
1086 
1087 void
lisp_putstr(window,attr,str)1088 lisp_putstr(window, attr, str)
1089      winid window;
1090      int attr;
1091      const char *str;
1092 {
1093   static char statline1[BUFSZ] = "";
1094   if (window == WIN_STATUS)
1095     {
1096       if (statline1[0]=='\0')
1097 	Strcpy (statline1, str);
1098       else
1099 	{
1100 	  generate_status_line ();
1101 	  statline1[0]='\0';
1102 	}
1103     }
1104   else if (window == WIN_MESSAGE)
1105     lisp_cmd ("message",
1106 	      lisp_literal (attr_to_string (attr));
1107 	      lisp_string (str));
1108   else
1109     lisp_cmd ("menu-putstr",
1110 	      lisp_int (window);
1111 	      lisp_literal (attr_to_string (attr));
1112 	      lisp_string (str));
1113 }
1114 
1115 void
lisp_start_menu(window)1116 lisp_start_menu(window)
1117      winid window;
1118 {
1119   lisp_menu_list_num = 0;
1120   lisp_current_accelerator = 'a';
1121   lisp_cmd ("start-menu", lisp_int (window));
1122 }
1123 
1124 void
lisp_add_menu(window,glyph,cnt,identifier,ch,gch,attr,str,preselected)1125 lisp_add_menu(window, glyph, cnt, identifier, ch, gch, attr, str, preselected)
1126     winid window;		/* window to use, must be of type NHW_MENU */
1127     int glyph;			/* glyph to display with item (unused) */
1128     int cnt;                    /* max number of times this item can be selected */
1129     const anything *identifier;	/* what to return if selected */
1130     char ch;			/* keyboard accelerator (0 = pick our own) */
1131     char gch;			/* group accelerator (0 = no group) */
1132     int attr;			/* attribute for string (like tty_putstr()) */
1133     const char *str;		/* menu string */
1134     boolean preselected;	/* item is marked as selected */
1135 {
1136   if (identifier->a_void)
1137     {
1138       lisp_menu_item_list[lisp_menu_list_num].identifier = *identifier;
1139       if (ch == 0)
1140 	{
1141 	  ch = lisp_menu_item_list[lisp_menu_list_num].accelerator = lisp_current_accelerator;
1142 	  if (lisp_current_accelerator == 'z')
1143 	    lisp_current_accelerator = 'A';
1144 	  else
1145 	    lisp_current_accelerator++;
1146 	}
1147       else
1148 	lisp_menu_item_list[lisp_menu_list_num].accelerator = ch;
1149 
1150       lisp_menu_list_num++;
1151     }
1152   else
1153     ch = -1;
1154 
1155   lisp_cmd ("add-menu",
1156 	    lisp_int (window);
1157 	    lisp_int (glyph);
1158 	    lisp_int (glyph2tile[glyph]);
1159 	    lisp_int (ch);
1160 	    lisp_int (gch);
1161 	    lisp_literal (attr_to_string (attr));
1162 	    lisp_string (str);
1163 	    preselected ? lisp_t : lisp_nil;
1164 	    /* The max count feature is already implemented in
1165 	       nethack-el, so we can avoid emitting `cnt'.  */
1166 	    /* lisp_int (cnt); */
1167 	    );
1168 }
1169 
1170 void
lisp_end_menu(window,prompt)1171 lisp_end_menu(window, prompt)
1172     winid window;	/* menu to use */
1173     const char *prompt;	/* prompt to for menu */
1174 {
1175   lisp_cmd ("end-menu",
1176 	    lisp_int (window);
1177 	    lisp_string (prompt));
1178 }
1179 
1180 static int
lisp_get_menu_identifier(ch,identifier)1181 lisp_get_menu_identifier(ch, identifier)
1182      char ch;
1183      anything *identifier;
1184 {
1185   int i;
1186 
1187   for(i=0; i < lisp_menu_list_num; i++)
1188     {
1189       if( lisp_menu_item_list[i].accelerator == ch )
1190 	{
1191 	  *identifier = lisp_menu_item_list[i].identifier;
1192 	  return 1;
1193 	}
1194     }
1195 
1196   return 0;
1197 }
1198 
1199 int
lisp_select_menu(window,how,menu_list)1200 lisp_select_menu(window, how, menu_list)
1201     winid window;
1202     int how;
1203     menu_item **menu_list;
1204 {
1205   const char *delim = "() \n";
1206   char *list;
1207   char *token;
1208   int size = 0;
1209 
1210   lisp_cmd ("select-menu",
1211 	    lisp_int (window);
1212 	    lisp_literal (how_to_string (how)));
1213 
1214   read_string ("menu", &list);
1215 
1216 /*   lisp_prompt ("menu"); */
1217 /*   fgets (list, LINESIZ, stdin); */
1218 
1219   /* The client should submit a structure like this:
1220 
1221    ((ch count) (ch count) (ch count) ...)
1222 
1223    where ch is the accelerator for the menu item and count is the
1224    number of them to select.
1225 
1226    We strtok it so we just get id count id count id count. */
1227 
1228   token = strtok (list, delim);
1229 
1230   /* Start with some memory so realloc doesn't fail. */
1231   *menu_list = malloc (sizeof (menu_item));
1232   if (*menu_list == NULL)
1233     {
1234       panic ("Memory allocation failure; cannot get %u bytes",
1235 	     sizeof (menu_item));
1236     }
1237   size = 0;
1238 
1239   while (token != NULL)
1240     {
1241       /* Make more room in the array for the new item */
1242       size++;
1243       if ((*menu_list = realloc (*menu_list, size * sizeof (menu_item))) == NULL)
1244 	{
1245 	  panic ("Memory allocation failure; cannot get %u bytes",
1246 		 size * sizeof (menu_item));
1247 	}
1248 
1249       /* assign the item ID */
1250       lisp_get_menu_identifier (atoi (token), &(*menu_list)[size-1].item );
1251 
1252       /* Read the item count */
1253       token = strtok (NULL, delim);
1254       (*menu_list)[size-1].count = atoi (token);
1255 
1256       /* read the next item ID */
1257       token = strtok (NULL, delim);
1258     }
1259 
1260   free (list);
1261 
1262   return size;
1263 }
1264 
1265 /* This is a tty-specific hack. Do we need it? */
1266 char
lisp_message_menu(let,how,mesg)1267 lisp_message_menu(let, how, mesg)
1268      char let;
1269      int how;
1270      const char *mesg;
1271 {
1272   lisp_cmd ("message-menu",
1273 	    lisp_int (let);
1274 	    lisp_literal (how_to_string (how));
1275 	    lisp_string (mesg));
1276   return '\0';
1277 }
1278 
1279 static int
lisp_get_cmd(str)1280 lisp_get_cmd(str)
1281      const char *str;
1282 {
1283   int i;
1284 
1285   for (i=0; cmd_index[i].name != (char *)0; i++)
1286     {
1287       if (!strcmp (str, cmd_index[i].name))
1288 	return i;
1289     }
1290 
1291   return -1;
1292 }
1293 
1294 static int
lisp_get_ext_cmd_id(str)1295 lisp_get_ext_cmd_id (str)
1296      const char *str;
1297 {
1298   int i;
1299 
1300   for (i=0; extcmdlist[i].ef_txt != (char *)0; i++) {
1301     if (!strcmp (str, extcmdlist[i].ef_txt)) return i;
1302   }
1303 
1304   return -1;
1305 }
1306 
1307 /* static int */
1308 /* num_digits(n) */
1309 /*      int n; */
1310 /* { */
1311 /*   int i; */
1312 /*   int ret = 1; */
1313 
1314 /*   for (i=10;n / i; i *= 10) */
1315 /*     { */
1316 /*       ret++; */
1317 /*     } */
1318 
1319 /*   return ret; */
1320 /* } */
1321 
1322 /* static */
1323 /* int */
1324 /* power_of_ten (n) */
1325 /*      int n; */
1326 /* { */
1327 /*   int i; */
1328 /*   int power = 1; */
1329 
1330 /*   for (i=0; i<n; i++) */
1331 /*     { */
1332 /*       power *= 10; */
1333 /*     } */
1334 
1335 /*   return power; */
1336 /* } */
1337 
1338 int
lisp_nhgetch()1339 lisp_nhgetch()
1340 {
1341   /* multi is not 0 if this  */
1342   static char count_buf[BUFSIZ] = "";
1343   static char *count_pos = count_buf;
1344   static int count_cmd = -1;
1345   int cmd;
1346 
1347   if (*count_pos)
1348     {
1349       char *tmp = count_pos;
1350       count_pos++;
1351       return *tmp;
1352     }
1353 
1354   if (count_cmd >= 0)
1355     {
1356       cmd = count_cmd;
1357       count_cmd = -1;
1358     }
1359   else
1360     {
1361       char cmdstr[BUFSZ];
1362       int nh_cmd = 0;
1363 
1364       while (!nh_cmd)
1365 	{
1366 	  read_command ("command", cmdstr, count_buf);
1367 
1368 	  count_pos = count_buf;
1369 	  cmd = lisp_get_cmd (cmdstr);
1370 	  if (cmd == -1)
1371 	    {
1372 	      printf ("(nhapi-message 'atr-none \"undefined-command %s\")\n", cmdstr);
1373 	    }
1374 	  else if (cmd_index[cmd].type == CMD_LISP)
1375 	    {
1376 	      /* We have to handle Lisp commands in this inner loop, because
1377 		 they don't interact with the nethack layer. */
1378 	      /* FIXME: Maybe this should go in an array? */
1379 	      if (!strcmp(cmd_index[cmd].name, "options"))
1380 		{
1381 		  get_options();
1382 		}
1383 	    } else {
1384 	      /* We have a nh command. */
1385 	      nh_cmd = 1;
1386 	    }
1387 	}
1388 
1389       if (atoi (count_pos) > 1)
1390 	{
1391 	  char* tmp = count_pos;
1392 	  count_pos++;
1393 	  count_cmd = cmd;
1394 	  return *tmp;
1395 	}
1396       else
1397 	{
1398 	  /* Since the count is 1, zero out the string. */
1399 	  *count_pos = 0;
1400 	}
1401     }
1402 
1403   if (cmd_index[cmd].type == CMD_KEY)
1404     {
1405       return cmd_index[cmd].cmd;
1406     }
1407   else if (cmd_index[cmd].type == CMD_EXT)
1408     {
1409       if ((extended_cmd_id = lisp_get_ext_cmd_id (cmd_index[cmd].name)) == -1)
1410 	{
1411 	  /* Can never happen. */
1412 	  printf ("%s:%d: Bad extended command name\n", __FILE__,  __LINE__);
1413 	}
1414       return '#';
1415     }
1416   else
1417     {
1418       impossible ("Impossible command type: %d", cmd_index[cmd].type);
1419     }
1420   return NULL;
1421 }
1422 
1423 int
lisp_nh_poskey(x,y,mod)1424 lisp_nh_poskey(x, y, mod)
1425      int *x, *y, *mod;
1426 {
1427 /*    char scratch[256]; */
1428 
1429 /*    printf ("(nethack-api-poskey)\n"); */
1430 
1431 /*    scanf ("( %d %d '%255s )", x, y, scratch); */
1432 /*    if (!strcmp (scratch, "click-1")) *mod = CLICK_1; */
1433 /*    else *mod = CLICK_2; */
1434 
1435 /*    return 0; */
1436 
1437   return lisp_nhgetch();
1438 }
1439 
1440 static boolean inven_win_created = FALSE;
1441 
1442 /* These globals are used to keep track of window IDs. */
1443 static winid *winid_list = NULL;
1444 static int winid_list_len = 0;
1445 static int winid_list_max = 0;
1446 
1447 /* returns index into winid_list that can be used. */
1448 static int
find_empty_cell()1449 find_empty_cell ()
1450 {
1451   int i;
1452 
1453   /* Check for a vacant spot in the list. */
1454   for (i=0; i<winid_list_len; i++)
1455     {
1456       if (winid_list[i] == -1) return i;
1457     }
1458 
1459   /* no vacant ones, so grow the array. */
1460   if (winid_list_len >= winid_list_max)
1461     {
1462       winid_list_max *= 2;
1463       winid_list = realloc (winid_list, sizeof (int) * winid_list_max);
1464       if (winid_list == NULL)
1465 	bail ("Out of memory\n");
1466     }
1467   winid_list_len++;
1468 
1469   return winid_list_len-1;
1470 }
1471 
1472 static int
winid_is_taken(winid n)1473 winid_is_taken (winid n)
1474 {
1475   int i;
1476 
1477   for (i=0; i<winid_list_len; i++)
1478     if (winid_list[i] == n) return 1;
1479 
1480   return 0;
1481 }
1482 
1483 static int
add_winid(winid n)1484 add_winid (winid n)
1485 {
1486   if (winid_is_taken (n)) return 0; /* failed. */
1487 
1488   winid_list[find_empty_cell()] = n;
1489   return 1; /* success! */
1490 }
1491 
1492 static winid
get_unique_winid()1493 get_unique_winid ()
1494 {
1495   winid i;
1496 
1497   /* look for a unique number, and add it to the list of taken
1498      numbers. */
1499   i = 0;
1500   while (!add_winid (i)) i++;
1501 
1502   return i;
1503 }
1504 
1505 /* When a window is destroyed, it gives back its window number with
1506    this function. */
1507 static void
return_winid(winid n)1508 return_winid (winid n)
1509 {
1510   int i;
1511 
1512   for (i=0; i<winid_list_len; i++)
1513     {
1514       if (winid_list[i] == n)
1515 	{
1516 	  winid_list[i] = -1;
1517 	  return;
1518 	}
1519     }
1520 }
1521 
1522 static void
init_winid_list()1523 init_winid_list ()
1524 {
1525   winid_list_max = 10;
1526   winid_list_len = 0;
1527 
1528   winid_list = malloc (winid_list_max * sizeof (int));
1529 }
1530 
1531 /* Prints a create_nhwindow function and expects from stdin the id of
1532    this new window as a number. */
1533 winid
lisp_create_nhwindow(type)1534 lisp_create_nhwindow(type)
1535      int type;
1536 {
1537   winid id = get_unique_winid();
1538 
1539   switch (type)
1540     {
1541     case NHW_MESSAGE:
1542       lisp_cmd ("create-message-window",);
1543       break;
1544     case NHW_MAP:
1545       lisp_cmd ("create-map-window",);
1546       break;
1547     case NHW_STATUS:
1548       lisp_cmd ("create-status-window",);
1549       break;
1550     case NHW_TEXT:
1551       lisp_cmd ("create-text-window", lisp_int (id));
1552       break;
1553     case NHW_MENU:
1554       if (!inven_win_created)
1555 	{
1556 	  lisp_cmd ("create-inventory-window", lisp_int (id));
1557 	  inven_win_created = TRUE;
1558 	}
1559       else
1560 	lisp_cmd ("create-menu-window", lisp_int (id));
1561       break;
1562     default:
1563       impossible ("Unknown window type: %d", type);
1564     };
1565 
1566   return id;
1567 }
1568 
1569 void
lisp_clear_nhwindow(window)1570 lisp_clear_nhwindow(window)
1571      winid window;
1572 {
1573   if (window == WIN_MESSAGE)
1574     lisp_cmd ("clear-message",);
1575   else if (window == WIN_MAP)
1576     lisp_cmd ("clear-map",);
1577   else
1578     /* are other window types ever cleared? */
1579     lisp_cmd ("error", lisp_string("clearing unknown winid"));
1580 }
1581 
1582 void
lisp_display_nhwindow(window,blocking)1583 lisp_display_nhwindow(window, blocking)
1584      winid window;
1585      boolean blocking;
1586 {
1587   /* don't send display messages for anything but menus */
1588   char *dummy;
1589   if (window != WIN_MESSAGE && window != WIN_STATUS && window != WIN_MAP)
1590     {
1591       lisp_cmd ("display-menu", lisp_int (window));
1592       read_string ("dummy", &dummy);
1593       free (dummy);
1594     }
1595   else if (blocking)
1596     {
1597       if (window == WIN_MESSAGE)
1598 	{
1599 	  /* blocking on the message window happens only at the end of
1600 	     the game */
1601 	  lisp_cmd ("end",);
1602 	}
1603       else
1604 	{
1605 	  lisp_cmd ("block",);
1606 	  read_string ("dummy", &dummy);
1607 	  free (dummy);
1608 	}
1609     }
1610   else if (window == WIN_STATUS)
1611     {
1612       /* initial window setup hack here :) */
1613       lisp_cmd ("restore-window-configuration",);
1614     }
1615 }
1616 
1617 void
lisp_destroy_nhwindow(window)1618 lisp_destroy_nhwindow(window)
1619      winid window;
1620 {
1621   if ((window != WIN_STATUS)
1622       && (window != WIN_MESSAGE)
1623       && (window != WIN_MAP))
1624     {
1625       lisp_cmd ("destroy-menu", lisp_int (window));
1626       return_winid (window);
1627     }
1628 }
1629 
1630 void
lisp_update_inventory()1631 lisp_update_inventory()
1632 {
1633   lisp_cmd ("update-inventory",);
1634 }
1635 
1636 int
lisp_doprev_message()1637 lisp_doprev_message()
1638 {
1639   lisp_cmd ("doprev-message",);
1640   return 0;
1641 }
1642 
1643 void
lisp_nhbell()1644 lisp_nhbell()
1645 {
1646   lisp_cmd ("nhbell",);
1647 }
1648 
1649 /* Can be an empty call says window.doc. */
1650 void
lisp_mark_synch()1651 lisp_mark_synch()
1652 {
1653   /* lisp_cmd ("mark-sync",); */
1654 }
1655 
1656 void
lisp_wait_synch()1657 lisp_wait_synch()
1658 {
1659   lisp_cmd ("wait-synch",);
1660 }
1661 
1662 /* Since nethack will never be suspended, we need not worry about this
1663    function. */
1664 void
lisp_resume_nhwindows()1665 lisp_resume_nhwindows()
1666 {
1667   return;
1668 }
1669 
1670 /* Since nethack will never be suspended, we need not worry about this
1671    function. */
1672 void
lisp_suspend_nhwindows(str)1673 lisp_suspend_nhwindows(str)
1674      const char *str;
1675 {
1676   return;
1677 }
1678 
1679 /* All keys are defined in emacs, so number_pad makes no sense. */
1680 void
lisp_number_pad(state)1681 lisp_number_pad(state)
1682      int state;
1683 {
1684   return;
1685 }
1686 
1687 void
lisp_init_nhwindows(argcp,argv)1688 lisp_init_nhwindows(argcp,argv)
1689      int* argcp;
1690      char** argv;
1691 {
1692   int i;
1693 
1694   /* Print each command-line option, constructing a list of strings */
1695   lisp_cmd ("init-nhwindows",
1696 	    for (i=0; i<*argcp; i++)
1697 	      lisp_string (argv[i]));
1698 
1699   /* FIXME: doesn't remove the arguments parsed, as specified in the
1700      api doc. */
1701 
1702   /* Setup certain flags lisp clients need */
1703   iflags.num_pad = FALSE;
1704 #ifdef EXP_ON_BOTL		/* we are going to lose if Nethack is
1705 				   compiled without this option -rcy */
1706   flags.showexp = TRUE;
1707 #endif
1708   flags.time = TRUE;
1709 
1710   /* inform nethack that the windows have been initialized. */
1711   iflags.window_inited = TRUE;
1712 
1713   init_winid_list();
1714 }
1715 
1716 void
lisp_exit_nhwindows(str)1717 lisp_exit_nhwindows (str)
1718      const char *str;
1719 {
1720   lisp_cmd ("exit-nhwindows ", lisp_string (str));
1721 }
1722 
1723 void
lisp_delay_output()1724 lisp_delay_output()
1725 {
1726   char *dummy;
1727   lisp_cmd ("delay-output",);
1728   read_string ("dummy", &dummy);
1729   free (dummy);
1730 }
1731 
1732 void
lisp_getlin(question,input)1733 lisp_getlin(question, input)
1734      const char *question;
1735      char *input;
1736 {
1737   char *tmp;
1738   lisp_cmd ("getlin", lisp_string (question));
1739   read_string ("string", &tmp);
1740   /* FIXME: potential buffer overflow. */
1741   strcpy (input, tmp);
1742 }
1743 
1744 int
lisp_get_ext_cmd()1745 lisp_get_ext_cmd()
1746 {
1747 /*    int cmd; */
1748 /*    int i; */
1749 
1750 /*    printf ("(nethack-api-get-ext-cmd '("); */
1751 
1752 /*    for (i=0; extcmdlist[i].ef_txt != (char *)0; i++) { */
1753 /*      printf ("(\"%s\" . %d)", extcmdlist[i].ef_txt, i); */
1754 /*    } */
1755 /*    printf ("))\n"); */
1756 
1757 /*    scanf ("%d", &cmd); */
1758 
1759   /* This is set when the user chooses an extended command. */
1760   return extended_cmd_id;
1761 }
1762 
1763 void
1764 #ifdef FILE_AREAS
lisp_display_file(farea,fname,complain)1765 lisp_display_file(farea, fname, complain)
1766      const char *farea;
1767 #else
1768 lisp_display_file(fname, complain)
1769 #endif
1770      const char *fname;
1771      boolean complain;
1772 {
1773   lisp_cmd ("display-file",
1774 	    lisp_string (fname);
1775 	    complain ? lisp_t : lisp_nil);;
1776 }
1777 
1778 char
lisp_yn_function(ques,choices,def)1779 lisp_yn_function(ques, choices, def)
1780      const char *ques;
1781      const char *choices;
1782      char def;
1783 {
1784   int answer;
1785 
1786   /* Some questions have special functions. */
1787   if (!strncmp (ques, "In what direction", 17)
1788       || !strncmp (ques, "Talk to whom? (in what direction)", 33))
1789     {
1790       char *dir;
1791       lisp_cmd ("ask-direction",
1792 		lisp_string (ques));
1793       read_string ("direction", &dir);
1794       if (!strcmp (dir, "n"))
1795 	answer = 'k';
1796       else if (!strcmp (dir, "s"))
1797 	answer = 'j';
1798       else if (!strcmp (dir, "e"))
1799 	answer = 'l';
1800       else if (!strcmp (dir, "w"))
1801 	answer = 'h';
1802       else if (!strcmp (dir, "ne"))
1803 	answer = 'u';
1804       else if (!strcmp (dir, "nw"))
1805 	answer = 'y';
1806       else if (!strcmp (dir, "se"))
1807 	answer = 'n';
1808       else if (!strcmp (dir, "sw"))
1809 	answer = 'b';
1810       else if (!strcmp (dir, "up"))
1811 	answer = '<';
1812       else if (!strcmp (dir, "down"))
1813 	answer = '>';
1814       else if (!strcmp (dir, "self"))
1815 	answer = '.';
1816       else
1817 	{
1818 	  if (def == '\0')
1819 	    answer = 0x20;		/* space */
1820 	  else
1821 	    answer = def;
1822 	}
1823 
1824       free (dir);
1825     }
1826   else
1827     {
1828       lisp_cmd ("yn-function",
1829 		lisp_string (ques);
1830 		lisp_string (choices);
1831 		lisp_int (def));
1832       read_int ("number", &answer);
1833     }
1834 
1835   return (char)answer;
1836 }
1837 
1838 #ifdef POSITIONBAR
1839 void
lisp_update_positionbar(features)1840 lisp_update_positionbar(features)
1841      char *features;
1842 {
1843   lisp_cmd ("update-positionbar", lisp_string (features));
1844 }
1845 #endif
1846 
1847 #define zap_color(n)  zapcolors[n]
1848 #define cmap_color(n) defsyms[n].color
1849 #define obj_color(n)  objects[n].oc_color
1850 #define mon_color(n)  mons[n].mcolor
1851 #define invis_color(n) NO_COLOR
1852 #define pet_color(n)  mons[n].mcolor
1853 #define warn_color(n) def_warnsyms[n].color
1854 
1855 void
lisp_print_glyph(window,x,y,glyph)1856 lisp_print_glyph(window, x, y, glyph)
1857     winid window;
1858     xchar x, y;
1859     int glyph;
1860 {
1861     glyph_t ch;
1862     int	    color;
1863     unsigned special;
1864 
1865     /* map glyph to character and color */
1866     mapglyph(glyph, &ch, &color, &special, x, y);
1867 
1868     /* If the user doesn't want to highlight the pet, then we erase
1869        the PET bit from special. In the lisp code the special argument
1870        will be 'pet if the glyph is a pet and will be printed in the
1871        color of the pet highlight face. But we don't want this if the
1872        user hasn't turned on hilite_pet. */
1873     if (!iflags.hilite_pet)
1874       {
1875 	special &= ~MG_PET;
1876       }
1877 
1878     if (window == WIN_MAP)
1879       {
1880 	/* The last parameter, special, is optional. It is only
1881 	   present when the tile is special in some way. FIXME: This
1882 	   duplicate code is a bit gross. */
1883 	if (special)
1884 	  {
1885 	    lisp_cmd ("print-glyph",
1886 		      lisp_int (x);
1887 		      lisp_int (y);
1888 		      lisp_int (color);
1889 		      lisp_int (glyph);
1890 		      lisp_int (glyph2tile[glyph]);
1891 		      lisp_int (ch);
1892 		      lisp_literal (special_glyph_to_string (special)););
1893 	  }
1894 	else
1895 	  {
1896 	    lisp_cmd ("print-glyph",
1897 		      lisp_int (x);
1898 		      lisp_int (y);
1899 		      lisp_int (color);
1900 		      lisp_int (glyph);
1901 		      lisp_int (glyph2tile[glyph]);
1902 		      lisp_int (ch););
1903 	  }
1904       }
1905     else
1906       lisp_cmd ("error",
1907 		lisp_string ("lisp_print_glyph bad window");
1908 		lisp_int (window));
1909 }
1910 
1911 #ifdef CLIPPING
1912 void
lisp_cliparound(x,y)1913 lisp_cliparound(x, y)
1914      int x;
1915      int y;
1916 {
1917   /* as far as I can tell, the x and y values here are exactly the
1918      ones given by the next lisp_curs call, so its redundant
1919      information -rcy */
1920 
1921   /*   lisp_cmd ("cliparound", lisp_int (x); lisp_int (y)); */
1922 }
1923 #endif
1924 
lisp_start_screen()1925 void lisp_start_screen() { return; } /* called from setftty() in unixtty.c */
lisp_end_screen()1926 void lisp_end_screen() {return; }    /* called from settty() in unixtty.c */
1927 
1928 static void
get_death_text(buf)1929 get_death_text (buf)
1930      char buf[BUFSZ];
1931 {
1932 
1933 }
1934 
1935 void
lisp_outrip(window,how)1936 lisp_outrip(window, how)
1937      winid window;
1938      int how;
1939 {
1940   lisp_cmd ("outrip",
1941 	    lisp_int (window);
1942 	    lisp_string (plname);
1943 	    lisp_long (u.ugold);
1944 	    lisp_string ("Died while trying to finish nethack-el."));
1945 }
1946