Source:NetHack 3.1.0/mail.c

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