Source:NetHack 3.2.0/mail.c

Below is the full text to mail.c from the source code of NetHack 3.2.0. To link to a particular line, write [[NetHack 3.2.0/mail.c#line123 ]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

1.   /*	SCCS Id: @(#)mail.c	3.2	95/07/19	*/ 2.   /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3.    /* NetHack may be freely redistributed. See license for details. */ 4.     5.    #include "hack.h"  6. 7.   #ifdef MAIL 8.   #include "mail.h"  9.    /* 10.   * Notify user when new mail has arrived. Idea by Merlyn Leroy. 11.   *  12.    * The mail daemon can move with less than usual restraint. It can: 13.   *	- move diagonally from a door 14.   *	- use secret and closed doors 15.   *	- run through a monster ("Gangway!", etc.) 16.   *	- run over pools & traps 17.   *  18.    * Possible extensions: 19.   *	- Open the file MAIL and do fstat instead of stat for efficiency. 20.   *	  (But sh uses stat, so this cannot be too bad.) 21.   *	- Examine the mail and produce a scroll of mail named "From somebody". 22.   *	- Invoke MAILREADER in such a way that only this single letter is read. 23.   *	- Do something to the text when the scroll is enchanted or cancelled. 24.   *	- Make the daemon always appear at a stairwell, and have it find a  25. *	 path to the hero. 26.   *  27.    * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go 28. *			 through most of the effects at 'random' moments. 29.   * Note by Paul Winner:  The MSDOS port also 'fakes' the mail daemon at  30. *			 random intervals. 31.   */  32.    33.   static boolean FDECL(md_start,(coord *)); 34.  static boolean FDECL(md_stop,(coord *, coord *)); 35.  static boolean FDECL(md_rush,(struct monst *,int,int)); 36.  static void FDECL(newmail, (struct mail_info *)); 37.   38.   extern char *viz_rmin, *viz_rmax;	/* line-of-sight limits (vision.c) */ 39.   40.   #ifdef OVL0 41.   42.   # if !defined(UNIX) && !defined(VMS) 43.  int mustgetmail = -1; 44.  # endif 45.   46.   #endif /* OVL0 */ 47.  #ifdef OVLB 48.   49.   # ifdef UNIX 50.  #include   51. #include  52. /* DON'T trust all Unices to declare getpwuid in  */ 53.  #  if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) 54.  #   if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) 55.  /* DO trust all SVR4 to typedef uid_t in  (probably to a long) */ 56.  #    if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) 57.  extern struct passwd *FDECL(getpwuid,(uid_t)); 58.  #    else 59.  extern struct passwd *FDECL(getpwuid,(int)); 60.  #    endif 61.  #   endif 62.  #  endif 63.  static struct stat omstat,nmstat; 64.  static char *mailbox = (char *)0; 65.  static long laststattime; 66.   67.   # ifdef AMS				/* Just a placeholder for AMS */ 68.  #   define MAILPATH "/dev/null" 69.  # else 70.  #  if defined(BSD) || defined(ULTRIX) 71.  #   define MAILPATH "/usr/spool/mail/" 72.  #  endif 73.  #  if defined(SYSV) || defined(HPUX) 74.  #   define MAILPATH "/usr/mail/" 75.  #  endif 76.  # endif /* AMS */ 77.   78.   void 79.  getmailstatus 80.  {  81.   	if(!mailbox && !(mailbox = getenv("MAIL"))) { 82.  #  ifdef MAILPATH 83.  #   ifdef AMS 84.  	        struct passwd ppasswd; 85.   86.   		(void) memcpy(&ppasswd, getpwuid(getuid), sizeof(struct passwd)); 87.  		if (ppasswd.pw_dir) { 88.  		     mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX)); 89.  		     Strcpy(mailbox, ppasswd.pw_dir); 90.  		     Strcat(mailbox, AMS_MAILBOX); 91.  		} else 92.  		  return; 93.  #   else 94.  		mailbox = (char *) alloc(sizeof(MAILPATH)+8); 95.  		Strcpy(mailbox, MAILPATH); 96.  		Strcat(mailbox, getpwuid(getuid)->pw_name); 97.  #  endif /* AMS */ 98.  #  else 99.  		return; 100. #  endif 101. 	}  102.  	if(stat(mailbox, &omstat)){ 103. #  ifdef PERMANENT_MAILBOX 104. 		pline("Cannot get status of MAIL=\"%s\".", mailbox); 105. 		mailbox = 0; 106. #  else 107. 		omstat.st_mtime = 0; 108. #  endif 109. 	}  110.  }  111.  # endif /* UNIX */ 112.  113.  #endif /* OVLB */ 114. #ifdef OVL0 115.  116.  /*  117.   * Pick coordinates for a starting position for the mail daemon. Called 118.  * from newmail and newphone. 119.  */  120.  static boolean 121. md_start(startp) 122.     coord *startp; 123. {  124.      coord testcc;	/* scratch coordinates */ 125.     int row;		/* current row we are checking */ 126.     int lax;		/* if TRUE, pick a position in sight. */ 127.      int dd;		/* distance to current point */ 128.     int max_distance;	/* max distance found so far */ 129.  130.      /*  131.       * If blind and not telepathic, then it doesn't matter what we pick --- 132.      * the hero is not going to see it anyway. So pick a nearby position. 133.      */  134.      if (Blind && !Telepat) { 135. 	if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0)) 136. 	    return FALSE;	/* no good posiitons */ 137. 	return TRUE; 138.     }  139.   140.      /*  141.       * Arrive at an up or down stairwell if it is in line of sight from the 142.      * hero. 143.      */  144.      if (couldsee(upstair.sx, upstair.sy)) { 145. 	startp->x = upstair.sx; 146. 	startp->y = upstair.sy; 147. 	return TRUE; 148.     }  149.      if (couldsee(dnstair.sx, dnstair.sy)) { 150. 	startp->x = dnstair.sx; 151. 	startp->y = dnstair.sy; 152. 	return TRUE; 153.     }  154.   155.      /*  156.       * Try to pick a location out of sight next to the farthest position away 157.      * from the hero. If this fails, try again, just picking the farthest 158.      * position that could be seen. What we really ought to be doing is 159. * finding a path from a stairwell... 160. * 161.       * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These 162.      * are the LOS limits for each row. 163.      */  164.      lax = 0;	/* be picky */ 165.     max_distance = -1; 166. retry: 167.     for (row = 0; row < ROWNO; row++) { 168. 	if (viz_rmin[row] < viz_rmax[row]) { 169. 	    /* There are valid positions on this row. */ 170.  	    dd = distu(viz_rmin[row],row); 171. 	    if (dd > max_distance) { 172. 		if (lax) { 173. 		    max_distance = dd; 174. 		    startp->y = row; 175. 		    startp->x = viz_rmin[row]; 176. 		  177.  		} else if (enexto(&testcc, (xchar)viz_rmin[row], row, 178. 						(struct permonst *) 0) &&  179.  			   !cansee(testcc.x, testcc.y) &&  180.  			   couldsee(testcc.x, testcc.y)) { 181. 		    max_distance = dd; 182. 		    *startp = testcc; 183. 		}  184.  	    }  185.  	    dd = distu(viz_rmax[row],row); 186. 	    if (dd > max_distance) { 187. 		if (lax) { 188. 		    max_distance = dd; 189. 		    startp->y = row; 190. 		    startp->x = viz_rmax[row]; 191. 		  192.  		} else if (enexto(&testcc, (xchar)viz_rmax[row], row, 193. 						(struct permonst *) 0) &&  194.  			   !cansee(testcc.x,testcc.y) &&  195.  			   couldsee(testcc.x, testcc.y)) { 196.  197.  		    max_distance = dd; 198. 		    *startp = testcc; 199. 		}  200.  	    }  201.  	}  202.      }  203.   204.      if (max_distance < 0) { 205. 	if (!lax) { 206. 	    lax = 1;		/* just find a position */ 207. 	    goto retry; 208. 	}  209.  	return FALSE; 210.     }  211.   212.      return TRUE; 213. }  214.   215.  /*  216.   * Try to choose a stopping point as near as possible to the starting 217.  * position while still adjacent to the hero. If all else fails, try 218.  * enexto. Use enexto as a last resort because enexto chooses 219.  * its point randomly, which is not what we want. 220.  */  221.  static boolean 222. md_stop(stopp, startp) 223.     coord *stopp;	/* stopping position (we fill it in) */ 224.     coord *startp;	/* starting positon (read only) */ 225. {  226.      int x, y, distance, min_distance = -1; 227.  228.      for (x = u.ux-1; x <= u.ux+1; x++) 229. 	for (y = u.uy-1; y <= u.uy+1; y++) { 230. 	    if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; 231.  232.  	    if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) { 233. 		distance = dist2(x,y,startp->x,startp->y); 234. 		if (min_distance < 0 || distance < min_distance ||  235.  			(distance == min_distance && rn2(2))) { 236. 		    stopp->x = x;  237. stopp->y = y; 238. min_distance = distance; 239. 		}  240.  	    }  241.  	}  242.   243.      /* If we didn't find a good spot, try enexto. */ 244.      if (min_distance < 0 &&  245.  		!enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON])) 246. 	return FALSE; 247.  248.      return TRUE; 249. }  250.   251.  /* Let the mail daemon have a larger vocabulary. */ 252.  static NEARDATA const char *mail_text[] = { 253.     "Gangway!", 254.     "Look out!", 255.     "Pardon me!" 256. };  257.  #define md_exclamations	(mail_text[rn2(3)]) 258.  259.  /*  260.   * Make the mail daemon run through the dungeon. The daemon will run over 261.  * any monsters that are in its path, but will replace them later. Return 262.  * FALSE if the md gets stuck in a position where there is a monster. Return 263.  * TRUE otherwise. 264.  */  265.  static boolean 266. md_rush(md,tx,ty) 267.     struct monst *md; 268.     register int tx, ty;		/* destination of mail daemon */ 269. {  270.      struct monst *mon;			/* displaced monster */ 271.     register int dx, dy;		/* direction counters */ 272.     int fx = md->mx, fy = md->my;	/* current location */ 273.     int nfx = fx, nfy = fy,		/* new location */ 274. 	d1, d2;				/* shortest distances */ 275.  276.      /*  277.       * It is possible that the monster at (fx,fy) is not the md when: 278.      * the md rushed the hero and failed, and is now starting back. 279.      */  280.      if (m_at(fx, fy) == md) { 281. 	remove_monster(fx, fy);		/* pick up from orig position */ 282. 	newsym(fx, fy); 283.     }  284.   285.      /*  286.       * At the beginning and exit of this loop, md is not placed in the 287.      * dungeon. 288.      */  289.      while (1) { 290. 	/* Find a good location next to (fx,fy) closest to (tx,ty). */ 291.  	d1 = dist2(fx,fy,tx,ty); 292. 	for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) 293. 	    if ((dx || dy) && isok(fx+dx,fy+dy) &&  294.  				       !IS_STWALL(levl[fx+dx][fy+dy].typ)) { 295. 		d2 = dist2(fx+dx,fy+dy,tx,ty); 296. 		if (d2 < d1) { 297. 		    d1 = d2; 298. 		    nfx = fx+dx; 299. 		    nfy = fy+dy; 300. 		}  301.  	    }  302.   303.  	/* Break if the md couldn't find a new position. */ 304.  	if (nfx == fx && nfy == fy) break; 305.  306.  	fx = nfx;			/* this is our new position */ 307. 	fy = nfy; 308.  309.  	/* Break if the md reaches its destination. */ 310.  	if (fx == tx && fy == ty) break; 311.  312.  	if ((mon = m_at(fx,fy)) != 0)	/* save monster at this position */ 313. 	    verbalize(md_exclamations); 314. 	else if (fx == u.ux && fy == u.uy) 315. 	    verbalize("Excuse me."); 316.  317.  	place_monster(md,fx,fy);	/* put md down */ 318. 	newsym(fx,fy);			/* see it */ 319. 	flush_screen(0);		/* make sure md shows up */ 320. 	delay_output;			/* wait a little bit */ 321.  322.  	/* Remove md from the dungeon. Restore original mon, if necessary. */ 323.  	if (mon) { 324. 	    if ((mon->mx != fx) || (mon->my != fy)) 325. 		place_worm_seg(mon, fx, fy); 326. 	    else 327. 		place_monster(mon, fx, fy); 328. 	} else 329. 	    remove_monster(fx, fy); 330. 	newsym(fx,fy); 331.     }  332.   333.      /*  334.       * Check for a monster at our stopping position (this is possible, but  335.       * very unlikely). If one exists, then have the md leave in disgust. 336.      */  337.      if ((mon = m_at(fx, fy)) != 0) { 338. 	place_monster(md, fx, fy);	/* display md with text below */ 339. 	newsym(fx, fy); 340. 	verbalize("This place's too crowded.  I'm outta here."); 341.  342.  	if ((mon->mx != fx) || (mon->my != fy))	/* put mon back */ 343. 	    place_worm_seg(mon, fx, fy); 344. 	else 345. 	    place_monster(mon, fx, fy); 346.  347.  	newsym(fx, fy); 348. 	return FALSE; 349.     }  350.   351.      place_monster(md, fx, fy);	/* place at final spot */ 352.     newsym(fx, fy); 353.     flush_screen(0); 354.     delay_output;			/* wait a little bit */ 355.  356.      return TRUE; 357. }  358.   359.  /* Deliver a scroll of mail. */ 360.  /*ARGSUSED*/ 361. static void 362. newmail(info) 363. struct mail_info *info; 364. {  365.      struct monst *md; 366.     coord start, stop; 367.     boolean message_seen = FALSE; 368.  369.      /* Try to find good starting and stopping places. */ 370.      if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up; 371.  372.      /* Make the daemon. Have it rush towards the hero. */ 373.      if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y))) goto give_up; 374.     if (!md_rush(md, stop.x, stop.y)) goto go_back; 375.  376.      message_seen = TRUE; 377.     verbalize("Hello, %s!  %s.", plname, info->display_txt); 378.  379.      if (info->message_typ) { 380. 	struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); 381. 	if (distu(md->mx,md->my) > 2) 382. 	    verbalize("Catch!"); 383. 	display_nhwindow(WIN_MESSAGE, FALSE); 384. 	if (info->object_nam) { 385. 	    obj = oname(obj, info->object_nam); 386. 	    if (info->response_cmd) {	/*(hide extension of the obj name)*/ 387. 		int namelth = info->response_cmd - info->object_nam - 1; 388. 		if ( namelth <= 0 || namelth >= (int) obj->onamelth ) 389. 		    impossible("mail delivery screwed up"); 390. 		else 391. 		    *(ONAME(obj) + namelth) = '\0'; 392. 		/* Note: renaming object will discard the hidden command. */ 393.  	    }  394.  	}  395.  	obj = hold_another_object(obj, "Oops!",  396.  				  (const char *)0, (const char *)0); 397.     }  398.   399.      /* zip back to starting location */ 400. go_back: 401.     (void) md_rush(md, start.x, start.y); 402.     mongone(md); 403.     /* deliver some classes of messages even if no daemon ever shows up */ 404. give_up: 405.     if (!message_seen && info->message_typ == MSG_OTHER) 406. 	pline("Hark!  \"%s.\"", info->display_txt); 407. }  408.   409.  # if !defined(UNIX) && !defined(VMS) 410.  411.  void 412. ckmailstatus 413. {  414.  	if (u.uswallow || !flags.biff) return; 415. 	if (mustgetmail < 0) { 416. #if defined(AMIGA) || defined(MSDOS) 417. 	    mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000)); 418. #endif 419. 	    return; 420. 	}  421.  	if (--mustgetmail <= 0) { 422. 		static struct mail_info 423. 			deliver = {MSG_MAIL,"I have some mail for you",0,0}; 424. 		newmail(&deliver); 425. 		mustgetmail = -1; 426. 	}  427.  }  428.   429.  /*ARGSUSED*/ 430. void 431. readmail(otmp) 432. struct obj *otmp; 433. {  434.  	char *junk[]={ 435. 	"Please disregard previous letter.", 436. 	"Welcome to NetHack 3.2!", 437. #ifdef AMIGA 438. 	"Only Amiga makes it possible.", 439. 	"CATS have all the answers.", 440. #endif 441. 	"Report bugs to nethack-bugs@linc.cis.upenn.edu" 442. 	};  443.   444.  	pline("It reads:  \"%s\"", junk[rn2(SIZE(junk))]); 445. }  446.   447.  # endif /* !UNIX && !VMS */ 448.  449.  # ifdef UNIX 450.  451.  void 452. ckmailstatus 453. {  454.  	if(!mailbox || u.uswallow || !flags.biff  455.  #  ifdef MAILCKFREQ  456.  		    || moves < laststattime + MAILCKFREQ  457.  #  endif  458.  							) 459. 		return; 460.  461.  	laststattime = moves; 462. 	if(stat(mailbox, &nmstat)){ 463. #  ifdef PERMANENT_MAILBOX 464. 		pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox); 465. 		mailbox = 0; 466. #  else 467. 		nmstat.st_mtime = 0; 468. #  endif 469. 	} else if(nmstat.st_mtime > omstat.st_mtime) { 470. 		if (nmstat.st_size) { 471. 		    static struct mail_info deliver = { 472. #  ifndef NO_MAILREADER 473. 			MSG_MAIL, "I have some mail for you", 474. #  else 475. 			/* suppress creation and delivery of scroll of mail */ 476. 			MSG_OTHER, "You have some mail in the outside world", 477. #  endif 478. 			0, 0  479.  		    };  480.  		    newmail(&deliver); 481. 		}  482.  		getmailstatus;	/* might be too late ... */ 483.  	}  484.  }  485.   486.  /*ARGSUSED*/ 487. void 488. readmail(otmp) 489. struct obj *otmp; 490. {  491.  #  ifdef DEF_MAILREADER			/* This implies that UNIX is defined */ 492. 	register const char *mr = 0; 493.  494.  	display_nhwindow(WIN_MESSAGE, FALSE); 495. 	if(!(mr = getenv("MAILREADER"))) 496. 		mr = DEF_MAILREADER; 497.  498.  	if(child(1)){ 499. 		(void) execl(mr, mr, (char *)0); 500. 		terminate(EXIT_FAILURE); 501. 	}  502.  #  else 503. #   ifndef AMS				/* AMS mailboxes are directories */ 504. 	display_file(mailbox, TRUE); 505. #   endif /* AMS */ 506. #  endif /* DEF_MAILREADER */ 507.  508.  	/* get new stat; not entirely correct: there is a small time 509. 	   window where we do not see new mail */ 510. 	getmailstatus; 511. }  512.   513.  # endif /* UNIX */ 514.  515.  # ifdef VMS 516.  517.  volatile int broadcasts = 0; 518.  519.  void 520. ckmailstatus 521. {  522.      struct mail_info *brdcst, *parse_next_broadcast; 523.  524.      if(u.uswallow || !flags.biff) return; 525.  526.      while (broadcasts > 0) {	/* process all trapped broadcasts [until] */ 527. 	broadcasts--; 528. 	if ((brdcst = parse_next_broadcast) != 0) { 529. 	    newmail(brdcst); 530. 	    break;		/* only handle one real message at a time */ 531. 	}  532.      }  533.  }  534.   535.  void 536. readmail(otmp) 537. struct obj *otmp; 538. {  539.  #  ifdef SHELL	/* can't access mail reader without spawning subprocess */ 540.     const char *txt, *cmd; 541.     char *p, buf[BUFSZ], qbuf[BUFSZ]; 542.     int len; 543.  544.      /* there should be a command hidden beyond the object name */ 545.     txt = otmp->onamelth ? ONAME(otmp) : ""; 546.     len = strlen(txt); 547.     cmd = (len + 1 < otmp->onamelth) ? txt + len + 1 : (char *) 0; 548.     if (!cmd || !*cmd) cmd = "SPAWN"; 549.  550.      Sprintf(qbuf, "System command (%s)", cmd); 551.     getlin(qbuf, buf); 552.     if (*buf != '\033') { 553. 	for (p = eos(buf); p > buf; *p = '\0') 554. 	    if (*--p != ' ') break;	/* strip trailing spaces */ 555. 	if (*buf) cmd = buf;		/* use user entered command */ 556. 	if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!")) 557. 	    cmd = (char *) 0;		/* interactive escape */ 558.  559.  	vms_doshell(cmd, TRUE); 560. 	(void) sleep(1); 561.     }  562.  #  endif /* SHELL */ 563. }  564.   565.  # endif /* VMS */ 566.  567.  #endif /* OVL0 */ 568.  569.  #endif /* MAIL */ 570.  571.  /*mail.c*/