1 /* SCCS Id: @(#)allmain.c 3.4 2003/04/02 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /* various code that was replicated in *main.c */
6
7 #include "hack.h"
8
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12
13 #ifdef POSITIONBAR
14 STATIC_DCL void NDECL(do_positionbar);
15 #endif
16 STATIC_DCL void FDECL(interrupt_multi, (const char *,int,int));
17
18 static int prev_hp_notify;
19
20 char *
hpnotify_format_str(char * str)21 hpnotify_format_str(char *str)
22 {
23 static char buf[128];
24 char *f, *p, *end;
25 int ispercent = 0;
26
27 buf[0] = '\0';
28
29 if (!str) return NULL;
30
31 f = str;
32 p = buf;
33 end = buf + sizeof(buf) - 10;
34
35 while (*f) {
36 if (ispercent) {
37 switch (*f) {
38 case 'a':
39 snprintf (p, end + 1 - p, "%ld", (long)abs(uhp()-prev_hp_notify));
40 while (*p != '\0')
41 p++;
42 break;
43 case 'c':
44 snprintf (p, end + 1 - p, "%c", (prev_hp_notify > uhp() ? '-' : '+'));
45 p++;
46 break;
47 case 'm':
48 snprintf (p, end + 1 - p, "%ld", (long)uhpmax());
49 while (*p != '\0')
50 p++;
51 break;
52 case 'H':
53 if (uhp() == uhpmax()) {
54 snprintf (p, end + 1 - p, "%s", "max");
55 } else {
56 snprintf (p, end + 1 - p, "%ld", (long)uhp());
57 }
58 while (*p != '\0')
59 p++;
60 break;
61 case 'h':
62 snprintf (p, end + 1 - p, "%ld", (long)uhp());
63 while (*p != '\0')
64 p++;
65 break;
66 default:
67 *p = *f;
68 if (p < end)
69 p++;
70 }
71 ispercent = 0;
72 } else {
73 if (*f == '%')
74 ispercent = 1;
75 else {
76 *p = *f;
77 if (p < end)
78 p++;
79 }
80 }
81 f++;
82 }
83 *p = '\0';
84
85 return buf;
86 }
87
88 /* Elven players cannot regenerate if in direct contact with cold
89 * iron, and vampiric players cannot if in direct contact with silver.
90 * Make an exception for your quest artifact, though -- currently,
91 * the only worn iron quest artifacts are weapons, a helm, and an
92 * amulet, and the only worn silver quest artifact is a shield.
93 * It is also not possible to hold artifacts as secondary weapons.
94 */
95 boolean
can_regenerate()96 can_regenerate()
97 {
98 if (is_elf(youmonst.data)) {
99 if (uwep && is_iron(uwep) &&
100 !is_quest_artifact(uwep) && !uarmg) return 0;
101 #ifdef TOURIST
102 if (uarm && is_iron(uarm) && !uarmu) return 0;
103 if (uarmu && is_iron(uarmu)) return 0;
104 if (uarmc && is_iron(uarmc) && !uarmu && !uarm) return 0;
105 #else
106 if (uarm && is_iron(uarm)) return 0;
107 if (uarmc && is_iron(uarmc) && !uarm) return 0;
108 #endif
109 if (uarmh && is_iron(uarmh) &&
110 !is_quest_artifact(uarmh)) return 0;
111 if (uarms && is_iron(uarms) && !uarmg) return 0;
112 if (uarmg && is_iron(uarmg)) return 0;
113 if (uarmf && is_iron(uarmf)) return 0;
114 if (uleft && is_iron(uleft)) return 0;
115 if (uright && is_iron(uright)) return 0;
116 #ifdef TOURIST
117 if (uamul && is_iron(uamul) &&
118 !is_quest_artifact(uamul) && !uarmu && !uarm) return 0;
119 #else
120 if (uamul && is_iron(uamul) &&
121 !is_quest_artifact(uamul) && !uarm) return 0;
122 #endif
123 if (ublindf && is_iron(ublindf)) return 0;
124 if (uchain && is_iron(uchain)) return 0;
125 if (uswapwep && is_iron(uswapwep) && u.twoweap) return 0;
126 } else if (is_vampiric(youmonst.data)) {
127 if (uwep && is_silver(uwep) && !uarmg) return 0;
128 if (uarms && is_silver(uarms) &&
129 !is_quest_artifact(uarms) && !uarmg) return 0;
130 if (uleft && is_silver(uleft)) return 0;
131 if (uright && is_silver(uright)) return 0;
132 }
133 return 1;
134 }
135
136 void
moveloop()137 moveloop()
138 {
139 #if defined(MICRO) || defined(WIN32)
140 char ch;
141 int abort_lev;
142 #endif
143 int moveamt = 0, wtcap = 0, change = 0;
144 boolean didmove = FALSE, monscanmove = FALSE;
145 /* don't make it obvious when monsters will start speeding up */
146 int monclock;
147 int timeout_start = rnd(10000)+25000;
148 int clock_base = 80000L-timeout_start;
149 int past_clock;
150 /* for keeping track of Elbereth and correctstatus line display */
151 int was_on_elbereth = 0;
152 int is_on_elbereth = 0;
153 boolean can_regen = can_regenerate();
154
155 flags.moonphase = phase_of_the_moon();
156 if(flags.moonphase == FULL_MOON) {
157 You("are lucky! Full moon tonight.");
158 change_luck(1);
159 } else if(flags.moonphase == NEW_MOON) {
160 pline("Be careful! New moon tonight.");
161 }
162 flags.friday13 = friday_13th();
163 if (flags.friday13) {
164 pline("Watch out! Bad things can happen on Friday the 13th.");
165 change_luck(-1);
166 }
167
168 initrack();
169
170
171 /* Note: these initializers don't do anything except guarantee that
172 we're linked properly.
173 */
174 decl_init();
175 monst_init();
176 monstr_init(); /* monster strengths */
177 objects_init();
178 dragons_init();
179 doredraw(); /* partial workaround to http://sourceforge.net/apps/trac/unnethack/ticket/2 */
180 shop_selection_init();
181
182 #ifdef WIZARD
183 if (wizard) add_debug_extended_commands();
184 #endif
185
186 (void) encumber_msg(); /* in case they auto-picked up something */
187
188 u.uz0.dlevel = u.uz.dlevel;
189 youmonst.movement = NORMAL_SPEED; /* give the hero some movement points */
190 prev_hp_notify = uhp();
191 #ifdef WHEREIS_FILE
192 touch_whereis();
193 #endif
194
195 for(;;) {
196 get_nh_event();
197 #ifdef POSITIONBAR
198 do_positionbar();
199 #endif
200
201 didmove = flags.move;
202 if(didmove) {
203 /* actual time passed */
204 youmonst.movement -= NORMAL_SPEED;
205
206 do { /* hero can't move this turn loop */
207 wtcap = encumber_msg();
208
209 flags.mon_moving = TRUE;
210 do {
211 monscanmove = movemon();
212 if (youmonst.movement > NORMAL_SPEED)
213 break; /* it's now your turn */
214 } while (monscanmove);
215 flags.mon_moving = FALSE;
216 /* heaven or hell mode: player always has 1 maxhp */
217 if (heaven_or_hell_mode)
218 {
219 u.uhpmax = 1;
220 if (u.uhp > u.uhpmax)
221 u.uhp = u.uhpmax;
222 }
223
224 if (!monscanmove && youmonst.movement < NORMAL_SPEED) {
225 /* both you and the monsters are out of steam this round */
226 /* set up for a new turn */
227 struct monst *mtmp;
228 mcalcdistress(); /* adjust monsters' trap, blind, etc */
229
230 /* reallocate movement rations to monsters */
231 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
232 mtmp->movement += mcalcmove(mtmp);
233
234 /* Vanilla generates a critter every 70-ish turns.
235 * The rate accelerates to every 50 or so below the Castle,
236 * and 'round every 25 turns once you've done the Invocation.
237 *
238 * We will push it even further. Monsters post-Invocation
239 * will almost always appear on the stairs (if present), and
240 * much more frequently; this, along with the extra intervene()
241 * calls, should certainly make it seem like you're wading back
242 * through the teeming hordes.
243 *
244 * Aside from that, a more general clock should be put on things;
245 * after about 30,000 turns, the frequency rate of appearance
246 * and (TODO) difficulty of monsters generated will slowly increase until
247 * it reaches the point it will be at as if you were post-Invocation.
248 *
249 * 80,000 turns should be adequate as a target mark for this effect;
250 * if you haven't ascended in 80,000 turns, you're intentionally
251 * fiddling around somewhere and will certainly be strong enough
252 * to handle anything that comes your way, so this won't be
253 * dropping newbies off the edge of the planet. -- DSR 12/2/07
254 */
255 monclock = 70;
256 if (u.uevent.udemigod) {
257 monclock = 10;
258 } else {
259 if (depth(&u.uz) > depth(&stronghold_level)) {
260 monclock = 50;
261 }
262 past_clock = moves - timeout_start;
263 if (past_clock > 0) {
264 monclock -= past_clock*60/clock_base;
265 }
266 }
267 /* make sure we don't fall off the bottom */
268 if (monclock < 10) { monclock = 10; }
269
270 /* TODO: adj difficulty in makemon */
271 if (!rn2(monclock)) {
272 if (u.uevent.udemigod && xupstair && rn2(10)) {
273 (void) makemon((struct permonst *)0, xupstair, yupstair, MM_ADJACENTOK);
274 } else {
275 (void) makemon((struct permonst *)0, 0, 0, NO_MM_FLAGS);
276 }
277 }
278
279 /* calculate how much time passed. */
280 #ifdef STEED
281 if (u.usteed && u.umoved) {
282 /* your speed doesn't augment steed's speed */
283 moveamt = mcalcmove(u.usteed);
284 } else
285 #endif
286 {
287 moveamt = youmonst.data->mmove;
288
289 if (Very_fast) { /* speed boots or potion */
290 /* average movement is 1.67 times normal */
291 moveamt += NORMAL_SPEED / 2;
292 if (rn2(3) == 0) moveamt += NORMAL_SPEED / 2;
293 } else if (Fast) {
294 /* average movement is 1.33 times normal */
295 if (rn2(3) != 0) moveamt += NORMAL_SPEED / 2;
296 }
297 }
298
299 switch (wtcap) {
300 case UNENCUMBERED: break;
301 case SLT_ENCUMBER: moveamt -= (moveamt / 4); break;
302 case MOD_ENCUMBER: moveamt -= (moveamt / 2); break;
303 case HVY_ENCUMBER: moveamt -= ((moveamt * 3) / 4); break;
304 case EXT_ENCUMBER: moveamt -= ((moveamt * 7) / 8); break;
305 default: break;
306 }
307
308 youmonst.movement += moveamt;
309 if (youmonst.movement < 0) youmonst.movement = 0;
310 settrack();
311
312 monstermoves++;
313 moves++;
314
315 /********************************/
316 /* once-per-turn things go here */
317 /********************************/
318
319 if (flags.bypasses) clear_bypasses();
320 if(Glib) glibr();
321 nh_timeout();
322 run_regions();
323 #ifdef DUNGEON_GROWTH
324 dgn_growths(TRUE, TRUE);
325 #endif
326 if (u.ublesscnt) u.ublesscnt--;
327 if(flags.time && !flags.run)
328 flags.botl = 1;
329
330 /* One possible result of prayer is healing. Whether or
331 * not you get healed depends on your current hit points.
332 * If you are allowed to regenerate during the prayer, the
333 * end-of-prayer calculation messes up on this.
334 * Another possible result is rehumanization, which requires
335 * that encumbrance and movement rate be recalculated.
336 */
337 if (u.uinvulnerable) {
338 /* for the moment at least, you're in tiptop shape */
339 wtcap = UNENCUMBERED;
340 } else if (Upolyd && youmonst.data->mlet == S_EEL && !is_pool(u.ux,u.uy) && !Is_waterlevel(&u.uz)) {
341 if (u.mh > 1) {
342 u.mh--;
343 flags.botl = 1;
344 } else if (u.mh < 1)
345 rehumanize();
346 } else if (Upolyd && u.mh < u.mhmax) {
347 if (u.mh < 1)
348 rehumanize();
349 else if (can_regenerate() && (Regeneration ||
350 (wtcap < MOD_ENCUMBER && !(moves%20)))) {
351 flags.botl = 1;
352 u.mh++;
353 interrupt_multi("Hit points", u.mh, u.mhmax);
354 }
355 } else if (u.uhp < u.uhpmax && can_regenerate() &&
356 (wtcap < MOD_ENCUMBER || !u.umoved || Regeneration)) {
357 if (u.ulevel > 9 && !(moves % 3)) {
358 int heal, Con = (int) ACURR(A_CON);
359
360 if (Con <= 12) {
361 heal = 1;
362 } else {
363 heal = rnd(Con);
364 if (heal > u.ulevel-9) heal = u.ulevel-9;
365 }
366 flags.botl = 1;
367 u.uhp += heal;
368 if(u.uhp > u.uhpmax)
369 u.uhp = u.uhpmax;
370 interrupt_multi("Hit points", u.uhp, u.uhpmax);
371 } else if (Regeneration ||
372 (u.ulevel <= 9 &&
373 !(moves % ((MAXULEV+12) / (u.ulevel+2) + 1)))) {
374 flags.botl = 1;
375 u.uhp++;
376 interrupt_multi("Hit points", u.uhp, u.uhpmax);
377 }
378 }
379
380 /* moving around while encumbered is hard work */
381 if (wtcap > MOD_ENCUMBER && u.umoved) {
382 if(!(wtcap < EXT_ENCUMBER ? moves%30 : moves%10)) {
383 if (Upolyd && u.mh > 1) {
384 u.mh--;
385 } else if (!Upolyd && u.uhp > 1) {
386 u.uhp--;
387 } else {
388 You("pass out from exertion!");
389 exercise(A_CON, FALSE);
390 fall_asleep(-10, FALSE);
391 }
392 }
393 }
394
395 if ((u.uen < u.uenmax) &&
396 ((wtcap < MOD_ENCUMBER &&
397 (!(moves%((MAXULEV + 8 - u.ulevel) *
398 (Role_if(PM_WIZARD) ? 3 : 4) / 6))))
399 || Energy_regeneration)) {
400 u.uen += rn1((int)(ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1,1);
401 if (u.uen > u.uenmax) u.uen = u.uenmax;
402 flags.botl = 1;
403 interrupt_multi("Magic energy", u.uen, u.uenmax);
404 }
405
406 if(!u.uinvulnerable) {
407 if(Teleportation && !rn2(85)) {
408 xchar old_ux = u.ux, old_uy = u.uy;
409 tele();
410 if (u.ux != old_ux || u.uy != old_uy) {
411 if (!next_to_u()) {
412 check_leash(old_ux, old_uy);
413 }
414 #ifdef REDO
415 /* clear doagain keystrokes */
416 pushch(0);
417 savech(0);
418 #endif
419 }
420 }
421 /* delayed change may not be valid anymore */
422 if ((change == 1 && !Polymorph) ||
423 (change == 2 && u.ulycn == NON_PM))
424 change = 0;
425 if(Polymorph && !rn2(100))
426 change = 1;
427 else if (u.ulycn >= LOW_PM && !Upolyd &&
428 !rn2(80 - (20 * night())))
429 change = 2;
430 if (change && !Unchanging) {
431 if (multi >= 0) {
432 if (occupation)
433 stop_occupation();
434 else
435 nomul(0, 0);
436 if (change == 1) polyself(FALSE);
437 else you_were();
438 change = 0;
439 }
440 }
441 }
442
443 if(Searching && multi >= 0) (void) dosearch0(1);
444 dosounds();
445 do_storms();
446 gethungry();
447 age_spells();
448 exerchk();
449 invault();
450 if (u.uhave.amulet) amulet();
451 if (!rn2(40+(int)(ACURR(A_DEX)*3)))
452 u_wipe_engr(rnd(3));
453 if (u.uevent.udemigod && !u.uinvulnerable) {
454 if (u.udg_cnt) u.udg_cnt--;
455 if (!u.udg_cnt) {
456 intervene();
457 u.udg_cnt = rn1(200, (42+Luck));
458 }
459 }
460 restore_attrib();
461 /* underwater and waterlevel vision are done here */
462 if (Is_waterlevel(&u.uz))
463 movebubbles();
464 else if (Underwater)
465 under_water(0);
466 /* vision while buried done here */
467 else if (u.uburied) under_ground(0);
468
469 /* when immobile, count is in turns */
470 if(multi < 0) {
471 if (++multi == 0) { /* finished yet? */
472 unmul((char *)0);
473 /* if unmul caused a level change, take it now */
474 if (u.utotype) deferred_goto();
475 }
476 }
477 }
478 } while (youmonst.movement<NORMAL_SPEED); /* hero can't move loop */
479
480 /******************************************/
481 /* once-per-hero-took-time things go here */
482 /******************************************/
483
484 if (u.utrap && u.utraptype == TT_LAVA) {
485 if (!is_lava(u.ux,u.uy))
486 u.utrap = 0;
487 else if (!u.uinvulnerable) {
488 u.utrap -= 1<<8;
489 if (u.utrap < 1<<8) {
490 killer_format = KILLED_BY;
491 killer = "molten lava";
492 You("sink below the surface and die.");
493 done(DISSOLVED);
494 } else if (!u.umoved) {
495 Norep("You sink deeper into the lava.");
496 u.utrap += rnd(4);
497 }
498 }
499 }
500
501 } /* actual time passed */
502
503 /****************************************/
504 /* once-per-player-input things go here */
505 /****************************************/
506
507 find_ac();
508 if(!flags.mv || Blind) {
509 /* redo monsters if hallu or wearing a helm of telepathy */
510 if (Hallucination) { /* update screen randomly */
511 see_monsters();
512 see_objects();
513 see_traps();
514 if (u.uswallow) swallowed(0);
515 } else if (Unblind_telepat) {
516 see_monsters();
517 } else if (Warning || Warn_of_mon)
518 see_monsters();
519
520 if (vision_full_recalc) vision_recalc(0); /* vision! */
521 }
522
523 #ifdef ELBERETH
524 /* check changes of Elbereth at current player location */
525 is_on_elbereth = sengr_at("Elbereth", u.ux, u.uy);
526 if (was_on_elbereth != is_on_elbereth) {
527 was_on_elbereth = is_on_elbereth;
528 flags.botlx = 1;
529 }
530 #endif
531
532 #ifdef REALTIME_ON_BOTL
533 if (iflags.showrealtime) {
534 /* Update the bottom line if the number of minutes has
535 * changed */
536 time_t currenttime = get_realtime();
537 if (currenttime / 60 != realtime_data.last_displayed_time / 60) {
538 flags.botl = 1;
539 realtime_data.last_displayed_time = currenttime;
540 }
541 }
542 #endif
543
544 if(flags.botl || flags.botlx) bot();
545
546 if (iflags.hp_notify && (prev_hp_notify != uhp())) {
547 pline("%s", hpnotify_format_str(iflags.hp_notify_fmt ? iflags.hp_notify_fmt : "[HP%c%a=%h]"));
548 prev_hp_notify = uhp();
549 }
550
551 if (can_regen != can_regenerate()) {
552 if (!Hallucination){
553 You_feel("%s.", (can_regen) ? "itchy" : "relief");
554 } else {
555 You_feel("%s.", (can_regen) ? (is_elf(youmonst.data) ? "magnetic" :
556 "tarnished") : "like you are no longer failing Organic Chemistry");
557 }
558 can_regen = can_regenerate();
559 }
560
561 flags.move = 1;
562
563 if(multi >= 0 && occupation) {
564 #if defined(MICRO) || defined(WIN32)
565 abort_lev = 0;
566 if (kbhit()) {
567 if ((ch = Getchar()) == ABORT)
568 abort_lev++;
569 # ifdef REDO
570 else
571 pushch(ch);
572 # endif /* REDO */
573 }
574 if (!abort_lev && (*occupation)() == 0)
575 #else
576 if ((*occupation)() == 0)
577 #endif
578 occupation = 0;
579 if(
580 #if defined(MICRO) || defined(WIN32)
581 abort_lev ||
582 #endif
583 monster_nearby()) {
584 stop_occupation();
585 reset_eat();
586 }
587 #if defined(MICRO) || defined(WIN32)
588 if (!(++occtime % 7))
589 display_nhwindow(WIN_MAP, FALSE);
590 #endif
591 continue;
592 }
593
594 if ((u.uhave.amulet || Clairvoyant) &&
595 !In_endgame(&u.uz) && !BClairvoyant &&
596 !(moves % 15) && !rn2(2))
597 do_vicinity_map();
598
599 #ifdef WIZARD
600 if (iflags.sanity_check)
601 sanity_check();
602 #endif
603
604 #ifdef CLIPPING
605 /* just before rhack */
606 cliparound(u.ux, u.uy);
607 #endif
608
609 u.umoved = FALSE;
610
611 if (multi > 0) {
612 lookaround();
613 if (!multi) {
614 /* lookaround may clear multi */
615 flags.move = 0;
616 if (flags.time) flags.botl = 1;
617 continue;
618 }
619 if (flags.mv) {
620 if(multi < COLNO && !--multi)
621 flags.travel = iflags.travel1 = flags.mv = flags.run = 0;
622 domove();
623 } else {
624 --multi;
625 rhack(save_cm);
626 }
627 } else if (multi == 0) {
628 #ifdef MAIL
629 ckmailstatus();
630 maybe_hint();
631 #endif
632 maybe_tutorial();
633 rhack((char *)0);
634 }
635 if (u.utotype) /* change dungeon level */
636 deferred_goto(); /* after rhack() */
637 /* !flags.move here: multiple movement command stopped */
638 else if (flags.time && (!flags.move || !flags.mv))
639 flags.botl = 1;
640
641 if (vision_full_recalc) vision_recalc(0); /* vision! */
642 /* when running in non-tport mode, this gets done through domove() */
643 if ((!flags.run || iflags.runmode == RUN_TPORT) &&
644 (multi && (!flags.travel ? !(multi % 7) : !(moves % 7L)))) {
645 if (flags.time && flags.run) flags.botl = 1;
646 display_nhwindow(WIN_MAP, FALSE);
647 }
648 }
649 }
650
651 void
stop_occupation()652 stop_occupation()
653 {
654 if(occupation) {
655 if (!maybe_finished_meal(TRUE))
656 You("stop %s.", occtxt);
657 occupation = 0;
658 flags.botl = 1; /* in case u.uhs changed */
659 /* fainting stops your occupation, there's no reason to sync.
660 sync_hunger();
661 */
662 #ifdef REDO
663 nomul(0, 0);
664 pushch(0);
665 #endif
666 }
667 }
668
669 void
display_gamewindows()670 display_gamewindows()
671 {
672 WIN_MESSAGE = create_nhwindow(NHW_MESSAGE);
673 WIN_STATUS = create_nhwindow(NHW_STATUS);
674 WIN_MAP = create_nhwindow(NHW_MAP);
675 WIN_INVEN = create_nhwindow(NHW_MENU);
676
677 #ifdef MAC
678 /*
679 * This _is_ the right place for this - maybe we will
680 * have to split display_gamewindows into create_gamewindows
681 * and show_gamewindows to get rid of this ifdef...
682 */
683 if ( ! strcmp ( windowprocs . name , "mac" ) ) {
684 SanePositions ( ) ;
685 }
686 #endif
687
688 /*
689 * The mac port is not DEPENDENT on the order of these
690 * displays, but it looks a lot better this way...
691 */
692 display_nhwindow(WIN_STATUS, FALSE);
693 display_nhwindow(WIN_MESSAGE, FALSE);
694 clear_glyph_buffer();
695 display_nhwindow(WIN_MAP, FALSE);
696 }
697
698 void
newgame()699 newgame()
700 {
701 int i;
702
703 #ifdef MFLOPPY
704 gameDiskPrompt();
705 #endif
706
707 flags.ident = 1;
708
709 for (i = 0; i < NUMMONS; i++)
710 mvitals[i].mvflags = mons[i].geno & G_NOCORPSE;
711
712 init_objects(); /* must be before u_init() */
713
714 flags.pantheon = -1; /* role_init() will reset this */
715 role_init(); /* must be before init_dungeons(), u_init(),
716 * and init_artifacts() */
717
718 init_dungeons(); /* must be before u_init() to avoid rndmonst()
719 * creating odd monsters for any tins and eggs
720 * in hero's initial inventory */
721 init_artifacts(); /* before u_init() in case $WIZKIT specifies
722 * any artifacts */
723 u_init();
724
725 #ifndef NO_SIGNAL
726 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
727 #endif
728 #ifdef NEWS
729 if(iflags.news) display_file_area(NEWS_AREA, NEWS, FALSE);
730 #endif
731 load_qtlist(); /* load up the quest text info */
732 /* quest_init();*/ /* Now part of role_init() */
733
734 mklev();
735 u_on_upstairs();
736 vision_reset(); /* set up internals for level (after mklev) */
737 check_special_room(FALSE);
738
739 flags.botlx = 1;
740
741 /* Move the monster from under you or else
742 * makedog() will fail when it calls makemon().
743 * - ucsfcgl!kneller
744 */
745 if(MON_AT(u.ux, u.uy)) mnexto(m_at(u.ux, u.uy));
746 (void) makedog();
747 docrt();
748 #ifdef CONVICT
749 if (Role_if(PM_CONVICT)) {
750 setworn(mkobj(CHAIN_CLASS, TRUE), W_CHAIN);
751 setworn(mkobj(BALL_CLASS, TRUE), W_BALL);
752 uball->spe = 1;
753 placebc();
754 newsym(u.ux,u.uy);
755 }
756 #endif /* CONVICT */
757
758 if (flags.legacy) {
759 flush_screen(1);
760 #ifdef CONVICT
761 if (Role_if(PM_CONVICT)) {
762 com_pager(199);
763 } else {
764 com_pager(1);
765 }
766 #else
767 com_pager(1);
768 #endif /* CONVICT */
769 }
770
771 #ifdef INSURANCE
772 save_currentstate();
773 #endif
774 program_state.something_worth_saving++; /* useful data now exists */
775
776 #if defined(RECORD_REALTIME) || defined(REALTIME_ON_BOTL)
777
778 /* Start the timer here */
779 realtime_data.realtime = (time_t)0L;
780
781 #if defined(BSD) && !defined(POSIX_TYPES)
782 (void) time((long *)&realtime_data.restoretime);
783 #else
784 (void) time(&realtime_data.restoretime);
785 #endif
786
787 #endif /* RECORD_REALTIME || REALTIME_ON_BOTL */
788
789 /* Success! */
790 welcome(TRUE);
791 return;
792 }
793
794 /* show "welcome [back] to unnethack" message at program startup */
795 void
welcome(new_game)796 welcome(new_game)
797 boolean new_game; /* false => restoring an old game */
798 {
799 char buf[BUFSZ];
800 boolean currentgend = Upolyd ? u.mfemale : flags.female;
801 const char *role_name;
802 char *annotation;
803
804 /*
805 * The "welcome back" message always describes your innate form
806 * even when polymorphed or wearing a helm of opposite alignment.
807 * Alignment is shown unconditionally for new games; for restores
808 * it's only shown if it has changed from its original value.
809 * Sex is shown for new games except when it is redundant; for
810 * restores it's only shown if different from its original value.
811 */
812 *buf = '\0';
813 if (new_game || u.ualignbase[A_ORIGINAL] != u.ualignbase[A_CURRENT])
814 Sprintf(eos(buf), " %s", align_str(u.ualignbase[A_ORIGINAL]));
815 if (!urole.name.f &&
816 (new_game ? (urole.allow & ROLE_GENDMASK) == (ROLE_MALE|ROLE_FEMALE) :
817 currentgend != flags.initgend))
818 Sprintf(eos(buf), " %s", genders[currentgend].adj);
819
820 role_name = (currentgend && urole.name.f) ? urole.name.f : urole.name.m;
821 pline(new_game ? "%s %s, welcome to UnNetHack! You are a%s %s %s."
822 : "%s %s, the%s %s %s, welcome back to UnNetHack!",
823 Hello((struct monst *) 0), plname, buf, urace.adj, role_name);
824 #ifdef LIVELOGFILE
825 /* Start live reporting */
826 livelog_start();
827 livelog_game_started(new_game ? "started" : "resumed",
828 buf, urace.adj, role_name);
829 #endif
830 /* show level annotation when restoring the level */
831 if (!new_game && iflags.show_annotation &&
832 (annotation = get_annotation(&u.uz))) {
833 You("annotated this level: %s", annotation);
834 }
835 }
836
837 #ifdef POSITIONBAR
838 STATIC_DCL void
do_positionbar()839 do_positionbar()
840 {
841 static char pbar[COLNO];
842 char *p;
843
844 p = pbar;
845 /* up stairway */
846 if (upstair.sx &&
847 (glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) ==
848 S_upstair ||
849 glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) ==
850 S_upladder)) {
851 *p++ = '<';
852 *p++ = upstair.sx;
853 }
854 if (sstairs.sx &&
855 (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
856 S_upstair ||
857 glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
858 S_upladder)) {
859 *p++ = '<';
860 *p++ = sstairs.sx;
861 }
862
863 /* down stairway */
864 if (dnstair.sx &&
865 (glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) ==
866 S_dnstair ||
867 glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) ==
868 S_dnladder)) {
869 *p++ = '>';
870 *p++ = dnstair.sx;
871 }
872 if (sstairs.sx &&
873 (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
874 S_dnstair ||
875 glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
876 S_dnladder)) {
877 *p++ = '>';
878 *p++ = sstairs.sx;
879 }
880
881 /* hero location */
882 if (u.ux) {
883 *p++ = '@';
884 *p++ = u.ux;
885 }
886 /* fence post */
887 *p = 0;
888
889 update_positionbar(pbar);
890 }
891 #endif
892
893 #if defined(REALTIME_ON_BOTL) || defined (RECORD_REALTIME)
894 time_t
get_realtime(void)895 get_realtime(void)
896 {
897 time_t current_time = 0;
898 /* Add maximally this many seconds per invocation to get somewhat
899 * reasonable realtime values. */
900 #define MAX_IDLE_TIME_IN_SECONDS 60
901 static time_t time_last_activity = 0;
902 static time_t time_spent_playing = 0;
903
904 /* Get current time */
905 #if defined(BSD) && !defined(POSIX_TYPES)
906 (void) time((long *)¤t_time);
907 #else
908 (void) time(¤t_time);
909 #endif
910
911 /* Initialize last_activity_time. */
912 if (time_last_activity == 0) {
913 time_last_activity = current_time;
914 }
915
916 /* Since the timer isn't set until the game starts, this prevents us
917 * from displaying nonsense on the bottom line before it does. */
918 if (realtime_data.restoretime == 0) {
919 time_spent_playing = realtime_data.realtime;
920 } else {
921 time_t idletime = current_time - time_last_activity;
922 time_last_activity = current_time;
923 /* Add time the player spent "thinking". */
924 time_spent_playing += (idletime > MAX_IDLE_TIME_IN_SECONDS) ? MAX_IDLE_TIME_IN_SECONDS : idletime;
925 /* Update realtime for livelog events. */
926 realtime_data.realtime = time_spent_playing;
927 }
928
929 return time_spent_playing;
930 }
931 #undef MAX_IDLE_TIME_IN_SECONDS
932 #endif /* REALTIME_ON_BOTL || RECORD_REALTIME */
933
934 /** Interrupt a multiturn action if current_points is equal to max_points. */
935 STATIC_DCL
936 void
interrupt_multi(points,current_points,max_points)937 interrupt_multi(points, current_points, max_points)
938 const char *points;
939 int current_points;
940 int max_points;
941 {
942 if (multi > 0 &&
943 current_points == max_points) {
944 nomul(0, 0);
945 if (flags.verbose) pline("%s restored.", points);
946 }
947 }
948
949 /*allmain.c*/
950