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