From e7f30eef99e4307228479e935cdb6f40cd59dbc4 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:49 +0000 Subject: [PATCH] *** empty log message *** --- src/mail.c | 624 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 624 insertions(+) create mode 100644 src/mail.c diff --git a/src/mail.c b/src/mail.c new file mode 100644 index 000000000..1a446902f --- /dev/null +++ b/src/mail.c @@ -0,0 +1,624 @@ +/* SCCS Id: @(#)mail.c 3.3 1999/08/24 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef MAIL +#include "mail.h" + +/* + * Notify user when new mail has arrived. Idea by Merlyn Leroy. + * + * The mail daemon can move with less than usual restraint. It can: + * - move diagonally from a door + * - use secret and closed doors + * - run through a monster ("Gangway!", etc.) + * - run over pools & traps + * + * Possible extensions: + * - Open the file MAIL and do fstat instead of stat for efficiency. + * (But sh uses stat, so this cannot be too bad.) + * - Examine the mail and produce a scroll of mail named "From somebody". + * - Invoke MAILREADER in such a way that only this single letter is read. + * - Do something to the text when the scroll is enchanted or cancelled. + * - Make the daemon always appear at a stairwell, and have it find a + * path to the hero. + * + * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go + * through most of the effects at 'random' moments. + * Note by Paul Winner: The MSDOS port also 'fakes' the mail daemon at + * random intervals. + */ + +STATIC_DCL boolean FDECL(md_start,(coord *)); +STATIC_DCL boolean FDECL(md_stop,(coord *, coord *)); +STATIC_DCL boolean FDECL(md_rush,(struct monst *,int,int)); +STATIC_DCL void FDECL(newmail, (struct mail_info *)); + +extern char *viz_rmin, *viz_rmax; /* line-of-sight limits (vision.c) */ + +#ifdef OVL0 + +# if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) +int mustgetmail = -1; +# endif + +#endif /* OVL0 */ +#ifdef OVLB + +# ifdef UNIX +#include +#include +/* DON'T trust all Unices to declare getpwuid() in */ +# if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) +# if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) +/* DO trust all SVR4 to typedef uid_t in (probably to a long) */ +# if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) +extern struct passwd *FDECL(getpwuid,(uid_t)); +# else +extern struct passwd *FDECL(getpwuid,(int)); +# endif +# endif +# endif +static struct stat omstat,nmstat; +static char *mailbox = (char *)0; +static long laststattime; + +# if !defined(MAILPATH) && defined(AMS) /* Just a placeholder for AMS */ +# define MAILPATH "/dev/null" +# endif +# if !defined(MAILPATH) && (defined(LINUX) || defined(__osf__)) +# define MAILPATH "/var/spool/mail/" +# endif +# if !defined(MAILPATH) && defined(__FreeBSD__) +# define MAILPATH "/var/mail/" +# endif +# if !defined(MAILPATH) && (defined(BSD) || defined(ULTRIX)) +# define MAILPATH "/usr/spool/mail/" +# endif +# if !defined(MAILPATH) && (defined(SYSV) || defined(HPUX)) +# define MAILPATH "/usr/mail/" +# endif + +void +getmailstatus() +{ + if(!mailbox && !(mailbox = nh_getenv("MAIL"))) { +# ifdef MAILPATH +# ifdef AMS + struct passwd ppasswd; + + (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof(struct passwd)); + if (ppasswd.pw_dir) { + mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX)); + Strcpy(mailbox, ppasswd.pw_dir); + Strcat(mailbox, AMS_MAILBOX); + } else + return; +# else + const char *pw_name = getpwuid(getuid())->pw_name; + mailbox = (char *) alloc(sizeof(MAILPATH)+strlen(pw_name)); + Strcpy(mailbox, MAILPATH); + Strcat(mailbox, pw_name); +# endif /* AMS */ +# else + return; +# endif + } + if(stat(mailbox, &omstat)){ +# ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=\"%s\".", mailbox); + mailbox = 0; +# else + omstat.st_mtime = 0; +# endif + } +} +# endif /* UNIX */ + +#endif /* OVLB */ +#ifdef OVL0 + +/* + * Pick coordinates for a starting position for the mail daemon. Called + * from newmail() and newphone(). + */ +STATIC_OVL boolean +md_start(startp) + coord *startp; +{ + coord testcc; /* scratch coordinates */ + int row; /* current row we are checking */ + int lax; /* if TRUE, pick a position in sight. */ + int dd; /* distance to current point */ + int max_distance; /* max distance found so far */ + + /* + * If blind and not telepathic, then it doesn't matter what we pick --- + * the hero is not going to see it anyway. So pick a nearby position. + */ + if (Blind && !Blind_telepat) { + if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0)) + return FALSE; /* no good posiitons */ + return TRUE; + } + + /* + * Arrive at an up or down stairwell if it is in line of sight from the + * hero. + */ + if (couldsee(upstair.sx, upstair.sy)) { + startp->x = upstair.sx; + startp->y = upstair.sy; + return TRUE; + } + if (couldsee(dnstair.sx, dnstair.sy)) { + startp->x = dnstair.sx; + startp->y = dnstair.sy; + return TRUE; + } + + /* + * Try to pick a location out of sight next to the farthest position away + * from the hero. If this fails, try again, just picking the farthest + * position that could be seen. What we really ought to be doing is + * finding a path from a stairwell... + * + * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These + * are the LOS limits for each row. + */ + lax = 0; /* be picky */ + max_distance = -1; +retry: + for (row = 0; row < ROWNO; row++) { + if (viz_rmin[row] < viz_rmax[row]) { + /* There are valid positions on this row. */ + dd = distu(viz_rmin[row],row); + if (dd > max_distance) { + if (lax) { + max_distance = dd; + startp->y = row; + startp->x = viz_rmin[row]; + + } else if (enexto(&testcc, (xchar)viz_rmin[row], row, + (struct permonst *) 0) && + !cansee(testcc.x, testcc.y) && + couldsee(testcc.x, testcc.y)) { + max_distance = dd; + *startp = testcc; + } + } + dd = distu(viz_rmax[row],row); + if (dd > max_distance) { + if (lax) { + max_distance = dd; + startp->y = row; + startp->x = viz_rmax[row]; + + } else if (enexto(&testcc, (xchar)viz_rmax[row], row, + (struct permonst *) 0) && + !cansee(testcc.x,testcc.y) && + couldsee(testcc.x, testcc.y)) { + + max_distance = dd; + *startp = testcc; + } + } + } + } + + if (max_distance < 0) { + if (!lax) { + lax = 1; /* just find a position */ + goto retry; + } + return FALSE; + } + + return TRUE; +} + +/* + * Try to choose a stopping point as near as possible to the starting + * position while still adjacent to the hero. If all else fails, try + * enexto(). Use enexto() as a last resort because enexto() chooses + * its point randomly, which is not what we want. + */ +STATIC_OVL boolean +md_stop(stopp, startp) + coord *stopp; /* stopping position (we fill it in) */ + coord *startp; /* starting positon (read only) */ +{ + int x, y, distance, min_distance = -1; + + for (x = u.ux-1; x <= u.ux+1; x++) + for (y = u.uy-1; y <= u.uy+1; y++) { + if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; + + if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) { + distance = dist2(x,y,startp->x,startp->y); + if (min_distance < 0 || distance < min_distance || + (distance == min_distance && rn2(2))) { + stopp->x = x; + stopp->y = y; + min_distance = distance; + } + } + } + + /* If we didn't find a good spot, try enexto(). */ + if (min_distance < 0 && + !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON])) + return FALSE; + + return TRUE; +} + +/* Let the mail daemon have a larger vocabulary. */ +static NEARDATA const char *mail_text[] = { + "Gangway!", + "Look out!", + "Pardon me!" +}; +#define md_exclamations() (mail_text[rn2(3)]) + +/* + * Make the mail daemon run through the dungeon. The daemon will run over + * any monsters that are in its path, but will replace them later. Return + * FALSE if the md gets stuck in a position where there is a monster. Return + * TRUE otherwise. + */ +STATIC_OVL boolean +md_rush(md,tx,ty) + struct monst *md; + register int tx, ty; /* destination of mail daemon */ +{ + struct monst *mon; /* displaced monster */ + register int dx, dy; /* direction counters */ + int fx = md->mx, fy = md->my; /* current location */ + int nfx = fx, nfy = fy, /* new location */ + d1, d2; /* shortest distances */ + + /* + * It is possible that the monster at (fx,fy) is not the md when: + * the md rushed the hero and failed, and is now starting back. + */ + if (m_at(fx, fy) == md) { + remove_monster(fx, fy); /* pick up from orig position */ + newsym(fx, fy); + } + + /* + * At the beginning and exit of this loop, md is not placed in the + * dungeon. + */ + while (1) { + /* Find a good location next to (fx,fy) closest to (tx,ty). */ + d1 = dist2(fx,fy,tx,ty); + for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) + if ((dx || dy) && isok(fx+dx,fy+dy) && + !IS_STWALL(levl[fx+dx][fy+dy].typ)) { + d2 = dist2(fx+dx,fy+dy,tx,ty); + if (d2 < d1) { + d1 = d2; + nfx = fx+dx; + nfy = fy+dy; + } + } + + /* Break if the md couldn't find a new position. */ + if (nfx == fx && nfy == fy) break; + + fx = nfx; /* this is our new position */ + fy = nfy; + + /* Break if the md reaches its destination. */ + if (fx == tx && fy == ty) break; + + if ((mon = m_at(fx,fy)) != 0) /* save monster at this position */ + verbalize(md_exclamations()); + else if (fx == u.ux && fy == u.uy) + verbalize("Excuse me."); + + place_monster(md,fx,fy); /* put md down */ + newsym(fx,fy); /* see it */ + flush_screen(0); /* make sure md shows up */ + delay_output(); /* wait a little bit */ + + /* Remove md from the dungeon. Restore original mon, if necessary. */ + if (mon) { + if ((mon->mx != fx) || (mon->my != fy)) + place_worm_seg(mon, fx, fy); + else + place_monster(mon, fx, fy); + } else + remove_monster(fx, fy); + newsym(fx,fy); + } + + /* + * Check for a monster at our stopping position (this is possible, but + * very unlikely). If one exists, then have the md leave in disgust. + */ + if ((mon = m_at(fx, fy)) != 0) { + place_monster(md, fx, fy); /* display md with text below */ + newsym(fx, fy); + verbalize("This place's too crowded. I'm outta here."); + + if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */ + place_worm_seg(mon, fx, fy); + else + place_monster(mon, fx, fy); + + newsym(fx, fy); + return FALSE; + } + + place_monster(md, fx, fy); /* place at final spot */ + newsym(fx, fy); + flush_screen(0); + delay_output(); /* wait a little bit */ + + return TRUE; +} + +/* Deliver a scroll of mail. */ +/*ARGSUSED*/ +STATIC_OVL void +newmail(info) +struct mail_info *info; +{ + struct monst *md; + coord start, stop; + boolean message_seen = FALSE; + + /* Try to find good starting and stopping places. */ + if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up; + + /* Make the daemon. Have it rush towards the hero. */ + if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS))) + goto give_up; + if (!md_rush(md, stop.x, stop.y)) goto go_back; + + message_seen = TRUE; + verbalize("%s, %s! %s.", Hello(md), plname, info->display_txt); + + if (info->message_typ) { + struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); + if (distu(md->mx,md->my) > 2) + verbalize("Catch!"); + display_nhwindow(WIN_MESSAGE, FALSE); + if (info->object_nam) { + obj = oname(obj, info->object_nam); + if (info->response_cmd) { /*(hide extension of the obj name)*/ + int namelth = info->response_cmd - info->object_nam - 1; + if ( namelth <= 0 || namelth >= (int) obj->onamelth ) + impossible("mail delivery screwed up"); + else + *(ONAME(obj) + namelth) = '\0'; + /* Note: renaming object will discard the hidden command. */ + } + } + obj = hold_another_object(obj, "Oops!", + (const char *)0, (const char *)0); + } + + /* zip back to starting location */ +go_back: + (void) md_rush(md, start.x, start.y); + mongone(md); + /* deliver some classes of messages even if no daemon ever shows up */ +give_up: + if (!message_seen && info->message_typ == MSG_OTHER) + pline("Hark! \"%s.\"", info->display_txt); +} + +# if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) + +void +ckmailstatus() +{ + if (u.uswallow || !flags.biff) return; + if (mustgetmail < 0) { +#if defined(AMIGA) || defined(MSDOS) || defined(TOS) + mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000)); +#endif + return; + } + if (--mustgetmail <= 0) { + static struct mail_info + deliver = {MSG_MAIL,"I have some mail for you",0,0}; + newmail(&deliver); + mustgetmail = -1; + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ + static char *junk[] = { + "Please disregard previous letter.", + "Welcome to NetHack.", +#ifdef AMIGA + "Only Amiga makes it possible.", + "CATS have all the answers.", +#endif + "Report bugs to ." + }; + + if (Blind) { + pline("Unfortunately you cannot see what it says."); + } else + pline("It reads: \"%s\"", junk[rn2(SIZE(junk))]); + +} + +# endif /* !UNIX && !VMS && !LAN_MAIL */ + +# ifdef UNIX + +void +ckmailstatus() +{ + if(!mailbox || u.uswallow || !flags.biff +# ifdef MAILCKFREQ + || moves < laststattime + MAILCKFREQ +# endif + ) + return; + + laststattime = moves; + if(stat(mailbox, &nmstat)){ +# ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox); + mailbox = 0; +# else + nmstat.st_mtime = 0; +# endif + } else if(nmstat.st_mtime > omstat.st_mtime) { + if (nmstat.st_size) { + static struct mail_info deliver = { +# ifndef NO_MAILREADER + MSG_MAIL, "I have some mail for you", +# else + /* suppress creation and delivery of scroll of mail */ + MSG_OTHER, "You have some mail in the outside world", +# endif + 0, 0 + }; + newmail(&deliver); + } + getmailstatus(); /* might be too late ... */ + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ +# ifdef DEF_MAILREADER /* This implies that UNIX is defined */ + register const char *mr = 0; + + display_nhwindow(WIN_MESSAGE, FALSE); + if(!(mr = nh_getenv("MAILREADER"))) + mr = DEF_MAILREADER; + + if(child(1)){ + (void) execl(mr, mr, (char *)0); + terminate(EXIT_FAILURE); + } +# else +# ifndef AMS /* AMS mailboxes are directories */ + display_file(mailbox, TRUE); +# endif /* AMS */ +# endif /* DEF_MAILREADER */ + + /* get new stat; not entirely correct: there is a small time + window where we do not see new mail */ + getmailstatus(); +} + +# endif /* UNIX */ + +# ifdef VMS + +extern NDECL(struct mail_info *parse_next_broadcast); + +volatile int broadcasts = 0; + +void +ckmailstatus() +{ + struct mail_info *brdcst; + + if (u.uswallow || !flags.biff) return; + + while (broadcasts > 0) { /* process all trapped broadcasts [until] */ + broadcasts--; + if ((brdcst = parse_next_broadcast()) != 0) { + newmail(brdcst); + break; /* only handle one real message at a time */ + } + } +} + +void +readmail(otmp) +struct obj *otmp; +{ +# ifdef SHELL /* can't access mail reader without spawning subprocess */ + const char *txt, *cmd; + char *p, buf[BUFSZ], qbuf[BUFSZ]; + int len; + + /* there should be a command hidden beyond the object name */ + txt = otmp->onamelth ? ONAME(otmp) : ""; + len = strlen(txt); + cmd = (len + 1 < otmp->onamelth) ? txt + len + 1 : (char *) 0; + if (!cmd || !*cmd) cmd = "SPAWN"; + + Sprintf(qbuf, "System command (%s)", cmd); + getlin(qbuf, buf); + if (*buf != '\033') { + for (p = eos(buf); p > buf; *p = '\0') + if (*--p != ' ') break; /* strip trailing spaces */ + if (*buf) cmd = buf; /* use user entered command */ + if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!")) + cmd = (char *) 0; /* interactive escape */ + + vms_doshell(cmd, TRUE); + (void) sleep(1); + } +# endif /* SHELL */ +} + +# endif /* VMS */ + +# ifdef LAN_MAIL + +void +ckmailstatus() +{ + static int laststattime = 0; + + if(u.uswallow || !flags.biff +# ifdef MAILCKFREQ + || moves < laststattime + MAILCKFREQ +# endif + ) + return; + + laststattime = moves; + if (lan_mail_check()) { + static struct mail_info deliver = { +# ifndef NO_MAILREADER + MSG_MAIL, "I have some mail for you", +# else + /* suppress creation and delivery of scroll of mail */ + MSG_OTHER, "You have some mail in the outside world", +# endif + 0, 0 + }; + newmail(&deliver); + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ + lan_mail_read(otmp); +} + +# endif /* LAN_MAIL */ + +#endif /* OVL0 */ + +#endif /* MAIL */ + +/*mail.c*/