1 /* NetHack 3.6 mail.c $NHDT-Date: 1436754892 2015/07/13 02:34:52 $ $NHDT-Branch: master $:$NHDT-Revision: 1.20 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
16 * Notify user when new mail has arrived. Idea by Merlyn Leroy.
18 * The mail daemon can move with less than usual restraint. It can:
19 * - move diagonally from a door
20 * - use secret and closed doors
21 * - run through a monster ("Gangway!", etc.)
22 * - run over pools & traps
24 * Possible extensions:
25 * - Open the file MAIL and do fstat instead of stat for efficiency.
26 * (But sh uses stat, so this cannot be too bad.)
27 * - Examine the mail and produce a scroll of mail named "From somebody".
28 * - Invoke MAILREADER in such a way that only this single letter is
30 * - Do something to the text when the scroll is enchanted or cancelled.
31 * - Make the daemon always appear at a stairwell, and have it find a
34 * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go
35 * through most of the effects at 'random' moments.
36 * Note by Paul Winner: The MSDOS port also 'fakes' the mail daemon at
40 STATIC_DCL boolean FDECL(md_start, (coord *));
41 STATIC_DCL boolean FDECL(md_stop, (coord *, coord *));
42 STATIC_DCL boolean FDECL(md_rush, (struct monst *, int, int));
43 STATIC_DCL void FDECL(newmail, (struct mail_info *));
45 extern char *viz_rmin, *viz_rmax; /* line-of-sight limits (vision.c) */
47 #if !defined(UNIX) && !defined(VMS)
54 /* DON'T trust all Unices to declare getpwuid() in <pwd.h> */
55 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
56 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
57 /* DO trust all SVR4 to typedef uid_t in <sys/types.h> (probably to a long) */
58 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
59 extern struct passwd *FDECL(getpwuid, (uid_t));
61 extern struct passwd *FDECL(getpwuid, (int));
65 static struct stat omstat, nmstat;
66 static char *mailbox = (char *) 0;
67 static long laststattime;
69 #if !defined(MAILPATH) && defined(AMS) /* Just a placeholder for AMS */
70 #define MAILPATH "/dev/null"
72 #if !defined(MAILPATH) && (defined(LINUX) || defined(__osf__))
73 #define MAILPATH "/var/spool/mail/"
75 #if !defined(MAILPATH) && defined(__FreeBSD__)
76 #define MAILPATH "/var/mail/"
78 #if !defined(MAILPATH) && (defined(BSD) || defined(ULTRIX))
79 #define MAILPATH "/usr/spool/mail/"
81 #if !defined(MAILPATH) && (defined(SYSV) || defined(HPUX))
82 #define MAILPATH "/usr/mail/"
88 if (!mailbox && !(mailbox = nh_getenv("MAIL"))) {
91 struct passwd ppasswd;
93 (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof(struct passwd));
95 mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)
96 + sizeof(AMS_MAILBOX));
97 Strcpy(mailbox, ppasswd.pw_dir);
98 Strcat(mailbox, AMS_MAILBOX);
102 const char *pw_name = getpwuid(getuid())->pw_name;
103 mailbox = (char *) alloc(sizeof(MAILPATH) + strlen(pw_name));
104 Strcpy(mailbox, MAILPATH);
105 Strcat(mailbox, pw_name);
111 if (stat(mailbox, &omstat)) {
112 #ifdef PERMANENT_MAILBOX
113 pline("Cannot get status of MAIL=\"%s\".", mailbox);
123 * Pick coordinates for a starting position for the mail daemon. Called
124 * from newmail() and newphone().
130 coord testcc; /* scratch coordinates */
131 int row; /* current row we are checking */
132 int lax; /* if TRUE, pick a position in sight. */
133 int dd; /* distance to current point */
134 int max_distance; /* max distance found so far */
137 * If blind and not telepathic, then it doesn't matter what we pick ---
138 * the hero is not going to see it anyway. So pick a nearby position.
140 if (Blind && !Blind_telepat) {
141 if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0))
142 return FALSE; /* no good positions */
147 * Arrive at an up or down stairwell if it is in line of sight from the
150 if (couldsee(upstair.sx, upstair.sy)) {
151 startp->x = upstair.sx;
152 startp->y = upstair.sy;
155 if (couldsee(dnstair.sx, dnstair.sy)) {
156 startp->x = dnstair.sx;
157 startp->y = dnstair.sy;
162 * Try to pick a location out of sight next to the farthest position away
163 * from the hero. If this fails, try again, just picking the farthest
164 * position that could be seen. What we really ought to be doing is
165 * finding a path from a stairwell...
167 * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These
168 * are the LOS limits for each row.
170 lax = 0; /* be picky */
173 for (row = 0; row < ROWNO; row++) {
174 if (viz_rmin[row] < viz_rmax[row]) {
175 /* There are valid positions on this row. */
176 dd = distu(viz_rmin[row], row);
177 if (dd > max_distance) {
181 startp->x = viz_rmin[row];
183 } else if (enexto(&testcc, (xchar) viz_rmin[row], row,
184 (struct permonst *) 0)
185 && !cansee(testcc.x, testcc.y)
186 && couldsee(testcc.x, testcc.y)) {
191 dd = distu(viz_rmax[row], row);
192 if (dd > max_distance) {
196 startp->x = viz_rmax[row];
198 } else if (enexto(&testcc, (xchar) viz_rmax[row], row,
199 (struct permonst *) 0)
200 && !cansee(testcc.x, testcc.y)
201 && couldsee(testcc.x, testcc.y)) {
209 if (max_distance < 0) {
211 lax = 1; /* just find a position */
221 * Try to choose a stopping point as near as possible to the starting
222 * position while still adjacent to the hero. If all else fails, try
223 * enexto(). Use enexto() as a last resort because enexto() chooses
224 * its point randomly, which is not what we want.
227 md_stop(stopp, startp)
228 coord *stopp; /* stopping position (we fill it in) */
229 coord *startp; /* starting position (read only) */
231 int x, y, distance, min_distance = -1;
233 for (x = u.ux - 1; x <= u.ux + 1; x++)
234 for (y = u.uy - 1; y <= u.uy + 1; y++) {
235 if (!isok(x, y) || (x == u.ux && y == u.uy))
238 if (accessible(x, y) && !MON_AT(x, y)) {
239 distance = dist2(x, y, startp->x, startp->y);
240 if (min_distance < 0 || distance < min_distance
241 || (distance == min_distance && rn2(2))) {
244 min_distance = distance;
249 /* If we didn't find a good spot, try enexto(). */
250 if (min_distance < 0 && !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON]))
256 /* Let the mail daemon have a larger vocabulary. */
258 static NEARDATA const char *mail_text[] = { "Gangway!", "Look out!",
261 static NEARDATA const char *mail_text[] = { "
\82Ç
\82¢
\82½
\82Ç
\82¢
\82½
\81I", "
\8bC
\82ð
\82Â
\82¯
\82ë
\81I",
262 "
\82¶
\82á
\82Ü
\82·
\82é
\82æ
\81I" };
264 #define md_exclamations() (mail_text[rn2(3)])
267 * Make the mail daemon run through the dungeon. The daemon will run over
268 * any monsters that are in its path, but will replace them later. Return
269 * FALSE if the md gets stuck in a position where there is a monster. Return
275 register int tx, ty; /* destination of mail daemon */
277 struct monst *mon; /* displaced monster */
278 register int dx, dy; /* direction counters */
279 int fx = md->mx, fy = md->my; /* current location */
280 int nfx = fx, nfy = fy, /* new location */
281 d1, d2; /* shortest distances */
284 * It is possible that the monster at (fx,fy) is not the md when:
285 * the md rushed the hero and failed, and is now starting back.
287 if (m_at(fx, fy) == md) {
288 remove_monster(fx, fy); /* pick up from orig position */
293 * At the beginning and exit of this loop, md is not placed in the
297 /* Find a good location next to (fx,fy) closest to (tx,ty). */
298 d1 = dist2(fx, fy, tx, ty);
299 for (dx = -1; dx <= 1; dx++)
300 for (dy = -1; dy <= 1; dy++)
301 if ((dx || dy) && isok(fx + dx, fy + dy)
302 && !IS_STWALL(levl[fx + dx][fy + dy].typ)) {
303 d2 = dist2(fx + dx, fy + dy, tx, ty);
311 /* Break if the md couldn't find a new position. */
312 if (nfx == fx && nfy == fy)
315 fx = nfx; /* this is our new position */
318 /* Break if the md reaches its destination. */
319 if (fx == tx && fy == ty)
322 if ((mon = m_at(fx, fy)) != 0) /* save monster at this position */
323 verbalize1(md_exclamations());
324 else if (fx == u.ux && fy == u.uy)
326 verbalize("Excuse me.");
328 verbalize("
\82¿
\82å
\82Á
\82Æ
\82µ
\82Â
\82ê
\82¢
\81D");
330 place_monster(md, fx, fy); /* put md down */
331 newsym(fx, fy); /* see it */
332 flush_screen(0); /* make sure md shows up */
333 delay_output(); /* wait a little bit */
335 /* Remove md from the dungeon. Restore original mon, if necessary. */
337 if ((mon->mx != fx) || (mon->my != fy))
338 place_worm_seg(mon, fx, fy);
340 place_monster(mon, fx, fy);
342 remove_monster(fx, fy);
347 * Check for a monster at our stopping position (this is possible, but
348 * very unlikely). If one exists, then have the md leave in disgust.
350 if ((mon = m_at(fx, fy)) != 0) {
351 place_monster(md, fx, fy); /* display md with text below */
354 verbalize("This place's too crowded. I'm outta here.");
356 verbalize("
\82±
\82±
\82Í
\8d¬
\82Ý
\82·
\82¬
\81D
\82±
\82±
\82Å
\91Ò
\82Á
\82Ä
\82é
\82æ
\81D");
358 if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */
359 place_worm_seg(mon, fx, fy);
361 place_monster(mon, fx, fy);
367 place_monster(md, fx, fy); /* place at final spot */
370 delay_output(); /* wait a little bit */
375 /* Deliver a scroll of mail. */
379 struct mail_info *info;
383 boolean message_seen = FALSE;
385 /* Try to find good starting and stopping places. */
386 if (!md_start(&start) || !md_stop(&stop, &start))
389 /* Make the daemon. Have it rush towards the hero. */
390 if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS)))
392 if (!md_rush(md, stop.x, stop.y))
397 verbalize("%s, %s! %s.", Hello(md), plname, info->display_txt);
399 verbalize("%s
\81I%s
\81D", Hello(md), info->display_txt);
402 if (info->message_typ) {
403 struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE);
405 if (info->object_nam)
406 obj = oname(obj, info->object_nam);
407 if (info->response_cmd)
408 new_omailcmd(obj, info->response_cmd);
410 if (distu(md->mx, md->my) > 2)
414 verbalize("
\82Ù
\82ç
\82æ
\81I");
415 display_nhwindow(WIN_MESSAGE, FALSE);
417 obj = hold_another_object(obj, "Oops!", (const char *) 0,
420 obj = hold_another_object(obj, "
\82¨
\82Á
\82Æ
\81I", (const char *) 0,
425 /* zip back to starting location */
427 (void) md_rush(md, start.x, start.y);
429 /* deliver some classes of messages even if no daemon ever shows up */
431 if (!message_seen && info->message_typ == MSG_OTHER)
433 pline("Hark! \"%s.\"", info->display_txt);
435 pline("
\81u%s
\81D
\81v
\82Æ
\8c¾
\82¤
\82±
\82Æ
\82¾
\81I", info->display_txt);
438 #if !defined(UNIX) && !defined(VMS)
443 if (u.uswallow || !flags.biff)
445 if (mustgetmail < 0) {
446 #if defined(AMIGA) || defined(MSDOS) || defined(TOS)
447 mustgetmail = (moves < 2000) ? (100 + rn2(2000)) : (2000 + rn2(3000));
451 if (--mustgetmail <= 0) {
452 static struct mail_info deliver = { MSG_MAIL,
454 "I have some mail for you", 0,
456 "
\83\81\81[
\83\8b\82ð
\8e\9d\82Á
\82Ä
\82«
\82½
\82æ", 0,
468 static char *junk[] = {
469 NULL, /* placeholder for "Report bugs to <devteam@nethack.org>.", */
471 "Please disregard previous letter.", "Welcome to NetHack.",
473 "
\91O
\82Ì
\83\81\81[
\83\8b\82Í
\96Y
\82ê
\82Ä
\82
\82¾
\82³
\82¢
\81D", "NetHack
\82Ö
\82æ
\82¤
\82±
\82»
\81I",
475 "Only Amiga makes it possible.", "CATS have all the answers.",
477 "This mail complies with the Yendorian Anti-Spam Act (YASA)",
478 "Please find enclosed a small token to represent your Owlbear",
479 "**FR33 P0T10N 0F FULL H34L1NG**",
480 "Please return to sender (Asmodeus)",
481 "Buy a potion of gain level for only $19.99! Guaranteed to be blessed!",
482 "Invitation: Visit the NetHack web site at http://www.nethack.org!"
485 /* XXX replace with more general substitution code and add local
486 * contact message. Also use DEVTEAM_URL */
487 if (junk[0] == NULL) {
488 #define BUGS_FORMAT "Report bugs to <%s>."
489 /* +2 from '%s' suffices as substitute for usual +1 for terminator */
490 junk[0] = (char *) alloc(strlen(BUGS_FORMAT) + strlen(DEVTEAM_EMAIL));
491 Sprintf(junk[0], BUGS_FORMAT, DEVTEAM_EMAIL);
496 pline("Unfortunately you cannot see what it says.");
498 pline("
\8ec
\94O
\82È
\82ª
\82ç
\89½
\82Æ
\8f\91\82¢
\82Ä
\82 \82é
\82Ì
\82©
\8c©
\82é
\82±
\82Æ
\82ª
\82Å
\82«
\82È
\82¢
\81D");
501 pline("It reads: \"%s\"", junk[rn2(SIZE(junk))]);
503 pline("
\82»
\82ê
\82ð
\93Ç
\82ñ
\82¾
\81F\"%s\"", junk[rn2(SIZE(junk))]);
506 #endif /* !UNIX && !VMS */
513 if (!mailbox || u.uswallow || !flags.biff
515 || moves < laststattime + MAILCKFREQ
520 laststattime = moves;
521 if (stat(mailbox, &nmstat)) {
522 #ifdef PERMANENT_MAILBOX
523 pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox);
528 } else if (nmstat.st_mtime > omstat.st_mtime) {
529 if (nmstat.st_size) {
530 static struct mail_info deliver = {
531 #ifndef NO_MAILREADER
533 MSG_MAIL, "I have some mail for you",
535 MSG_MAIL, "
\83\81\83C
\83\8b\82ð
\8e\9d\82Á
\82Ä
\82«
\82½
\82æ",
537 /* suppress creation and delivery of scroll of mail */
539 MSG_OTHER, "You have some mail in the outside world",
541 MSG_OTHER, "
\8aO
\82Ì
\90¢
\8aE
\82©
\82ç
\82Ì
\83\81\81[
\83\8b\82¾",
547 getmailstatus(); /* might be too late ... */
556 #ifdef DEF_MAILREADER /* This implies that UNIX is defined */
557 register const char *mr = 0;
559 display_nhwindow(WIN_MESSAGE, FALSE);
560 if (!(mr = nh_getenv("MAILREADER")))
564 (void) execl(mr, mr, (char *) 0);
565 terminate(EXIT_FAILURE);
568 #ifndef AMS /* AMS mailboxes are directories */
569 display_file(mailbox, TRUE);
571 #endif /* DEF_MAILREADER */
573 /* get new stat; not entirely correct: there is a small time
574 window where we do not see new mail */
582 extern NDECL(struct mail_info *parse_next_broadcast);
584 volatile int broadcasts = 0;
589 struct mail_info *brdcst;
591 if (u.uswallow || !flags.biff)
594 while (broadcasts > 0) { /* process all trapped broadcasts [until] */
596 if ((brdcst = parse_next_broadcast()) != 0) {
598 break; /* only handle one real message at a time */
607 #ifdef SHELL /* can't access mail reader without spawning subprocess */
608 const char *txt, *cmd;
609 char *p, buf[BUFSZ], qbuf[BUFSZ];
612 /* there should be a command in OMAILCMD */
618 if (has_omailcmd(otmp))
619 cmd = OMAILCMD(otmp);
623 Sprintf(qbuf, "System command (%s)", cmd);
625 if (*buf != '\033') {
626 for (p = eos(buf); p > buf; *p = '\0')
628 break; /* strip trailing spaces */
630 cmd = buf; /* use user entered command */
631 if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!"))
632 cmd = (char *) 0; /* interactive escape */
634 vms_doshell(cmd, TRUE);