root/trunk/src/cmd4.c

Revision 1065, 96.3 kB (checked in by takkaria, 2 weeks ago)

(#609) Stop pretending that the player can set the glyphs of terrain features that mimic others by not presenting them on the knowledge screen.

  • Property svn:eol-style set to native
Line 
1 /*
2  * File: cmd4.c
3  * Purpose: Various kinds of browsing functions.
4  *
5  * Copyright (c) 1997-2007 Robert A. Koeneke, James E. Wilson, Ben Harrison,
6  * Eytan Zweig, Andrew Doull, Pete Mack.
7  *
8  * This work is free software; you can redistribute it and/or modify it
9  * under the terms of either:
10  *
11  * a) the GNU General Public License as published by the Free Software
12  *    Foundation, version 2, or
13  *
14  * b) the "Angband licence":
15  *    This software may be copied and distributed for educational, research,
16  *    and not for profit purposes provided that this copyright and statement
17  *    are included in all such copies.  Other copyrights may also apply.
18  */
19 #include "angband.h"
20 #include "object/tvalsval.h"
21 #include "cmds.h"
22 #include "option.h"
23 #include "ui.h"
24 #include "externs.h"
25 #include "ui-menu.h"
26
27
28 static void do_cmd_pref_file_hack(long row);
29
30
31
32 /* Flag value for missing array entry */
33 #define MISSING -17
34
35 #define APP_MACRO       101
36 #define ASK_MACRO       103
37 #define DEL_MACRO       104
38 #define NEW_MACRO       105
39 #define APP_KEYMAP      106
40 #define ASK_KEYMAP      107
41 #define DEL_KEYMAP      108
42 #define NEW_KEYMAP      109
43 #define ENTER_ACT       110
44 #define LOAD_PREF       111
45 #define DUMP_MON        112
46 #define DUMP_OBJ        113
47 #define DUMP_FEAT       114
48 #define DUMP_FLAV       115
49 #define MOD_MON         116
50 #define MOD_OBJ         117
51 #define MOD_FEAT        118
52 #define MOD_FLAV        119
53 #define DUMP_COL        120
54 #define MOD_COL         121
55 #define RESET_VIS       122
56
57
58 #define INFO_SCREENS 2 /* Number of screens in character info mode */
59
60
61
62 typedef struct
63 {
64         int maxnum;          /* Maximum possible item count for this class */
65         bool easy_know;      /* Items don't need to be IDed to recognize membership */
66
67         const char *(*name)(int gid);               /* Name of this group */
68
69         /* Compare, in group and display order (optional if already sorted) */
70         int (*gcomp)(const void *, const void *);   /* Compares gids of two oids */
71         int (*group)(int oid);                      /* Returns gid for an oid */
72
73         /* Summary function for the "object" information. */
74         void (*summary)(int gid, const int *object_list, int n, int top, int row, int col);
75
76 } group_funcs;
77
78 typedef struct
79 {
80         /* Displays an entry at specified location, including kill-count and graphics */
81         void (*display_member)(int col, int row, bool cursor, int oid);
82
83         void (*lore)(int oid);       /* Displays lore for an oid */
84
85
86         /* Required only for objects with modifiable display attributes */
87         /* Unknown 'flavors' return flavor attributes */
88         char *(*xchar)(int oid);     /* Get character attr for OID (by address) */
89         byte *(*xattr)(int oid);     /* Get color attr for OID (by address) */
90
91         const char *(*xtra_prompt)(int oid);  /* Returns optional extra prompt */
92         void (*xtra_act)(char ch, int oid);   /* Handles optional extra actions */
93
94         bool is_visual;                       /* Does this kind have visual editing? */
95
96 } member_funcs;
97
98
99 /* Helper class for generating joins */
100 typedef struct join
101 {
102                 int oid;
103                 int gid;
104 } join_t;
105
106 /* A default group-by */
107 static join_t *default_join;
108 #if 0
109 static int default_join_cmp(const void *a, const void *b)
110 {
111                 join_t *ja = &default_join[*(int*)a];
112                 join_t *jb = &default_join[*(int*)b];
113                 int c = ja->gid - jb->gid;
114                 if (c) return c;
115                 return ja->oid - jb->oid;
116 }
117 #endif
118 static int default_group(int oid) { return default_join[oid].gid; }
119
120
121 static int *obj_group_order;
122
123 /*
124  * Description of each monster group.
125  */
126 static struct
127 {
128         cptr chars;
129         cptr name;
130 } monster_group[] =
131 {
132         { (cptr)-1,     "Uniques" },
133         { "A",          "Angels" },
134         { "a",          "Ants" },
135         { "b",          "Bats" },
136         { "B",          "Birds" },
137         { "C",          "Canines" },
138         { "c",          "Centipedes" },
139         { "uU",         "Demons" },
140         { "dD",         "Dragons" },
141         { "vE",         "Elementals/Vortices" },
142         { "e",          "Eyes/Beholders" },
143         { "f",          "Felines" },
144         { "G",          "Ghosts" },
145         { "OP",         "Giants/Ogres" },
146         { "g",          "Golems" },
147         { "H",          "Harpies/Hybrids" },
148         { "h",          "Hominids (Elves, Dwarves)" },
149         { "M",          "Hydras" },
150         { "i",          "Icky Things" },
151         { "lFI",        "Insects" },
152         { "j",          "Jellies" },
153         { "K",          "Killer Beetles" },
154         { "k",          "Kobolds" },
155         { "L",          "Lichs" },
156         { "tp",         "Men" },
157         { ".$?!_",      "Mimics" },
158         { "m",          "Molds" },
159         { ",",          "Mushroom Patches" },
160         { "n",          "Nagas" },
161         { "o",          "Orcs" },
162         { "q",          "Quadrupeds" },
163         { "Q",          "Quylthulgs" },
164         { "R",          "Reptiles/Amphibians" },
165         { "r",          "Rodents" },
166         { "S",          "Scorpions/Spiders" },
167         { "s",          "Skeletons/Drujs" },
168         { "J",          "Snakes" },
169         { "T",          "Trolls" },
170         { "V",          "Vampires" },
171         { "W",          "Wights/Wraiths" },
172         { "w",          "Worms/Worm Masses" },
173         { "X",          "Xorns/Xarens" },
174         { "Y",          "Yeti" },
175         { "Z",          "Zephyr Hounds" },
176         { "z",          "Zombies" },
177         { NULL,         NULL }
178 };
179
180 /*
181  * Description of each feature group.
182  */
183 const char *feature_group_text[] =
184 {
185         "Floors",
186         "Traps",
187         "Doors",
188         "Stairs",
189         "Walls",
190         "Streamers",
191         "Obstructions",
192         "Stores",
193         "Other",
194         NULL
195 };
196
197
198
199 /* Useful method declarations */
200 static void display_visual_list(int col, int row, int height, int width,
201                                 byte attr_top, char char_left);
202
203 static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr,
204                                 int height, int width,
205                                 byte *attr_top_ptr, char *char_left_ptr,
206                                 byte *cur_attr_ptr, char *cur_char_ptr,
207                                 int col, int row, int *delay);
208
209 static void place_visual_list_cursor(int col, int row, byte a,
210                                 byte c, byte attr_top, byte char_left);
211
212 static void dump_pref_file(void (*dump)(ang_file *), const char *title, int row);
213
214 /*
215  * Clipboard variables for copy&paste in visual mode
216  */
217 static byte attr_idx = 0;
218 static byte char_idx = 0;
219
220 /*
221  * Return a specific ordering for the features
222  */
223 static int feat_order(int feat)
224 {
225         feature_type *f_ptr = &f_info[feat];
226
227         switch (f_ptr->d_char)
228         {
229                 case '.':                               return 0;
230                 case '^':                               return 1;
231                 case '\'': case '+':    return 2;
232                 case '<': case '>':             return 3;
233                 case '#':                               return 4;
234                 case '*': case '%' :    return 5;
235                 case ';': case ':' :    return 6;
236
237                 default:
238                 {
239                         return 8;
240                 }
241         }
242 }
243
244
245 /* Emit a 'graphical' symbol and a padding character if appropriate */
246 static void big_pad(int col, int row, byte a, byte c)
247 {
248         Term_putch(col, row, a, c);
249         if (!use_bigtile) return;
250
251         if (a & 0x80)
252                 Term_putch(col + 1, row, 255, -1);
253         else
254                 Term_putch(col + 1, row, 1, ' ');
255 }
256
257 /* Return the actual width of a symbol */
258 static int actual_width(int width)
259 {
260 #ifdef UNANGBAND
261         if (use_trptile) width *= 3;
262         else if (use_dbltile) width *= 2;
263 #endif
264
265         if (use_bigtile) width *= 2;
266
267         return width;
268 }
269
270 /* Return the actual height of a symbol */
271 static int actual_height(int height)
272 {
273 #ifdef UNANGBAND
274         if (use_trptile) height = height * 3 / 2;
275         else if (use_dbltile) height *= 2;
276 #endif
277
278         if (use_bigtile) height *= 2;
279
280         return height;
281 }
282
283
284 /* From an actual width, return the logical width */
285 static int logical_width(int width)
286 {
287         int divider = 1;
288
289 #ifdef UNANGBAND
290         if (use_trptile) divider = 3;
291         else if (use_dbltile) divider = 2;
292 #endif
293
294         if (use_bigtile) divider *= 2;
295
296         return width / divider;
297 }
298
299 /* From an actual height, return the logical height */
300 static int logical_height(int height)
301 {
302         int divider = 1;
303
304 #ifdef UNANGBAND
305         if (use_trptile)
306         {
307                 height *= 2;
308                 divider = 3;
309         }
310         else if (use_dbltile) divider = 2;
311 #endif
312
313         if (use_bigtile) divider *= 2;
314
315         return height / divider;
316 }
317
318
319 static void display_group_member(menu_type *menu, int oid,
320                                                 bool cursor, int row, int col, int wid)
321 {
322         const member_funcs *o_funcs = menu->menu_data;
323         byte attr = curs_attrs[CURS_KNOWN][cursor == oid];
324
325         (void)wid;
326
327         /* Print the interesting part */
328         o_funcs->display_member(col, row, cursor, oid);
329
330 #if 0 /* Debugging code */
331         c_put_str(attr, format("%d", oid), row, 60);
332 #endif
333
334         /* Do visual mode */
335         if (o_funcs->is_visual && o_funcs->xattr)
336         {
337                 char c = *o_funcs->xchar(oid);
338                 byte a = *o_funcs->xattr(oid);
339
340                 c_put_str(attr, format((c & 0x80) ? "%02x/%02x" : "%02x/%d", a, c), row, 60);
341         }
342 }
343
344 static const char *recall_prompt(int oid)
345 {
346         (void)oid;
347         return ", 'r' to recall";
348 }
349
350 #define swap(a, b) (swapspace = (void*)(a)), ((a) = (b)), ((b) = swapspace)
351
352 /*
353  * Interactive group by.
354  * Recognises inscriptions, graphical symbols, lore
355  */
356 static void display_knowledge(const char *title, int *obj_list, int o_count,
357                                 group_funcs g_funcs, member_funcs o_funcs,
358                                 const char *otherfields)
359 {
360         /* maximum number of groups to display */
361         int max_group = g_funcs.maxnum < o_count ? g_funcs.maxnum : o_count ;
362
363         /* This could (should?) be (void **) */
364         int *g_list, *g_offset;
365
366         const char **g_names;
367
368         int g_name_len = 8;  /* group name length, minumum is 8 */
369
370         int grp_cnt = 0; /* total number groups */
371
372         int g_cur = 0, grp_old = -1; /* group list positions */
373         int o_cur = 0;                                  /* object list positions */
374         int g_o_count = 0;                               /* object count for group */
375         int oid = -1;                           /* object identifiers */
376
377         region title_area = { 0, 0, 0, 4 };
378         region group_region = { 0, 6, MISSING, -2 };
379         region object_region = { MISSING, 6, 0, -2 };
380
381         /* display state variables */
382         bool visual_list = FALSE;
383         byte attr_top = 0;
384         char char_left = 0;
385
386         int delay = 0;
387
388         menu_type group_menu;
389         menu_type object_menu;
390         menu_iter object_iter;
391
392         /* Panel state */
393         /* These are swapped in parallel whenever the actively browsing " */
394         /* changes */
395         int *active_cursor = &g_cur, *inactive_cursor = &o_cur;
396         menu_type *active_menu = &group_menu, *inactive_menu = &object_menu;
397         int panel = 0;
398
399         void *swapspace;
400         bool do_swap = FALSE;
401
402         bool flag = FALSE;
403         bool redraw = TRUE;
404
405         int browser_rows;
406         int wid, hgt;
407         int i;
408         int prev_g = -1;
409
410         int omode = rogue_like_commands;
411
412
413         /* Get size */
414         Term_get_size(&wid, &hgt);
415         browser_rows = hgt - 8;
416
417         /* Disable the roguelike commands for the duration */
418         rogue_like_commands = FALSE;
419
420
421
422         /* Do the group by. ang_sort only works on (void **) */
423         /* Maybe should make this a precondition? */
424         if (g_funcs.gcomp)
425                 qsort(obj_list, o_count, sizeof(*obj_list), g_funcs.gcomp);
426
427
428         /* Sort everything into group order */
429         g_list = C_ZNEW(max_group + 1, int);
430         g_offset = C_ZNEW(max_group + 1, int);
431
432         for (i = 0; i < o_count; i++)
433         {
434                 if (prev_g != g_funcs.group(obj_list[i]))
435                 {
436                         prev_g = g_funcs.group(obj_list[i]);
437                         g_offset[grp_cnt] = i;
438                         g_list[grp_cnt++] = prev_g;
439                 }
440         }
441
442         g_offset[grp_cnt] = o_count;
443         g_list[grp_cnt] = -1;
444
445
446         /* The compact set of group names, in display order */
447         g_names = C_ZNEW(grp_cnt, const char **);
448
449         for (i = 0; i < grp_cnt; i++)
450         {
451                 int len;
452                 g_names[i] = g_funcs.name(g_list[i]);
453                 len = strlen(g_names[i]);
454                 if (len > g_name_len) g_name_len = len;
455         }
456
457         /* Reasonable max group name len */
458         if (g_name_len >= 20) g_name_len = 20;
459
460         object_region.col = g_name_len + 3;
461         group_region.width = g_name_len;
462
463
464         /* Leave room for the group summary information */
465         if (g_funcs.summary) object_region.page_rows = -3;
466
467
468         /* Set up the two menus */
469         WIPE(&group_menu, menu_type);
470         group_menu.count = grp_cnt;
471         group_menu.cmd_keys = "\n\r6\x8C"/* Ignore these as menu commands */
472         group_menu.menu_data = g_names;
473
474         WIPE(&object_menu, menu_type);
475         object_menu.menu_data = &o_funcs;
476         WIPE(&object_iter, object_iter);
477         object_iter.display_row = display_group_member;
478
479         o_funcs.is_visual = FALSE;
480
481         menu_init(&group_menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_STRINGS), &group_region);
482         menu_init(&object_menu, MN_SKIN_SCROLL, &object_iter, &object_region);
483
484
485         /* This is the event loop for a multi-region panel */
486         /* Panels are -- text panels, two menus, and visual browser */
487         /* with "pop-up menu" for lore */
488         while ((!flag) && (grp_cnt))
489         {
490                 ui_event_data ke, ke0;
491
492                 if (redraw)
493                 {
494                         /* Print the title bits */
495                         region_erase(&title_area);
496                         prt(format("Knowledge - %s", title), 2, 0);
497                         prt("Group", 4, 0);
498                         prt("Name", 4, g_name_len + 3);
499
500                         if (otherfields)
501                                 prt(otherfields, 4, 55);
502
503
504                         /* Print dividers: horizontal and vertical */
505                         for (i = 0; i < 79; i++)
506                                 Term_putch(i, 5, TERM_WHITE, '=');
507
508                         for (i = 0; i < browser_rows; i++)
509                                 Term_putch(g_name_len + 1, 6 + i, TERM_WHITE, '|');
510
511
512                         /* Reset redraw flag */
513                         redraw = FALSE;
514                 }
515
516                 if (g_cur != grp_old)
517                 {
518                         grp_old = g_cur;
519                         o_cur = 0;
520                         g_o_count = g_offset[g_cur+1] - g_offset[g_cur];
521                         menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count);
522                         group_menu.cursor = g_cur;
523                         object_menu.cursor = 0;
524                 }
525
526                 /* HACK ... */
527                 if (!visual_list)
528                 {
529                         /* ... The object menu may be browsing the entire group... */
530                         o_funcs.is_visual = FALSE;
531                         menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count);
532                         object_menu.cursor = o_cur;
533                 }
534                 else
535                 {
536                         /* ... or just a single element in the group. */
537                         o_funcs.is_visual = TRUE;
538                         menu_set_filter(&object_menu, obj_list + o_cur + g_offset[g_cur], 1);
539                         object_menu.cursor = 0;
540                 }
541
542                 oid = obj_list[g_offset[g_cur]+o_cur];
543
544                 /* Print prompt */
545                 {
546                         const char *pedit = (!o_funcs.xattr) ? "" :
547                                         (!(attr_idx|char_idx) ? ", 'c' to copy" : ", 'c', 'p' to paste");
548                         const char *xtra = o_funcs.xtra_prompt ? o_funcs.xtra_prompt(oid) : "";
549                         const char *pvs = "";
550
551                         if (visual_list) pvs = ", ENTER to accept";
552                         else if (o_funcs.xattr) pvs = ", 'v' for visuals";
553
554
555
556                         prt(format("<dir>%s%s%s, ESC", pvs, pedit, xtra), hgt - 1, 0);
557                 }
558
559                 if (do_swap)
560                 {
561                         do_swap = FALSE;
562                         swap(active_menu, inactive_menu);
563                         swap(active_cursor, inactive_cursor);
564                         panel = 1 - panel;
565                 }
566
567                 if (g_funcs.summary && !visual_list)
568                 {
569                         g_funcs.summary(g_cur, obj_list, g_o_count, g_offset[g_cur],
570                                         object_menu.boundary.row + object_menu.boundary.page_rows,
571                                         object_region.col);
572                 }
573
574                 menu_refresh(inactive_menu);
575                 menu_refresh(active_menu);
576
577                 handle_stuff();
578
579                 if (visual_list)
580                 {
581                         display_visual_list(g_name_len + 3, 7, browser_rows-1,
582                                                      wid - (g_name_len + 3), attr_top, char_left);
583                         place_visual_list_cursor(g_name_len + 3, 7, *o_funcs.xattr(oid),
584                                                                                 *o_funcs.xchar(oid), attr_top, char_left);
585                 }
586
587                 if (delay)
588                 {
589                         /* Force screen update */
590                         Term_fresh();
591
592                         /* Delay */
593                         Term_xtra(TERM_XTRA_DELAY, delay);
594
595                         delay = 0;
596                 }
597
598                 ke = inkey_ex();
599
600                 /* Do visual mode command if needed */
601                 if (o_funcs.xattr && o_funcs.xchar &&
602                                         visual_mode_command(ke, &visual_list,
603                                         browser_rows-1, wid - (g_name_len + 3),
604                                         &attr_top, &char_left,
605                                         o_funcs.xattr(oid), o_funcs.xchar(oid),
606                                         g_name_len + 3, 7, &delay))
607                 {
608                         continue;
609                 }
610
611                 if (ke.type == EVT_MOUSE)
612                 {
613                         /* Change active panels */
614                         if (region_inside(&inactive_menu->boundary, &ke))
615                         {
616                                 swap(active_menu, inactive_menu);
617                                 swap(active_cursor, inactive_cursor);
618                                 panel = 1-panel;
619                         }
620                 }
621
622                 ke0 = run_event_loop(&active_menu->target, &ke);
623                 if (ke0.type != EVT_AGAIN) ke = ke0;
624
625                 switch (ke.type)
626                 {
627                         case EVT_KBRD:
628                         {
629                                 break;
630                         }
631
632                         case ESCAPE:
633                         {
634                                 flag = TRUE;
635                                 continue;
636                         }
637
638                         case EVT_SELECT:
639                         {
640                                 if (panel == 1 && oid >= 0 && o_cur == active_menu->cursor)
641                                 {
642                                         o_funcs.lore(oid);
643                                         redraw = TRUE;
644                                 }
645                         }
646
647                         case EVT_MOVE:
648                         {
649                                 *active_cursor = active_menu->cursor;
650                                 continue;
651                         }
652
653                         case EVT_BACK:
654                         {
655                                 if (panel == 1)
656                                         do_swap = TRUE;
657                         }
658
659                         /* XXX Handle EVT_RESIZE */
660
661                         default:
662                         {
663                                 continue;
664                         }
665                 }
666
667                 switch (ke.key)
668                 {
669                         case ESCAPE:
670                         {
671                                 flag = TRUE;
672                                 break;
673                         }
674
675                         case 'R':
676                         case 'r':
677                         {
678                                 /* Recall on screen */
679                                 if (oid >= 0)
680                                         o_funcs.lore(oid);
681
682                                 redraw = TRUE;
683                                 break;
684                         }
685
686                         /* Jump down a page */
687                         case '3':
688                         {
689                                 *active_cursor += browser_rows;
690
691                                 if (g_cur >= grp_cnt) g_cur = grp_cnt - 1;
692                                 else if (o_cur >= g_o_count) o_cur = g_o_count - 1;
693
694                                 break;
695                         }
696
697                         /* Jump up a page */
698                         case '9':
699                         {
700                                 *active_cursor -= browser_rows;
701
702                                 if (*active_cursor < 0) *active_cursor = 0;
703
704                                 break;
705                         }
706
707                         default:
708                         {
709                                 int d = target_dir(ke.key);
710
711                                 /* Handle key-driven motion between panels */
712                                 if (ddx[d] && ((ddx[d] < 0) == (panel == 1)))
713                                 {
714                                         /* Silly hack -- diagonal arithmetic */
715                                         *inactive_cursor += ddy[d];
716                                         if (*inactive_cursor < 0) *inactive_cursor = 0;
717                                         else if (g_cur >= grp_cnt) g_cur = grp_cnt - 1;
718                                         else if (o_cur >= g_o_count) o_cur = g_o_count - 1;
719                                         do_swap = TRUE;
720                                 }
721                                 else if (o_funcs.xtra_act)
722                                 {
723                                         o_funcs.xtra_act(ke.key, oid);
724                                 }
725
726                                 break;
727                         }
728                 }
729         }
730
731         /* Restore roguelike option */
732         rogue_like_commands = omode;
733
734         /* Prompt */
735         if (!grp_cnt)
736                 prt(format("No %s known.", title), 15, 0);
737
738         FREE(g_names);
739         FREE(g_offset);
740         FREE(g_list);
741 }
742
743 /*
744  * Display visuals.
745  */
746 static void display_visual_list(int col, int row, int height, int width, byte attr_top, char char_left)
747 {
748         int i, j;
749
750         /* Clear the display lines */
751         for (i = 0; i < height; i++)
752                         Term_erase(col, row + i, width);
753
754         width = logical_width(width);
755
756         /* Display lines until done */
757         for (i = 0; i < height; i++)
758         {
759                 /* Display columns until done */
760                 for (j = 0; j < width; j++)
761                 {
762                         byte a;
763                         char c;
764                         int x = col + actual_width(j);
765                         int y = row + actual_width(i);
766                         int ia, ic;
767
768                         ia = attr_top + i;
769                         ic = char_left + j;
770
771                         a = (byte)ia;
772                         c = (char)ic;
773
774                         /* Display symbol */
775                         big_pad(x, y, a, c);
776                 }
777         }
778 }
779
780
781 /*
782  * Place the cursor at the collect position for visual mode
783  */
784 static void place_visual_list_cursor(int col, int row, byte a, byte c, byte attr_top, byte char_left)
785 {
786         int i = a - attr_top;
787         int j = c - char_left;
788
789         int x = col + actual_width(j);
790         int y = row + actual_height(i);
791
792         /* Place the cursor */
793         Term_gotoxy(x, y);
794 }
795
796
797 /*
798  *  Do visual mode command -- Change symbols
799  */
800 static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr,
801                                 int height, int width,
802                                 byte *attr_top_ptr, char *char_left_ptr,
803                                 byte *cur_attr_ptr, char *cur_char_ptr,
804                                 int col, int row, int *delay)
805 {
806         static byte attr_old = 0;
807         static char char_old = 0;
808
809         int frame_left = logical_width(10);
810         int frame_right = logical_width(10);
811         int frame_top = logical_height(4);
812         int frame_bottom = logical_height(4);
813
814         switch (ke.key)
815         {
816                 case ESCAPE:
817                 {
818             &