1 /* NetHack 3.6	allmain.c	$NHDT-Date: 1555552624 2019/04/18 01:57:04 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.100 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 /* various code that was replicated in *main.c */
7 
8 #include "hack.h"
9 #include <ctype.h>
10 
11 #ifndef NO_SIGNAL
12 #include <signal.h>
13 #endif
14 
15 #ifdef POSITIONBAR
16 STATIC_DCL void NDECL(do_positionbar);
17 #endif
18 STATIC_DCL void FDECL(regen_hp, (int));
19 STATIC_DCL void FDECL(interrupt_multi, (const char *));
20 STATIC_DCL void FDECL(debug_fields, (const char *));
21 
22 void
moveloop(resuming)23 moveloop(resuming)
24 boolean resuming;
25 {
26 #if defined(MICRO) || defined(WIN32)
27     char ch;
28     int abort_lev;
29 #endif
30     int moveamt = 0, wtcap = 0, change = 0;
31     boolean monscanmove = FALSE;
32 
33     /* Note:  these initializers don't do anything except guarantee that
34             we're linked properly.
35     */
36     decl_init();
37     monst_init();
38     objects_init();
39 
40     /* if a save file created in normal mode is now being restored in
41        explore mode, treat it as normal restore followed by 'X' command
42        to use up the save file and require confirmation for explore mode */
43     if (resuming && iflags.deferred_X)
44         (void) enter_explore_mode();
45 
46     /* side-effects from the real world */
47     flags.moonphase = phase_of_the_moon();
48     if (flags.moonphase == FULL_MOON) {
49         You("are lucky!  Full moon tonight.");
50         change_luck(1);
51     } else if (flags.moonphase == NEW_MOON) {
52         pline("Be careful!  New moon tonight.");
53     }
54     flags.friday13 = friday_13th();
55     if (flags.friday13) {
56         pline("Watch out!  Bad things can happen on Friday the 13th.");
57         change_luck(-1);
58     }
59 
60     if (!resuming) { /* new game */
61         context.rndencode = rnd(9000);
62         set_wear((struct obj *) 0); /* for side-effects of starting gear */
63         (void) pickup(1);      /* autopickup at initial location */
64     }
65     context.botlx = TRUE; /* for STATUS_HILITES */
66     update_inventory(); /* for perm_invent */
67     if (resuming) { /* restoring old game */
68         read_engr_at(u.ux, u.uy); /* subset of pickup() */
69     }
70 
71     (void) encumber_msg(); /* in case they auto-picked up something */
72     if (defer_see_monsters) {
73         defer_see_monsters = FALSE;
74         see_monsters();
75     }
76     initrack();
77 
78     u.uz0.dlevel = u.uz.dlevel;
79     youmonst.movement = NORMAL_SPEED; /* give the hero some movement points */
80     context.move = 0;
81 
82     program_state.in_moveloop = 1;
83     for (;;) {
84 #ifdef SAFERHANGUP
85         if (program_state.done_hup)
86             end_of_input();
87 #endif
88         get_nh_event();
89 #ifdef POSITIONBAR
90         do_positionbar();
91 #endif
92 
93         if (context.move) {
94             /* actual time passed */
95             youmonst.movement -= NORMAL_SPEED;
96 
97             do { /* hero can't move this turn loop */
98                 wtcap = encumber_msg();
99 
100                 context.mon_moving = TRUE;
101                 do {
102                     monscanmove = movemon();
103                     if (youmonst.movement >= NORMAL_SPEED)
104                         break; /* it's now your turn */
105                 } while (monscanmove);
106                 context.mon_moving = FALSE;
107 
108                 if (!monscanmove && youmonst.movement < NORMAL_SPEED) {
109                     /* both hero and monsters are out of steam this round */
110                     struct monst *mtmp;
111 
112                     /* set up for a new turn */
113                     mcalcdistress(); /* adjust monsters' trap, blind, etc */
114 
115                     /* reallocate movement rations to monsters; don't need
116                        to skip dead monsters here because they will have
117                        been purged at end of their previous round of moving */
118                     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
119                         mtmp->movement += mcalcmove(mtmp);
120 
121                     /* occasionally add another monster; since this takes
122                        place after movement has been allotted, the new
123                        monster effectively loses its first turn */
124                     if (!rn2(u.uevent.udemigod ? 25
125                              : (depth(&u.uz) > depth(&stronghold_level)) ? 50
126                                : 70))
127                         (void) makemon((struct permonst *) 0, 0, 0,
128                                        NO_MM_FLAGS);
129 
130                     /* calculate how much time passed. */
131                     if (u.usteed && u.umoved) {
132                         /* your speed doesn't augment steed's speed */
133                         moveamt = mcalcmove(u.usteed);
134                     } else {
135                         moveamt = youmonst.data->mmove;
136 
137                         if (Very_fast) { /* speed boots, potion, or spell */
138                             /* gain a free action on 2/3 of turns */
139                             if (rn2(3) != 0)
140                                 moveamt += NORMAL_SPEED;
141                         } else if (Fast) { /* intrinsic */
142                             /* gain a free action on 1/3 of turns */
143                             if (rn2(3) == 0)
144                                 moveamt += NORMAL_SPEED;
145                         }
146                     }
147 
148                     switch (wtcap) {
149                     case UNENCUMBERED:
150                         break;
151                     case SLT_ENCUMBER:
152                         moveamt -= (moveamt / 4);
153                         break;
154                     case MOD_ENCUMBER:
155                         moveamt -= (moveamt / 2);
156                         break;
157                     case HVY_ENCUMBER:
158                         moveamt -= ((moveamt * 3) / 4);
159                         break;
160                     case EXT_ENCUMBER:
161                         moveamt -= ((moveamt * 7) / 8);
162                         break;
163                     default:
164                         break;
165                     }
166 
167                     youmonst.movement += moveamt;
168                     if (youmonst.movement < 0)
169                         youmonst.movement = 0;
170                     settrack();
171 
172                     monstermoves++;
173                     moves++;
174 
175                     /********************************/
176                     /* once-per-turn things go here */
177                     /********************************/
178 
179                     if (Glib)
180                         glibr();
181                     nh_timeout();
182                     run_regions();
183 
184                     if (u.ublesscnt)
185                         u.ublesscnt--;
186                     if (flags.time && !context.run)
187                         iflags.time_botl = TRUE;
188 
189                     /* One possible result of prayer is healing.  Whether or
190                      * not you get healed depends on your current hit points.
191                      * If you are allowed to regenerate during the prayer,
192                      * the end-of-prayer calculation messes up on this.
193                      * Another possible result is rehumanization, which
194                      * requires that encumbrance and movement rate be
195                      * recalculated.
196                      */
197                     if (u.uinvulnerable) {
198                         /* for the moment at least, you're in tiptop shape */
199                         wtcap = UNENCUMBERED;
200                     } else if (!Upolyd ? (u.uhp < u.uhpmax)
201                                        : (u.mh < u.mhmax
202                                           || youmonst.data->mlet == S_EEL)) {
203                         /* maybe heal */
204                         regen_hp(wtcap);
205                     }
206 
207                     /* moving around while encumbered is hard work */
208                     if (wtcap > MOD_ENCUMBER && u.umoved) {
209                         if (!(wtcap < EXT_ENCUMBER ? moves % 30
210                                                    : moves % 10)) {
211                             if (Upolyd && u.mh > 1) {
212                                 u.mh--;
213                                 context.botl = TRUE;
214                             } else if (!Upolyd && u.uhp > 1) {
215                                 u.uhp--;
216                                 context.botl = TRUE;
217                             } else {
218                                 You("pass out from exertion!");
219                                 exercise(A_CON, FALSE);
220                                 fall_asleep(-10, FALSE);
221                             }
222                         }
223                     }
224 
225                     if (u.uen < u.uenmax
226                         && ((wtcap < MOD_ENCUMBER
227                              && (!(moves % ((MAXULEV + 8 - u.ulevel)
228                                             * (Role_if(PM_WIZARD) ? 3 : 4)
229                                             / 6)))) || Energy_regeneration)) {
230                         u.uen += rn1(
231                             (int) (ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1, 1);
232                         if (u.uen > u.uenmax)
233                             u.uen = u.uenmax;
234                         context.botl = TRUE;
235                         if (u.uen == u.uenmax)
236                             interrupt_multi("You feel full of energy.");
237                     }
238 
239                     if (!u.uinvulnerable) {
240                         if (Teleportation && !rn2(85)) {
241                             xchar old_ux = u.ux, old_uy = u.uy;
242 
243                             tele();
244                             if (u.ux != old_ux || u.uy != old_uy) {
245                                 if (!next_to_u()) {
246                                     check_leash(old_ux, old_uy);
247                                 }
248                                 /* clear doagain keystrokes */
249                                 pushch(0);
250                                 savech(0);
251                             }
252                         }
253                         /* delayed change may not be valid anymore */
254                         if ((change == 1 && !Polymorph)
255                             || (change == 2 && u.ulycn == NON_PM))
256                             change = 0;
257                         if (Polymorph && !rn2(100))
258                             change = 1;
259                         else if (u.ulycn >= LOW_PM && !Upolyd
260                                  && !rn2(80 - (20 * night())))
261                             change = 2;
262                         if (change && !Unchanging) {
263                             if (multi >= 0) {
264                                 stop_occupation();
265                                 if (change == 1)
266                                     polyself(0);
267                                 else
268                                     you_were();
269                                 change = 0;
270                             }
271                         }
272                     }
273 
274                     if (Searching && multi >= 0)
275                         (void) dosearch0(1);
276                     if (Warning)
277                         warnreveal();
278                     mkot_trap_warn();
279                     dosounds();
280                     do_storms();
281                     gethungry();
282                     age_spells();
283                     exerchk();
284                     invault();
285                     if (u.uhave.amulet)
286                         amulet();
287                     if (!rn2(40 + (int) (ACURR(A_DEX) * 3)))
288                         u_wipe_engr(rnd(3));
289                     if (u.uevent.udemigod && !u.uinvulnerable) {
290                         if (u.udg_cnt)
291                             u.udg_cnt--;
292                         if (!u.udg_cnt) {
293                             intervene();
294                             u.udg_cnt = rn1(200, 50);
295                         }
296                     }
297                     restore_attrib();
298                     /* underwater and waterlevel vision are done here */
299                     if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz))
300                         movebubbles();
301                     else if (Is_firelevel(&u.uz))
302                         fumaroles();
303                     else if (Underwater)
304                         under_water(0);
305                     /* vision while buried done here */
306                     else if (u.uburied)
307                         under_ground(0);
308 
309                     /* when immobile, count is in turns */
310                     if (multi < 0) {
311                         if (++multi == 0) { /* finished yet? */
312                             unmul((char *) 0);
313                             /* if unmul caused a level change, take it now */
314                             if (u.utotype)
315                                 deferred_goto();
316                         }
317                     }
318                 }
319             } while (youmonst.movement < NORMAL_SPEED); /* hero can't move */
320 
321             /******************************************/
322             /* once-per-hero-took-time things go here */
323             /******************************************/
324 
325 #ifdef STATUS_HILITES
326             if (iflags.hilite_delta)
327                 status_eval_next_unhilite();
328 #endif
329             if (context.bypasses)
330                 clear_bypasses();
331             if ((u.uhave.amulet || Clairvoyant) && !In_endgame(&u.uz)
332                 && !BClairvoyant && !(moves % 15) && !rn2(2))
333                 do_vicinity_map((struct obj *) 0);
334             if (u.utrap && u.utraptype == TT_LAVA)
335                 sink_into_lava();
336             /* when/if hero escapes from lava, he can't just stay there */
337             else if (!u.umoved)
338                 (void) pooleffects(FALSE);
339 
340         } /* actual time passed */
341 
342         /****************************************/
343         /* once-per-player-input things go here */
344         /****************************************/
345 
346         clear_splitobjs();
347         find_ac();
348         if (!context.mv || Blind) {
349             /* redo monsters if hallu or wearing a helm of telepathy */
350             if (Hallucination) { /* update screen randomly */
351                 see_monsters();
352                 see_objects();
353                 see_traps();
354                 if (u.uswallow)
355                     swallowed(0);
356             } else if (Unblind_telepat) {
357                 see_monsters();
358             } else if (Warning || Warn_of_mon)
359                 see_monsters();
360 
361             if (vision_full_recalc)
362                 vision_recalc(0); /* vision! */
363         }
364         if (context.botl || context.botlx) {
365             bot();
366             curs_on_u();
367         } else if (iflags.time_botl) {
368             timebot();
369             curs_on_u();
370         }
371 
372         context.move = 1;
373 
374         if (multi >= 0 && occupation) {
375 #if defined(MICRO) || defined(WIN32)
376             abort_lev = 0;
377             if (kbhit()) {
378                 if ((ch = pgetchar()) == ABORT)
379                     abort_lev++;
380                 else
381                     pushch(ch);
382             }
383             if (!abort_lev && (*occupation)() == 0)
384 #else
385             if ((*occupation)() == 0)
386 #endif
387                 occupation = 0;
388             if (
389 #if defined(MICRO) || defined(WIN32)
390                 abort_lev ||
391 #endif
392                 monster_nearby()) {
393                 stop_occupation();
394                 reset_eat();
395             }
396 #if defined(MICRO) || defined(WIN32)
397             if (!(++occtime % 7))
398                 display_nhwindow(WIN_MAP, FALSE);
399 #endif
400             continue;
401         }
402 
403         if (iflags.sanity_check || iflags.debug_fuzzer)
404             sanity_check();
405 
406 #ifdef CLIPPING
407         /* just before rhack */
408         cliparound(u.ux, u.uy);
409 #endif
410 
411         u.umoved = FALSE;
412 
413         if (multi > 0) {
414             lookaround();
415             if (!multi) {
416                 /* lookaround may clear multi */
417                 context.move = 0;
418                 if (flags.time)
419                     context.botl = TRUE;
420                 continue;
421             }
422             if (context.mv) {
423                 if (multi < COLNO && !--multi)
424                     context.travel = context.travel1 = context.mv =
425                         context.run = 0;
426                 domove();
427             } else {
428                 --multi;
429                 rhack(save_cm);
430             }
431         } else if (multi == 0) {
432 #ifdef MAIL
433             ckmailstatus();
434 #endif
435             rhack((char *) 0);
436         }
437         if (u.utotype)       /* change dungeon level */
438             deferred_goto(); /* after rhack() */
439         /* !context.move here: multiple movement command stopped */
440         else if (flags.time && (!context.move || !context.mv))
441             context.botl = TRUE;
442 
443         if (vision_full_recalc)
444             vision_recalc(0); /* vision! */
445         /* when running in non-tport mode, this gets done through domove() */
446         if ((!context.run || flags.runmode == RUN_TPORT)
447             && (multi && (!context.travel ? !(multi % 7) : !(moves % 7L)))) {
448             if (flags.time && context.run)
449                 context.botl = TRUE;
450             /* [should this be flush_screen() instead?] */
451             display_nhwindow(WIN_MAP, FALSE);
452         }
453     }
454 }
455 
456 /* maybe recover some lost health (or lose some when an eel out of water) */
457 STATIC_OVL void
regen_hp(wtcap)458 regen_hp(wtcap)
459 int wtcap;
460 {
461     int heal = 0;
462     boolean reached_full = FALSE,
463             encumbrance_ok = (wtcap < MOD_ENCUMBER || !u.umoved);
464 
465     if (Upolyd) {
466         if (u.mh < 1) { /* shouldn't happen... */
467             rehumanize();
468         } else if (youmonst.data->mlet == S_EEL
469                    && !is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz)) {
470             /* eel out of water loses hp, similar to monster eels;
471                as hp gets lower, rate of further loss slows down */
472             if (u.mh > 1 && !Regeneration && rn2(u.mh) > rn2(8)
473                 && (!Half_physical_damage || !(moves % 2L)))
474                 heal = -1;
475         } else if (u.mh < u.mhmax) {
476             if (Regeneration || (encumbrance_ok && !(moves % 20L)))
477                 heal = 1;
478         }
479         if (heal) {
480             context.botl = TRUE;
481             u.mh += heal;
482             reached_full = (u.mh == u.mhmax);
483         }
484 
485     /* !Upolyd */
486     } else {
487         /* [when this code was in-line within moveloop(), there was
488            no !Upolyd check here, so poly'd hero recovered lost u.uhp
489            once u.mh reached u.mhmax; that may have been convenient
490            for the player, but it didn't make sense for gameplay...] */
491         if (u.uhp < u.uhpmax && (encumbrance_ok || Regeneration)) {
492             if (u.ulevel > 9) {
493                 if (!(moves % 3L)) {
494                     int Con = (int) ACURR(A_CON);
495 
496                     if (Con <= 12) {
497                         heal = 1;
498                     } else {
499                         heal = rnd(Con);
500                         if (heal > u.ulevel - 9)
501                             heal = u.ulevel - 9;
502                     }
503                 }
504             } else { /* u.ulevel <= 9 */
505                 if (!(moves % (long) ((MAXULEV + 12) / (u.ulevel + 2) + 1)))
506                     heal = 1;
507             }
508             if (Regeneration && !heal)
509                 heal = 1;
510 
511             if (heal) {
512                 context.botl = TRUE;
513                 u.uhp += heal;
514                 if (u.uhp > u.uhpmax)
515                     u.uhp = u.uhpmax;
516                 /* stop voluntary multi-turn activity if now fully healed */
517                 reached_full = (u.uhp == u.uhpmax);
518             }
519         }
520     }
521 
522     if (reached_full)
523         interrupt_multi("You are in full health.");
524 }
525 
526 void
stop_occupation()527 stop_occupation()
528 {
529     if (occupation) {
530         if (!maybe_finished_meal(TRUE))
531             You("stop %s.", occtxt);
532         occupation = 0;
533         context.botl = TRUE; /* in case u.uhs changed */
534         nomul(0);
535         pushch(0);
536     } else if (multi >= 0) {
537         nomul(0);
538     }
539 }
540 
541 void
display_gamewindows()542 display_gamewindows()
543 {
544     WIN_MESSAGE = create_nhwindow(NHW_MESSAGE);
545     if (VIA_WINDOWPORT()) {
546         status_initialize(0);
547     } else {
548         WIN_STATUS = create_nhwindow(NHW_STATUS);
549     }
550     WIN_MAP = create_nhwindow(NHW_MAP);
551     WIN_INVEN = create_nhwindow(NHW_MENU);
552     /* in case of early quit where WIN_INVEN could be destroyed before
553        ever having been used, use it here to pacify the Qt interface */
554     start_menu(WIN_INVEN), end_menu(WIN_INVEN, (char *) 0);
555 
556 #ifdef MAC
557     /* This _is_ the right place for this - maybe we will
558      * have to split display_gamewindows into create_gamewindows
559      * and show_gamewindows to get rid of this ifdef...
560      */
561     if (!strcmp(windowprocs.name, "mac"))
562         SanePositions();
563 #endif
564 
565     /*
566      * The mac port is not DEPENDENT on the order of these
567      * displays, but it looks a lot better this way...
568      */
569 #ifndef STATUS_HILITES
570     display_nhwindow(WIN_STATUS, FALSE);
571 #endif
572     display_nhwindow(WIN_MESSAGE, FALSE);
573     clear_glyph_buffer();
574     display_nhwindow(WIN_MAP, FALSE);
575 }
576 
577 void
newgame()578 newgame()
579 {
580     int i;
581 
582 #ifdef MFLOPPY
583     gameDiskPrompt();
584 #endif
585 
586     context.botlx = TRUE;
587     context.ident = 1;
588     context.stethoscope_move = -1L;
589     context.warnlevel = 1;
590     context.next_attrib_check = 600L; /* arbitrary first setting */
591     context.tribute.enabled = TRUE;   /* turn on 3.6 tributes    */
592     context.tribute.tributesz = sizeof(struct tribute_info);
593 
594     for (i = LOW_PM; i < NUMMONS; i++)
595         mvitals[i].mvflags = mons[i].geno & G_NOCORPSE;
596 
597     init_objects(); /* must be before u_init() */
598 
599     flags.pantheon = -1; /* role_init() will reset this */
600     role_init();         /* must be before init_dungeons(), u_init(),
601                           * and init_artifacts() */
602 
603     init_dungeons();  /* must be before u_init() to avoid rndmonst()
604                        * creating odd monsters for any tins and eggs
605                        * in hero's initial inventory */
606     init_artifacts(); /* before u_init() in case $WIZKIT specifies
607                        * any artifacts */
608     u_init();
609 
610 #ifndef NO_SIGNAL
611     (void) signal(SIGINT, (SIG_RET_TYPE) done1);
612 #endif
613 #ifdef NEWS
614     if (iflags.news)
615         display_file(NEWS, FALSE);
616 #endif
617     load_qtlist();          /* load up the quest text info */
618     /* quest_init();  --  Now part of role_init() */
619 
620     mklev();
621     u_on_upstairs();
622     if (wizard)
623         obj_delivery(FALSE); /* finish wizkit */
624     vision_reset();          /* set up internals for level (after mklev) */
625     check_special_room(FALSE);
626 
627     if (MON_AT(u.ux, u.uy))
628         mnexto(m_at(u.ux, u.uy));
629     (void) makedog();
630     docrt();
631 
632     if (flags.legacy) {
633         flush_screen(1);
634         com_pager(1);
635     }
636 
637     urealtime.realtime = 0L;
638     urealtime.start_timing = getnow();
639 #ifdef INSURANCE
640     save_currentstate();
641 #endif
642     program_state.something_worth_saving++; /* useful data now exists */
643 
644     /* Success! */
645     welcome(TRUE);
646     return;
647 }
648 
649 /* show "welcome [back] to nethack" message at program startup */
650 void
welcome(new_game)651 welcome(new_game)
652 boolean new_game; /* false => restoring an old game */
653 {
654     char buf[BUFSZ];
655     boolean currentgend = Upolyd ? u.mfemale : flags.female;
656 
657     /* skip "welcome back" if restoring a doomed character */
658     if (!new_game && Upolyd && ugenocided()) {
659         /* death via self-genocide is pending */
660         pline("You're back, but you still feel %s inside.", udeadinside());
661         return;
662     }
663 
664     /*
665      * The "welcome back" message always describes your innate form
666      * even when polymorphed or wearing a helm of opposite alignment.
667      * Alignment is shown unconditionally for new games; for restores
668      * it's only shown if it has changed from its original value.
669      * Sex is shown for new games except when it is redundant; for
670      * restores it's only shown if different from its original value.
671      */
672     *buf = '\0';
673     if (new_game || u.ualignbase[A_ORIGINAL] != u.ualignbase[A_CURRENT])
674         Sprintf(eos(buf), " %s", align_str(u.ualignbase[A_ORIGINAL]));
675     if (!urole.name.f
676         && (new_game
677                 ? (urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
678                 : currentgend != flags.initgend))
679         Sprintf(eos(buf), " %s", genders[currentgend].adj);
680 
681     pline(new_game ? "%s %s, welcome to NetHack!  You are a%s %s %s."
682                    : "%s %s, the%s %s %s, welcome back to NetHack!",
683           Hello((struct monst *) 0), plname, buf, urace.adj,
684           (currentgend && urole.name.f) ? urole.name.f : urole.name.m);
685 }
686 
687 #ifdef POSITIONBAR
688 STATIC_DCL void
do_positionbar()689 do_positionbar()
690 {
691     static char pbar[COLNO];
692     char *p;
693 
694     p = pbar;
695     /* up stairway */
696     if (upstair.sx
697         && (glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph)
698                 == S_upstair
699             || glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph)
700                    == S_upladder)) {
701         *p++ = '<';
702         *p++ = upstair.sx;
703     }
704     if (sstairs.sx
705         && (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph)
706                 == S_upstair
707             || glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph)
708                    == S_upladder)) {
709         *p++ = '<';
710         *p++ = sstairs.sx;
711     }
712 
713     /* down stairway */
714     if (dnstair.sx
715         && (glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph)
716                 == S_dnstair
717             || glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph)
718                    == S_dnladder)) {
719         *p++ = '>';
720         *p++ = dnstair.sx;
721     }
722     if (sstairs.sx
723         && (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph)
724                 == S_dnstair
725             || glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph)
726                    == S_dnladder)) {
727         *p++ = '>';
728         *p++ = sstairs.sx;
729     }
730 
731     /* hero location */
732     if (u.ux) {
733         *p++ = '@';
734         *p++ = u.ux;
735     }
736     /* fence post */
737     *p = 0;
738 
739     update_positionbar(pbar);
740 }
741 #endif
742 
743 STATIC_DCL void
interrupt_multi(msg)744 interrupt_multi(msg)
745 const char *msg;
746 {
747     if (multi > 0 && !context.travel && !context.run) {
748         nomul(0);
749         if (flags.verbose && msg)
750             Norep("%s", msg);
751     }
752 }
753 
754 /*
755  * Argument processing helpers - for xxmain() to share
756  * and call.
757  *
758  * These should return TRUE if the argument matched,
759  * whether the processing of the argument was
760  * successful or not.
761  *
762  * Most of these do their thing, then after returning
763  * to xxmain(), the code exits without starting a game.
764  *
765  */
766 
767 static struct early_opt earlyopts[] = {
768     {ARG_DEBUG, "debug", 5, TRUE},
769     {ARG_VERSION, "version", 4, TRUE},
770     {ARG_SHOWPATHS, "showpaths", 9, FALSE},
771 #ifdef WIN32
772     {ARG_WINDOWS, "windows", 4, TRUE},
773 #endif
774 };
775 
776 #ifdef WIN32
777 extern int FDECL(windows_early_options, (const char *));
778 #endif
779 
780 /*
781  * Returns:
782  *    0 = no match
783  *    1 = found and skip past this argument
784  *    2 = found and trigger immediate exit
785  */
786 
787 int
argcheck(argc,argv,e_arg)788 argcheck(argc, argv, e_arg)
789 int argc;
790 char *argv[];
791 enum earlyarg e_arg;
792 {
793     int i, idx;
794     boolean match = FALSE;
795     char *userea = (char *)0;
796     const char *dashdash = "";
797 
798     for (idx = 0; idx < SIZE(earlyopts); idx++) {
799         if (earlyopts[idx].e == e_arg)
800             break;
801     }
802     if ((idx >= SIZE(earlyopts)) || (argc <= 1))
803             return FALSE;
804 
805     for (i = 0; i < argc; ++i) {
806         if (argv[i][0] != '-')
807             continue;
808         if (argv[i][1] == '-') {
809             userea = &argv[i][2];
810             dashdash = "-";
811         } else {
812             userea = &argv[i][1];
813         }
814         match = match_optname(userea, earlyopts[idx].name,
815                               earlyopts[idx].minlength,
816                               earlyopts[idx].valallowed);
817         if (match) break;
818     }
819 
820     if (match) {
821         const char *extended_opt = index(userea, ':');
822 
823         if (!extended_opt)
824             extended_opt = index(userea, '=');
825         switch(e_arg) {
826         case ARG_DEBUG:
827             if (extended_opt) {
828                 extended_opt++;
829                 debug_fields(extended_opt);
830             }
831             return 1;
832         case ARG_VERSION: {
833             boolean insert_into_pastebuf = FALSE;
834 
835             if (extended_opt) {
836                 extended_opt++;
837                 if (match_optname(extended_opt, "paste", 5, FALSE)) {
838                     insert_into_pastebuf = TRUE;
839                 } else {
840                     raw_printf(
841                    "-%sversion can only be extended with -%sversion:paste.\n",
842                                dashdash, dashdash);
843                     return TRUE;
844                 }
845             }
846             early_version_info(insert_into_pastebuf);
847             return 2;
848         }
849         case ARG_SHOWPATHS: {
850             return 2;
851         }
852 #ifdef WIN32
853         case ARG_WINDOWS: {
854             if (extended_opt) {
855                 extended_opt++;
856                 return windows_early_options(extended_opt);
857             }
858         }
859 #endif
860         default:
861             break;
862         }
863     };
864     return FALSE;
865 }
866 
867 /*
868  * These are internal controls to aid developers with
869  * testing and debugging particular aspects of the code.
870  * They are not player options and the only place they
871  * are documented is right here. No gameplay is altered.
872  *
873  * test             - test whether this parser is working
874  * ttystatus        - TTY:
875  * immediateflips   - WIN32: turn off display performance
876  *                    optimization so that display output
877  *                    can be debugged without buffering.
878  */
879 STATIC_OVL void
debug_fields(opts)880 debug_fields(opts)
881 const char *opts;
882 {
883     char *op;
884     boolean negated = FALSE;
885 
886     while ((op = index(opts, ',')) != 0) {
887         *op++ = 0;
888         /* recurse */
889         debug_fields(op);
890     }
891     if (strlen(opts) > BUFSZ / 2)
892         return;
893 
894 
895     /* strip leading and trailing white space */
896     while (isspace((uchar) *opts))
897         opts++;
898     op = eos((char *) opts);
899     while (--op >= opts && isspace((uchar) *op))
900         *op = '\0';
901 
902     if (!*opts) {
903         /* empty */
904         return;
905     }
906     while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
907         if (*opts == '!')
908             opts++;
909         else
910             opts += 2;
911         negated = !negated;
912     }
913     if (match_optname(opts, "test", 4, FALSE))
914         iflags.debug.test = negated ? FALSE : TRUE;
915 #ifdef TTY_GRAPHICS
916     if (match_optname(opts, "ttystatus", 9, FALSE))
917         iflags.debug.ttystatus = negated ? FALSE : TRUE;
918 #endif
919 #ifdef WIN32
920     if (match_optname(opts, "immediateflips", 14, FALSE))
921         iflags.debug.immediateflips = negated ? FALSE : TRUE;
922 #endif
923     return;
924 }
925 /*allmain.c*/
926