abstract
| - 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 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*/
|