safern@aecom.UUCP (Eric Safern) (01/10/86)
Hello, I have received enough requests for this program to warrant, in my judgement, a new posting. I will do this next week as soon as I test Pacman on a system V machine. -- Eric Safern ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern
safern@aecom2.UUCP (Eric Safern) (03/14/86)
Hello, The following is a public domain, System V based version of pacman. It is based upon a Berkeley version of the same game posted a few months ago. I promised to post this in mid - January, when I finished it, but for the past two months I was unable to post due to problems involving a transfer to a new computer. Anyway, here it is, in two parts. It has been tested on Interactive System III, System V/68 and ATT System 5.2. Hopefully it will work an all system V and III machines, and with a minimum of work, on 4.X Berkeley. The last I'm not too sure about. I never tried it on a Berkeley system, so I couldn't really say. There are still some small problems. Firstly, sometimes when someone plays for the first time, it sits there for five or ten seconds before accepting commands. This only happens once in a while, and only when the person gets put in the user file for the first time. I have had some problems with turning on echo for the shell escape. I fixed this on system III by changing the curses library routine echo until it worked. I could not do this on system V because I couldn't get at the sources for curses under system V. Finally, I elected to not post the file "flsbuf.c" included in the earlier posting of pm, since it contains proprietary unix source for which I signed a non-disclosure agreement. The code in flsbuf.c contained _flsbuf(c, iop) from the stdio library, with a small change made to count the number of characters printed. This was done to allow for a delay after printing characters, to make the game smoother. If you compile the game and find it too `choppy', take the routine _flsbuf from the library, place it in flsbuf.c, and try to modify it to increment chcnt for each character printed. If you don't know how to do this, find an original posting containing the proprietary code. You may have some problems on system V, with the linker complaining about multiply defined procedures. I just left flsbuf.c out entirely, and it works fine. Don't forget to move tester to pm before you put it somewhere permanently, as it is in root mode if the game is called tester. Thanks to the original author, Peter Costantinidis, Jr. who wrote the Berkeley version. This is a pretty good game, quite well written. The biggest problem I had with it (other than the numerous berkeley dependencies) was the non-intelligence of the monsters. They are designed to only spot you if you are in their `line of sight'. If you simply stay out of their way, they will simply wander aimlessly. It seems to me that in the arcade version, they know where you are on the board, line of sight or not. Please send me a copy of any bug fixes, suggestions, etc. that you may have. -- Eric Safern ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern
safern@aecom2.UUCP (Eric Safern) (03/14/86)
#!/bin/sh : "This is a shell archive, meaning: " : "1. Remove everything above the #! /bin/sh line. " : "2. Save the resulting test in a file. " : "3. Execute the file with /bin/sh (not csh) to create the files:" : " READ_ME" : " Makefile" : " Makefile.b" : " TODO" : " pm.h" : " pm.6" : " shar" : " pm.c" : " random.c" : " rip.c" : " score.c" : " screen.c" : " timing.c" : " warning.c" : "This archive created: Fri Mar 14 01:19:33 EST 1986 " echo file: READ_ME sed 's/^X//' >READ_ME << 'END-of-READ_ME' XThis distribution is a System III/V version of pacman. XAll comments, etc. concerning this game should be directed to X XEric Safern X...{ihnp4,rocky2,philabs,esquire,cucard,pegasus}!aecom!safern X XHere is the readme from earlier distributions: X X XThis is the first source distribution of the game "pm". This game should Xrun under Version 7, BSD4.2, BSD4.3, BSD2.8, BSD2.9. A BSD4.3 version has Xnot been tested, but binary distribution bug reports have indicated some Xproblems in "flsbuf.c" that I think are now corrected. X XThis version still does busy looping. An implementation of msleep() was Xtried that used setitimer() for sleeping, but this did not work satisfactorly. XIf someone else is able to make a better version of msleep(), I would Xappreciate it if they could send me the source. However, thus far I have Xfound the current msleep() to be the most portable between different Xoperating system versions (it is the original UNIX Version 7 version). XNote the define "MYTIMER" turns on the busy looping and off the setitimer(2). X X XWed Oct 30 00:23:20 PST 1985 X XThis is the first binary distribution of the game "pm", a UNIX version Xof the arcade game Pacman. A source distribution will be made within Xthe next couple of months via netnews. Please send all bug reports to Xme (...!ucbvax!ucdavis!deneb!ccohesh001) so that they can be incorporated Xinto the first source distribution. X XThis binary distribution will only run under UNIX 4.2BSD. A binary for XUNIX 2.9BSD can be provided upon request. X XSince I am the main source of distribution, please mail all suggestions Xor comments to me. X X Peter Costantinidis, Jr. X ...!ucbvax!ucdavis!deneb!ccohesh001 X X XThe following may not make much sense unless you have a source distribution: X XMon Aug 30 11:18:09 PDT 1982 X XLook an pending(), it's sole purpose is to tell if the user has Xtyped anything, and if so, then read it, if not, then go on. If Xyou don't have a similar function, then you are screwed!!! X XThe purpose of delay() is to slow the CPU down so that the terminal can Xkeep up. I load in my own copy of flsbuf (stdio) that counts the number Xof characters that are printed. I make sure that the CPU waits long Xenough to make sure that they all got printed (a simple function of the Xbaud rate will tell you how long you need to wait). X XIf you want a list of commands, type in a '?'. If you want Xthe Wizard's commands, look in the define file. If you want the XWizard's password, decrypt the one in the define file or make your own. XIf (argv[0] == "tester") then you are wizard automatically (this Xshould probably be kept secret). X XPm "remembers" the uids of everyone who has played. These are maintained Xin the pm_user file by setting the bit corresponding to the uid. If a Xplayer has not played before, they are given an opening message (printed Xout by the inappropriately named function directions()), and will then Xnever see it again. X XIf you don't have curses, then give up! X XOne problem encountered during the development of this program was curses. XThe one I was using had problems and you may notice code that might not be Xuseful any longer (assuming your version of curses doesn't have these bugs). X XThe game looks best if the `maze' ('#' characters) are in inverse video. XIf your termcap defines standout to be high intensity, the board would Xprobably look best if you tweeked the code and made the maze normal (i.e. Xnot in standout) and everything else in high intensity (standout). X XI would like to thank Rick Heli, a member of UCD's Computer Center staff Xfor his help in the documentation of this program. END-of-READ_ME echo file: Makefile sed 's/^X//' >Makefile << 'END-of-Makefile' X# X# Makefile: makefile for pm X# X# Tunables: X# PM_ROLL - pathname of score file X# PM_USER - pathname of user log file X# OS_VERS - operating system version X# (BSD29, BSD41, BSD42, BSD43, SYSIII, SYSV) X# SEND - pathname of binary export directory X# X# Please note: on system 5.2 and any systems whose curses does not X# use termcap, you must comment out TERMLIB. X# X# [pm by Peter Costantinidis, Jr. @ University of California at Davis] X# X X# X# Files X# XHDRS = pm.h XOBJS = flsbuf.o init.o make_moves.o\ X misc.o monsters.o msg.o pm.o random.o\ X rip.o score.o screen.o timing.o warning.o XSRCS1 = config.c flsbuf.c init.c make_moves.c\ X misc.c monsters.c msg.c XSRCS2 = pm.c random.c rip.c score.c screen.c timing.c warning.c XSRCS = $(SRCS1) $(SRCS2) X X# X# Configuration section X# XPM_ROLL = /y1/safern/bin/lib/pm_roll XPM_USER = /y1/safern/bin/lib/pm_user XOS_VERS = SYSV XDEFS = -DPM_ROLL=\"$(PM_ROLL)\" -DPM_USER=\"$(PM_USER)\" \ X -DWIZARD_UID=283 XDEST = /y3/safern/bin/lib/xhesh X X# X# Flags section X# XCFLAGS = -O -D$(OS_VERS) XLDFLAGS = -o tester XLINTARGS = -ahxc $(DEFS) -DLINT XCRLIB = -lcurses XTERMLIB = -ltermcap # on ATT 5.2, this line must be commented out. X X# X# Misc. X# XPMLIB = pm.a XPMDOC = pm.6 XBEXPORT = READ_ME Makefile.b TODO $(PMLIB) $(PMDOC) config.c XALL = READ_ME Makefile Makefile.b TODO $(HDRS) $(SRCS) $(PMDOC) shar XREST = READ_ME Makefile Makefile.b TODO $(HDRS) $(PMDOC) shar XEX1 = $(REST) $(SRCS2) XEX2 = $(SRCS1) X Xtester: config.o $(OBJS) X -mv tester tester.old X cc $(LDFLAGS) $(OBJS) config.o $(CRLIB) $(TERMLIB) X chmod 04711 tester X Xconfig.o: $(HDRS) X cc $(CFLAGS) $(DEFS) -c config.c X Xinstall: pm pm_roll pm_user clean X Xclean: X rm $(OBJS) *.old X Xpm: tester X -mv pm pm.old; mv tester pm X# @strip pm X >tester X X$(OBJS): $(HDRS) X Xtags: $(HDRS) $(SRCS) X ctags -u $? X sort tags -o tags X Xpm_roll: X > $(PM_ROLL) X Xpm_user: X > $(PM_USER) X XPrint: $(HDRS) $(SRCS) X pr $? | print X -touch Print X XP_all: X pr $(HDRS) $(SRCS) | print X -touch Print X Xlint: X lint $(LINTARGS) $(SRCS) -lcurses X Xcount: X wc $(HDRS) $(SRCS) X Xbexport: $(PMLIB) X cp $(BEXPORT) $(DEST) X Xexport: X /bin/sh shar $(EX1) > pm1.shar X /bin/sh shar $(EX2) > pm2.shar X Xcrypt: pm.tar X crypt < pm.tar > pm.tar.c X XSave: $(HDRS) $(SRCS) X cp $? Makefile xsave X XCxref: $(SRCS) $(HDRS) X cxref $(SRCS) $(HDRS) > $@ END-of-Makefile echo file: Makefile.b sed 's/^X//' >Makefile.b << 'END-of-Makefile.b' X# X# Makefile.b - makefile for binary distribution X# X# The following macros must be redefined for your installation. X# X# [pm by Peter Costantinidis, Jr. @ University of California at Davis] X# XGROUP = sorcerer XOWNER = wizard XBINDEST = /usr/games XLIBDEST = /usr/games/lib XWIZARD_UID= 666 X# X# The following need not be changed. X# X XOBJS = config.o XSRCS = config.c XOS_VERS = BSD42 XPM_ROLL = $(LIBDEST)/pm_roll XPM_USER = $(LIBDEST)/pm_user XDEFS = -DPM_ROLL=\"$(PM_ROLL)\" -DPM_USER=\"$(PM_USER)\" \ X -D$(OS_VERS) -DWIZARD_UID=$(WIZARD_UID) XCFLAGS = -O $(DEFS) XLDFLAGS = -o pm XCRLIB = -lcurses XTERMLIB = -ltermlib XPMLIB = pm.a X Xall: pm X Xinstall: all pm_roll pm_user X cp pm $(BINDEST) X chgrp $(GROUP) $(BINDEST)/pm X chgrp $(GROUP) $(PM_ROLL) $(PM_USER) X chown $(OWNER) $(BINDEST)/pm X chown $(OWNER) $(PM_ROLL) $(PM_USER) X chmod 4711 $(BINDEST)/pm X Xpm: $(OBJS) X ranlib $(PMLIB) X cc $(LDFLAGS) $(OBJS) $(PMLIB) $(CRLIB) $(TERMLIB) X X$(OBJS): Makefile.b X XMakefile.b: X Xpm_roll: X > $(PM_ROLL) Xpm_user: X > $(PM_USER) X Xclean: X rm -f pm config.o X Xremove: clean X rm -f $(PM_ROLL) $(PM_USER) END-of-Makefile.b echo file: TODO sed 's/^X//' >TODO << 'END-of-TODO' X- Make the fruit appear and go away X- Fix the bug that sometimes causes wierd characters floating around X (something to do with the tunnel, the monster gets its mo_name mixed up) X (Note: haven't seen this one in quite a while, it might be fixed.) X- Possibly combine pm_roll and pm_user into one file X- Test on BSD4.3. END-of-TODO echo file: pm.h sed 's/^X//' >pm.h << 'END-of-pm.h' X/* X** pm.h - all pm source files include this X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X/* X** defines that control things X*/ X#define PATTERNS /* let there be patterns!!! */ X#define FASTER /* let the monsters move fast (monsters.c) */ X#define MAX_UPDATE /* more refreshes */ X X#if BSD42|BSD43|BSD29 X# define BAD_OVERLAY /* bug in curses (overlay.c) */ X#endif X X#if SYSIII|SYSV X# define ECHOBUG /* bug in curses (ttyctl.c) */ X#endif /* if you haven't got this problem, remove this */ X X#define MYTIMER /* quite reliable */ X X#define BYTE_SIZE 8 /* # of bits in a byte */ X X X#include <curses.h> X#include <ctype.h> X#include <sys/types.h> X X#ifdef MYTIMER X# undef ITIMER_REAL X#endif X X/* X** bound defines X*/ X#define TOP 1 X#define LEFT 0 X#define RIGHT 52 X X/* X** function status returns X*/ X#define ERROR -1 X#define STOP -2 X#define QUIT -3 X#define FINE -4 X#define BAD -5 X X/* X** input defines X*/ X#define MUP 'k' /* move up */ X#define MDOWN 'j' /* move down */ X#define MLEFT 'h' /* move left */ X#define MRIGHT 'l' /* move right */ X#define MSTOP ' ' /* stop movement */ X#define MKP_UP '8' /* key pad move up */ X#define MKP_DOWN '2' /* key pad move down */ X#define MKP_LEFT '4' /* key pad move left */ X#define MKP_RIGHT '6' /* key pad move right */ X#define MKP_STOP '5' /* key pad stop movement */ X#define MQUIT 'q' /* quit game */ X#define MREDRAW CTRL(L) /* redraw the screen */ X#define MSHELL '!' /* shell escape */ X#define MHELP '?' /* give list of commands */ X#define MFAST 'f' /* toggle looping in make_move */ X#define MQUIET 'b' /* toggle beeping */ X#define MPAUSE 'p' /* hang on getchar() */ X#define MHUH CTRL(R) /* reprint last message */ X#define MNULL '\0' X X/* X** special input defines (cheating) X*/ X#define MWIZARD CTRL(P) /* request to become wizard */ X#define MSTATUS 's' /* print debugging info */ X#define MMONS 'm' /* print monster "" "" */ X#define MPM '@' /* request for more pm's */ X#define MUP_LVL CTRL(U) /* up a level */ X#define MDN_LVL CTRL(D) /* down a level */ X#define MEAT CTRL(E) /* make them eatable */ X#define MMEAN CTRL(M) /* make them non-eatable */ X#define MSLOW 'r' /* change null padding */ X X/* X** wizardly defines X*/ X#define W_PASSWD "LuWBlzHTlqQ0g" X#define SALT "Lu" X X/* X** various characters on the screen X** the underscores mean the "submissive" ghost associated with * X*/ X#define PM '@' X#define HARPO 'H' X#define _HARPO 'h' X#define GROUCHO 'G' X#define _GROUCHO 'g' X#define ZEPPO 'Z' X#define _ZEPPO 'z' X#define CHICO 'C' X#define _CHICO 'c' X#define BLOCK '#' X#define DOT '.' X#define TUNNEL '-' X#define ENERGY '*' X#define EMPTY ' ' X#define DOOR '=' X X/* X** monster attributes X*/ X#define FAST 001 /* three different (relative) speeds */ X#define MED 002 X#define SLOW 004 X#define SMART 010 /* three different (relative) smarts */ X#define NORMAL 020 X#define DUMB 040 X X/* X** to keep lint quiet X** used primarily for the "Hit return to continue"'s X*/ X#ifdef LINT X# define trash(x) _trash_ = x X#else X# define trash(x) x X#endif X X/* X** miscellaneous definition X*/ X#define BELL '\07' X#define SPEED 15 X#define MIN_BAUD 1200 /* must alter bauds[] if this value is X ** changed */ X#if SYSV|SYSIII X# define EAT_PAUSE 20 /* used when pm is eaten */ X#else X# define EAT_PAUSE ((unss) 20) /* used when pm is eaten */ X#endif X X#define MAX_DIRS 4 X#define MAX_LEVEL 13 X#define MAX_ENERGY 4 X#define MAX_DOTS 208 X#define MAX_MONS 4 X#define MAX_PMS 4 X#define MAX_BLINKS 30 /* number of warning blinks */ X#define MAX_CNT 50 /* kind of useless, (see get_move) */ X#define TUNN_TIME 2 X#define TUNN_ROW 11 X#define DOOR_COL 26 /* column where door to monsters is */ X#define V_DOT 10 X#define V_ENERGY 50 X#define ALARM_TIME 10 /* messages get erased every 10 sec */ X#define BONUS 10000 /* get a free pm with 10000 score*/ X#define CMASK 0177 /* used to strip garbage from what X ** inch() returns (when in stand...) X */ X/* X** useful defines to make source more terse X*/ X#define when break; case X#define otherwise break; default X X#ifdef PATTERNS X# define SEED (uid+level) /* their uid */ X#else X# define SEED get_seed() /* their uid */ X#endif X X/* X** stuff to do with the score routines X*/ X#define DEFAULT_SH "/bin/sh" /* default shell */ X#define MAX_SCORES 10 /* maximum scores on pm roll */ X#define SCR_SIZE (sizeof(score)) X#define NAME_SIZE 50 /* biggest name allowed on roll */ X#define MODE 0644 /* the mode of the PM_ROLL */ X#define MAX_BYTES 4096 /* byte size of user file */ X#define MAX_USERS (MAX_BYTES*BYTE_SIZE)/* number of uid's (bit size of X ** PM_USER) X */ X X/* X** flag stuff for the above X*/ X#define FL_DIE 0 /* indicates he died */ X#define FL_QUIT 1 /* indicates he quit */ X X/* X** misc definitions X*/ X#define unss unsigned short X X/* X** structure definitions X*/ Xtypedef struct X{ X int x, y; X} coord; X Xtypedef struct /* monster description */ X{ X coord mo_pos; /* where it is at */ X char mo_inch; /* what it is on top of */ X bool mo_run, /* TRUE if it is eatable */ X mo_tunn, /* TRUE if it is in a tunnel */ X mo_eaten, /* TRUE if eaten */ X mo_inside; /* TRUE if inside */ X char mo_ch, /* current move */ X mo_name; /* name (letter) of monster */ X int mo_cnt, /* how many times to do mo_ch */ X mo_extunn, /* how long left in tunnel */ X mo_attrib; /* monsters characteristics */ X} mons; X Xtypedef struct X{ X int sc_uid; /* player's uid */ X long sc_score; /* player's score */ X int sc_level; /* how deep the player went */ X int sc_flags; /* misc. info */ X char sc_name[NAME_SIZE], /* player's name */ X sc_mons; /* monster's name */ X} score; X X/* X** psuedo functions X*/ X/* X** need to strip out the garbage that inch() returns, i only want X** the single character, and i think that it gets messed up when X** that character is in background X*/ X#define INCH() (inch() & CMASK) X#define abs(x) ((x) < 0 ? - (x) : (x)) X#define MVADDCH(p, c) mvaddch(p.y, p.x, c) X#define TF(x) (x ? "True" : "False") X#define DIST() rnd(4, 15) X#define IS_FRUIT(c) (c == fr_ch) X#define SLOWER() slow(FALSE) X#ifndef CTRL X# define CTRL(ch) ('ch' & '\037') X#endif X#define AT(pos1, pos2) (((pos1)->x == (pos2)->x) && ((pos1)->y == (pos2)->y)) X#define OUTOFTUNN(pos) (((pos)->y != 0) && ((pos)->y != 52)) X#define beep() putchar(BELL) X#define RN (((seed = (seed * 11109) + 13849) & 0xfff) >> 1) X#define randomize(i) seed = i X#define flush() raw(), noraw() X#define m_erase(mon) mvaddch(mon.mo_pos.y, mon.mo_pos.x, mon.mo_inch) X X#ifdef SYSV X# define doclear() refresh(), wclear(cls), wrefresh(cls); X#else X# define doclear() _puts(CL); X#endif X X#ifdef ECHOBUG X# define Echo() Necho() Xextern void Necho(); X#else X# define Echo() echo() X#endif X X#if SYSV|SYSIII X# include <fcntl.h> Xextern int oldfl, baud; Xextern long _tp; Xextern struct tbuffer garbage; X# define draw() _tp = times(&garbage), refresh(), delay() X#else Xextern int bauds[]; /* this is a character array on SYS3/V */ Xextern char baud; Xextern struct timeb _tp; X# define draw() ftime(&_tp), refresh(), delay() X#endif X X#ifdef SYSV Xextern WINDOW *cls; /* to clear the screen */ X#endif Xextern coord pm_pos; Xextern int pm_tunn, pm_extunn, d_left, e_left, level, fr_val, X fruit_val[], pms_left, pm_bonus, pm_eaten, pm_run, X mons_eaten, mons_val[], eat_times[], timer, was_wiz, is_wiz, X timeit, quiet, fast, uid, seed, wizard_uid; Xextern char *argv0, fruit[], fruit_eaten[], fr_ch, ch, oldch, X newch, moves[], *mesg, **environ, *pm_user, *pm_roll; X#ifdef LINT Xextern char _trash_; X#endif Xextern long thescore, hi_score, demon, move_cntr, chcnt; Xextern mons ghosts[]; Xextern mons *h, *g, *c, *z; Xextern char _putchar(), *crypt(), *strcpy(); Xextern int getuid(); Xextern off_t lseek(); X X/* X** local functions X*/ Xextern void add_fruit(), aggressive(), chg_lvl(), check_scrs(), X commands(), delay(), die(), directions(), X doadd(), draw_screen(), eat_pm(), init(), X m_eat_pm(), X mons_init(), m_move(), msg(), msg_erase(), msleep(), X mv_mon(), new_screen(), old_screen(), p_barriers(), p_dots(), X p_energizers(), p_fruits(), p_info(), p_monsters(), X p_pm(), p_pms(), p_scores(), place_m(), pm_eat_m(), X pmers(), X print_scrs(), quit_it(), quitit(), X re_msg(), redraw(), scores(), scrcpy(), X shell(), slow(), slowness(), status(), strucpy(), X submissive(), tombstone(), trap(), usage(), warning(); Xextern int _mv_mon(), can_see(), chk_pm_user(), dir_int(), get_move(), X is_mons(), is_safe(), m_is_safe(), X make_moves(), moveit(), move_to(), pending(), rnd(); Xextern char gen_mv(), *get_pass(), int_dir(), lturn(), *mons_str(), X opposite(), *punctrl(), rturn(), to_baud(), toletter(), X tunn_look(); Xextern long get_hi_scr(); X Xextern mons *wh_mons(); X X#ifdef PATTERNS Xextern int get_seed(); X#endif END-of-pm.h echo file: pm.6 sed 's/^X//' >pm.6 << 'END-of-pm.6' X.TH PM UCD "16 October 1985" X.SH NAME Xpm \- computer version of PacMan X.SH SYNOPSIS X.B pm X[ X.B -s X] [ X.B -p X] [ X.B -Bn X] X.SH OPTIONS X.TP X.B \-s XPrint out the list of scores. X.TP X.B \-Bn XSpecify the baud rate which you are using. X.TP X.B \-p XPrint list of players. X.SH DESCRIPTION X.I Pm Xis a UNIX version of the popular arcade game PacMan. XThe player, represented by the `@' symbol, attempts to score as many Xpoints as possible. He does this by eating the small dots on the Xscreeen, the energizers, the monsters and fruit. Each item provides Xa number of points as follows: X.sp X.nf X.ta 3i XDot(.) 10 XEnergizer(*) 50 XMonster varies XFruit(%,&,0,etc.) varies X.fi X.LP XOf course it would be simple if this was all there was to do. XUnfortunately, the pacman is chased by Groucho, Zeppo, Harpo and Chico Xwhich are represented by G, Z, H and C respectively. If they ever Xrun into the pacman, he is eaten and loses a life (you begin the game Xwith three). However, pacman can retaliate by eating an energizer. XWhen this happens, the characters representing the monsters become Xlower case and pacman will be able to eat them. But beware, the Xopportunity to do this is short. X.LP XThe points gained for eating monsters vary based on the number consecutively Xconsumed. This is probably related to the fact that the monsters Xtend to move more quickly as the game continues. XIn addition, fruit is worth more as the pacman continues. X.PP X.TP X.I List of Commands X.nf X.ta 3i X! exec a shell Xb toggle beeping Xf toggle speed Xh move left Xj move down Xk move up Xl move right Xp pause Xq quit XSPACE stop X.fi X.LP XPlayers will discover several interesting features in the game. For Xone, the monsters will become faster and more aggressive as the game Xgoes on. For another, players may notice that the monsters have their Xown personalities; it has been postulated that this is related to Xthe favorite machines of the author. X.LP XNormally the game does not generate beeps, but the player can cause X.I pm Xto beep whenever the pacman eats anything with the X.B b Xcommand. In addition, the game Xpauses after each cycle. These pauses may be shortened with the X.B f Xcommand. X.SH FILES X*/pm_roll list of the top ten X.br X*/pm_user list of who plays X.SH AUTHOR XPeter Costantinidis, Jr., University of California, Davis XSystem III, V port by Eric Safern, Yeshiva University, N.Y. X.SH DIAGNOSTICS XOccasionally complains when strange things seem to occur in the tunnel. X.SH BUGS X.I Pm Xis at less than its best at terminal speeds of less than 9600 Baud. XIn addition, the algorithm X.I pm Xuses to simulate real time is very cpu intensive, and thus the game is Xexpensive to play. END-of-pm.6 echo file: shar sed 's/^X//' >shar << 'END-of-shar' X#!/bin/sh Xecho "#!/bin/sh" Xecho ": \"This is a shell archive, meaning: \"" Xecho ": \"1. Remove everything above the #! /bin/sh line. \"" Xecho ": \"2. Save the resulting test in a file. \"" Xecho ": \"3. Execute the file with /bin/sh (not csh) to create the files:\"" Xfor i Xdo X echo ": \" $i\"" Xdone Xecho ": \"This archive created: " Xecho `date` \" Xfor i Xdo X echo "echo file: $i" X echo "sed 's/^X//' >$i << 'END-of-$i'" X sed 's/^/X/' $i X echo "END-of-$i" Xdone Xecho exit END-of-shar echo file: pm.c sed 's/^X//' >pm.c << 'END-of-pm.c' X/* X** pm.c - main: argument parsing, main control loop and usage messages X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include "pm.h" X Xmain (argc, argv) Xreg int argc; Xreg char **argv; X{ X argv0 = *argv++, argc--; X if (argc == 1) X { X if (*((*argv)++) != '-') X usage(); X switch (**argv) X { X case 'p': X pmers(); X exit(0); X case 's': X check_scrs(); X exit(0); X case 'B': X baud = to_baud(++*argv); X break; X default: X usage(); X } X } X else if (argc) X usage(); X if (!strcmp("tester", argv0)) X was_wiz = is_wiz = TRUE; X#ifdef NOFULLPATH X /* X ** at one time the author was worried about the whole world finding X ** out this games existed. to keep its location a little private X ** he tried to discourage full pathnames to the game from showing X ** up on a ps(1) output by forcing argv[0] to be "pm". X ** the following code checked for this condition. X */ X else if (getuid() != wizard_uid && strcmp("pm", argv0)) X { X fprintf(stderr, "That is a Big No NO!!!"); X setuid(getuid()); X if (getuid() != 0) X kill(0, 9); /* blast them out of the water */ X } X#endif X init(); X for (;;) X { X slow(); X demon++; X if (make_moves()) X break; X if (timer > 0) X timer--; X /* X ** we know we need a new board drawn when we are X ** out of dots and energizers X */ X p_scores(); X if (!e_left && !d_left) X { /* no more dots left */ X chg_lvl(1); X continue; X } X if (!timer && !pm_run) /* energizers ran out */ X { X aggressive(); X pm_run = TRUE; X continue; X } X if ((timer < MAX_BLINKS) && !(timer % 4)) X warning(); /* warn every four moves*/ X m_move(); /* move the monsters */ X } X quitit(); X X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X exit(0); X} X X/* X** usage() - print a usage message and exit X*/ Xvoid usage () X{ X fprintf(stderr, "Usage: %s [-s] [-p] [-Bn]\n", argv0); X exit(1); X} END-of-pm.c echo file: random.c sed 's/^X//' >random.c << 'END-of-random.c' X/* X** random.c - yet another random number generator and random seed maker X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include "pm.h" X X/* X** rnd() - return a number between a and b, inclusive X*/ Xint rnd (a, b) Xreg int a, b; X{ X return((RN % (abs(b - a) + 1)) + a); X} X X#ifndef PATTERNS X/* X** get_seed() - returns a seed for the random number generator X** dependent upon the time and date X*/ Xint get_seed () X{ X reg int seed; X reg struct tm *timestruct; X auto long clock; X extern long time(); X extern struct tm *localtime(); X X clock = time(0); X timestruct = localtime(&clock); X seed = timestruct->tm_sec + X timestruct->tm_min + X timestruct->tm_hour + X timestruct->tm_mday + X timestruct->tm_mon + X timestruct->tm_year + X timestruct->tm_yday; X return((int) ((seed + clock) % 32767)); X} X#endif END-of-random.c echo file: rip.c sed 's/^X//' >rip.c << 'END-of-rip.c' X/* X** rip.c - code dealing with the end of the game X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include "pm.h" X X/* X** die() - pm has died forever... X*/ Xvoid die (mon) Xreg char mon; X{ X doclear(); X tombstone(thescore, mon); X printf("[Press return to continue]"); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X endwin(); X trash(getchar()); X scores(mon, FL_DIE); X exit(0); X} X X/* X** quitit() - called when they quit X*/ Xvoid quitit () X{ X clear(); X move(LINES - 1, 0); X refresh(); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X printf("[Press return to continue]"); X endwin(); X trash(getchar()); X scores(NULL, FL_QUIT); X exit(0); X} X X/* X** tombstone() - print a pretty little pm X*/ Xstatic char *stone[] = X{ " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", X " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", X " @@@@@@ @@@@@", X " @@@@@ @@@@@@@@@@@@@ @@@@", X " @@@@@ @@@@@@@@@@@@@@@@@@@ @@@@", X " @@@@@ @@@@@ @@@@@ @@@@", X " @@@@@ @@@@ Eaten by @@@ @@@@@", X " @@@@@ @@@@ @@@ @@@@@", X " @@@@@ @@@ @@@ @@@@", X " @@@@ @@@@ @@@ @@@@", X " @@@@@ @@@ @@@ @@@@", X " @@@@@ @@@@ @@@ @@@@@", X " @@@@@ @@@@ @@@ @@@@@", X " @@@@ @@@@@ @@@@@ @@@@@@", X " @@@@@ @@@@@@@@@@@@@@@@@@@@@@@@", X " @@@@@ @@@@@@@@@@@@ @@@@", X " @@@@@", X " @@@@@", X " @@@@@", X " @@@@@", X " @@@@@@", X " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", X " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", X 0 X}; Xvoid tombstone (scr, monster) Xlong scr; Xreg char monster; X{ X reg char **s = stone; X X clear(); X move(0, 0); X while (*s) X printw("%s\n", *s++); X move(9, 33); X printw("%9s", mons_str(monster)); X move(18, 30); X printw("After getting %ld points.", scr); X move(LINES - 1, 0); X draw(); X} END-of-rip.c echo file: score.c sed 's/^X//' >score.c << 'END-of-score.c' X/* X** score.c - code dealing with maintaining the score file and the user log X** X** Note: the score file is not encrypted X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include "pm.h" X#include <pwd.h> X X#ifdef SYSIII X# include <sys/ioctl.h> X#endif SYSIII X Xextern int fwrite(), fread(); X X/* X** check_scrs() - print out the pm roll (only for score enquiries) X*/ Xvoid check_scrs () X{ X reg int i; X auto score scrs[MAX_SCORES]; X X if (rwscore(0, scrs)) X { X printf("No scores recorded at this time\n"); X return; X } X printf("\nTop Ten Players:\n"); X printf("Rank\tScore\tName\n"); X for (i = 0; i < MAX_SCORES && scrs[i].sc_uid != -1; i++) X { X print_scrs(&scrs[i], i + 1); X printf(".\n"); X } X} X X/* X** get_hi_scr() - get the highest score from the score file X*/ Xlong get_hi_scr () X{ X auto score scrs[MAX_SCORES]; X X if (rwscore(0, scrs)) X return(0L); X if (scrs[0].sc_uid == uid) X { /* taunt the high scorer */ X printf("\nWelcome back %s,\n", scrs[0].sc_name); X printf("do you think that you can do better than\n"); X printf("last time? Just a second while I think\n"); X printf("of a better strategy!\n"); X sleep(1); X#ifdef PATTERNS X /* X ** they seem to have this pattern mastered, so X ** lets be tricky and give them a different pattern X ** (i.e. change the seed) X */ X randomize(getpid()); X#endif X } X return(scrs[0].sc_score); X} X X/* X** chk_pm_user() - check the user file X** return non-zero on error X*/ Xint chk_pm_user () X{ X reg int i; X reg int bit, byte; X auto char buf[MAX_BYTES]; X X if (!pm_user || X uid > (unsigned) MAX_USERS) /* wraps on 16 bit machines */ X return(FALSE); X byte = uid / BYTE_SIZE; X bit = 1 << (uid % BYTE_SIZE); X for (i=0; i<MAX_BYTES; i++) X buf[i] = '\0'; X if (rwuser(0, buf)) X fprintf(stderr, "%s: creating user file\n", argv0); X if (!(buf[byte] & bit)) /* have they played before? */ X { X directions(); /* give directions and */ X buf[byte] |= bit; X if (rwuser(1, buf)) X { X fprintf(stderr, "%s: cannot update user file\n", argv0); X return(TRUE); X } X } X return(FALSE); X} X X/* X** print_scrs() - print out the score record X*/ Xvoid print_scrs (scr, rank) Xreg score *scr; Xreg int rank; X{ X static char *reason[] = X { X "eaten", X "quit", X }; X X if (scr->sc_uid < 0) X return; X printf("%d\t%6ld\t%s: %s after %d screen%c", X rank, scr->sc_score, scr->sc_name, X reason[scr->sc_flags], scr->sc_level+1, (scr->sc_level ? 's' : ' ')); X if (scr->sc_flags == FL_DIE) X printf(" by %s", mons_str(scr->sc_mons)); X} X X/* X** scores() - print the list of scores and conditionally add X** the new one X*/ Xvoid scores (mon, flags) Xchar mon; Xint flags; X{ X reg int i; X auto score scrs[MAX_SCORES]; X X if (rwscore(0, &(scrs[0]))) X { X fprintf(stderr, "%s: creating the score file\n", argv0); X for (i=0; i<MAX_SCORES; i++) X scrs[i].sc_uid = -1; /* indicates an invalid score */ X } X if (madeit(thescore, scrs)) X { X auto score newscr; X auto char buf[BUFSIZ]; X X printf("\nYou made it on the pm roll with "); X printf("your score of %ld!\n", thescore); X printf("Your name please: "); X#if SYSV|SYSIII /* doesn't seem to help much */ X ioctl(fileno(stdin), TCFLSH, 0); /* flush input */ X#endif X if (!gets(buf)) X printf("Sorry, that is not acceptable!\n"); X newscr.sc_score = thescore; X newscr.sc_uid = uid; X newscr.sc_level = level; X newscr.sc_mons = mon; X newscr.sc_flags = flags; X strucpy(newscr.sc_name, buf, NAME_SIZE); X newscr.sc_name[NAME_SIZE - 1] = NULL; X /* X ** find where to insert their score X */ X for (i = 0; i < MAX_SCORES && scrs[i].sc_uid != -1; i++) X { X auto score scr; X X if (thescore < scrs[i].sc_score) X continue; X scrcpy(&scr, &(scrs[i])); X scrcpy(&(scrs[i]), &newscr); X for (++i; i < MAX_SCORES; i++) X { X auto score tmp; X X scrcpy(&tmp,&(scrs[i])); X scrcpy(&(scrs[i]),&scr); X scrcpy(&scr,&tmp); X } X break; X } X if (scrs[i].sc_uid == -1) /* add at end of roll */ X scrcpy(&(scrs[i]), &newscr); X if (rwscore(1, scrs)) X fprintf(stderr, "%s: cannot write score file\n", argv0); X } X check_scrs(); X} X X/* X** madeit() - return true if the given score will make on the given roll X*/ Xstatic int madeit (l, scrs) Xreg long l; Xreg score *scrs; X{ X reg int i; X X if (!l || was_wiz) X return(FALSE); X for (i=0; i<MAX_SCORES; i++, scrs++) X if (scrs->sc_uid == -1) X return(TRUE); X else if (l > scrs->sc_score) X return(TRUE); X return(FALSE); X} X X/* X** pmers() - called by main() to print the list of players X*/ Xvoid pmers () X{ X auto char buf[MAX_BYTES]; X reg int j; X X if (rwuser(0, buf)) X { X perror(pm_user); X exit(1); X } X for (j = 0; j < MAX_BYTES; j++) X { X reg int i; X X if (!buf[j]) X continue; X for (i = 0; i < BYTE_SIZE; i++) X { X auto int puid; X struct passwd *pw; X extern struct passwd *getpwuid(); X X if (!((char) (1 << i) & buf[j])) X continue; X if (pw = getpwuid(puid = ((j * BYTE_SIZE) + i))) X printf("%s\n", pw->pw_name); X else X fprintf(stderr, "%s: getpwuid(%d) error\n", X argv0, puid); X } X } X} X X/* X** rwscore() - read/write the contents of the pm_roll file X** - if flag is true, write, else read X** - return non-zero on error X*/ Xstatic int rwscore (flag, scrs) Xreg int flag; Xreg score *scrs; X{ X reg FILE *fp; X reg int (*func)(); X X if (!(fp = fopen(pm_roll, flag ? "w" : "r"))) X return(TRUE); X func = flag ? fwrite : fread; X if ((*func)(scrs, sizeof(*scrs), MAX_SCORES, fp) != MAX_SCORES) X { X perror(pm_roll); X fclose(fp); X return(TRUE); X } X fclose(fp); X return(FALSE); X} X X/* X** rwuser() - read/write the contents of the pm_user file X** - if flag is true, write, else read X** - return non-zero on error X*/ Xstatic int rwuser (flag, buf) Xreg int flag; Xreg char *buf; X{ X reg FILE *fp; X reg int (*func)(); X X if (!(fp = fopen(pm_user, flag ? "w" : "r"))) X return(TRUE); X func = flag ? fwrite : fread; X if ((*func)(buf, sizeof(*buf), MAX_BYTES, fp) != MAX_BYTES) X { X perror(pm_user); X fclose(fp); X return(TRUE); X } X fclose(fp); X return(FALSE); X} END-of-score.c echo file: screen.c sed 's/^X//' >screen.c << 'END-of-screen.c' X/* X** screen.c - code dealing with display (most of what is written to the X** screen during the course of a game is done here) X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include "pm.h" X X/* X** p_draw_screen()- draw the board X** - this ordering is kind of important. the dots must X** be printed first and then the barriers in standout X** so that if the terminal uses "inverse video" for X** the so entry in /etc/termcap (or wherever) the X** paths through the maze are not in inverse video. X** - also, whenever the screen is cleared and dots() X** called, barriers() must be called again, or else X** dots() will overwrite the barriers. X** - the SLOWER() is so that the monsters don't move X** until the screen is totally redrawn X*/ Xvoid draw_screen () X{ X alarm(0); /* make sure the alarm is off */ X msg(""); X randomize(SEED); X clear(); X p_dots(); /* the dots must be printed first */ X/* standout(); */ X p_barriers(); X/* standend(); */ X p_scores(); X p_energizers(); X p_pm(); X p_pms(); X p_monsters(); X p_fruits(); X draw(); X sleep(1); X} X X/* X** new_screen() - called when a screen has been cleaned out X** X*/ Xvoid new_screen () X{ X reg int i; X X alarm(0); /* make sure the alarm is off */ X msg(""); X randomize(SEED); X#if !SYSV && !SYSIII X flush(); /* this totally destroys crmode on System III/V */ X#endif X mvaddch(pm_pos.y, pm_pos.x, EMPTY); X aggressive(); X for (i = 0; i < 4; i++) X m_erase(ghosts[i]); X draw(); X p_scores(); X p_dots(); X/* standout(); */ X p_barriers(); X/* standend(); */ X p_energizers(); X p_pm(); X p_monsters(); X p_fruits(); X draw(); X sleep(2); X} X X/* X** old_screen()- called when pm is eaten X*/ Xvoid old_screen () X{ X reg int i; X X alarm(0); /* make sure the alarm is off */ X msg(""); X#if !SYSV && !SYSIII X flush(); /* this totally destroys crmode on System III/V */ X#endif X mvaddch(pm_pos.y, pm_pos.x, EMPTY); X aggressive(); X for (i = 0; i < MAX_MONS; i++) X m_erase(ghosts[i]); X draw(); X p_scores(); X p_pms(); X p_pm(); X p_monsters(); X draw(); X sleep(1); X} X X/* X** redraw() - redraw the screen X*/ Xvoid redraw () X{ X alarm(0); /* make sure the alarm is off */ X clearok(stdscr, TRUE); X msg(""); X draw(); X sleep(1); X} X X/* X** p_scores() - print the score and high score if changed X** - check to see if they get another pm X** - must remember to print both scores when the X** game is first started X*/ Xvoid p_scores () X{ X static long _score = -1L; X X if (thescore == _score) X return; /* the posted score is accurate */ X if ((thescore > BONUS) && pm_bonus) X { X pm_bonus = FALSE; X pms_left++; X p_pms(); X } X move(0, 23); X printw("%06ld", thescore); X if (thescore > hi_score || _score == -1L) X { X move(0, 47); X printw("%06ld", X (thescore>hi_score) ? (hi_score=thescore) : hi_score); X } X _score = thescore; X} X X/* X** p_fruits() - place the fruit and sets its value X*/ Xvoid p_fruits () X{ X reg int lvl = (level >= MAX_LEVEL ? MAX_LEVEL-1 : level); X X fr_ch = fruit[lvl]; X fr_val = fruit_val[lvl]; X move(13, 26); X addch(fr_ch); X} X X/* X** add_fruit() - add eaten fruit to fruit list X** - have to shift things over some X*/ Xvoid add_fruit (fr) Xreg char fr; X{ X reg int i; X X for (i = 6; i > 0; i--) X fruit_eaten[i * 2] = fruit_eaten[(i - 1) * 2]; X fruit_eaten[0] = fr; X move(0, 55); X printw("%s", fruit_eaten); X} X X/* X** p_pm() - place the pm in its starting position X*/ Xvoid p_pm () X{ X pm_tunn = FALSE; X pm_pos.x = 26; X pm_pos.y = 17; X move(pm_pos.y, pm_pos.x); X addch(PM); X} X X/* X** p_pms() - place the spare pm's X*/ Xvoid p_pms () X{ X reg int i; X X for (i = 1; i < MAX_PMS; i++) X { X move(0, (2 * (i - 1))); X if (i >= pms_left) X addch(EMPTY); X else X addch(PM); X } X} X X/* X** p_energizers() - put in the energizers X*/ Xvoid p_energizers () X{ X move(4, 51); X addch(ENERGY); X move(4, 1); X addch(ENERGY); X move(17, 1); X addch(ENERGY); X move(17, 51); X addch(ENERGY); X e_left = MAX_ENERGY; X} X Xstatic char *_board[] = X{ X "#####################################################\n", X "# ##### #\n", X "# ######### ########### ##### ########### ######### #\n", X "# ######### ########### ##### ########### ######### #\n", X "# #\n", X "# ######### ##### ################# ##### ######### #\n", X "# ##### ##### ##### #\n", X "########### ########### ##### ########### ###########\n", X "# # ##### ##### # #\n", X "########### ##### ######## ######## ##### ###########\n", X " # # \n", X "########### ##### ################# ##### ###########\n", X "# # ##### ##### # #\n", X "########### ##### ################# ##### ###########\n", X "# ##### #\n", X "# ######### ########### ##### ########### ######### #\n", X "# ##### ##### #\n", X "##### ##### ##### ################# ##### ##### #####\n", X "# ##### ##### ##### #\n", X "# ##################### ##### ##################### #\n", X "# ##################### ##### ##################### #\n", X "# #\n", X "#####################################################\n", X 0 X}; X X/* X** p_barriers() - fills in the board X*/ Xvoid p_barriers () X{ X static int once = TRUE; X static WINDOW *tmp; X X if (once) X { X reg char **str = _board; X X if ((tmp = newwin(0, 0, 0, 0)) == (WINDOW *) ERR) X { X move(0, 0); X printw("barriers(): newwin() error"); X draw(); X quit_it(); X } X wmove(tmp, TOP, 0); X while (*str) X waddstr(tmp, *str++); X once = FALSE; X } X overlay(tmp, stdscr); X} X Xstatic char *_dots[] = X{ X " \n", X " . . . . . . . . . . . . . . . . . . . . . . . . \n", X " . . . . . . \n", X " . . . . . . \n", X " . . . . . . . . . . . . . . . . . . . . . . . . . . \n", X " . . . . . . \n", X " . . . . . . . . . . . . . . . . . . . . \n", X " . . \n", X " . . \n", X " . = . \n", X "- . . -\n", X " . . \n", X " . . \n", X " . . \n", X " . . . . . . . . . . . . . . . . . . . . . . . . \n", X " . . . . . . \n", X " . . . . . . . . . . . . . . . . . . . . \n", X " . . . . . . \n", X " . . . . . . . . . . . . . . . . . . . . \n", X " . . . . \n", X " . . . . \n", X " . . . . . . . . . . . . . . . . . . . . . . . . . . \n", X " \n", X 0 X}; X X/* X** p_dots() - fills in the board X*/ Xvoid p_dots () X{ X reg char **str = _dots; X X d_left = MAX_DOTS; X move(TOP, 0); X while (*str) X addstr(*str++); X} END-of-screen.c echo file: timing.c sed 's/^X//' >timing.c << 'END-of-timing.c' X/* X** timing.c - functions dealing with the "smooth" running of the game X** it is important not for the game to get ahead of the screen X** and the necessary `slowing down' of the game is done here X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include <signal.h> X#include "pm.h" X X#define BPBYTE 10 /* bits sent to termnal per character (byte) */ X#define PAWS(x) ((1000 * x * BPBYTE) / baud) X#define TIMEBS(x,y) ((x - y) * 100 / 6) X/* X** delay() - coordinate with tty speed X** / 1000 ms. \ X** | ------------------------------------ * delta(chars) | == delay in ms. X** \ (baud bits/sec)/(BPBYTE bits/char) / X*/ Xvoid delay () X{ X int u; X auto long tp; X X u = PAWS(chcnt); X for(;;) { X tp = times(&garbage); X if (TIMEBS(tp, _tp) >= u) { X chcnt = 0L; X return; X } X } X} X Xstatic int rates[] = /* these were `tuned' after much playing */ X{ X/* 0 1 2 3 4 5 6 7 8 9 */ X 320, 265, 220, 210, 200, 190, 180, 170, 160, 150, X/* 10 11 12 13 14 15 16 17 18 19 */ X 140, 130, 120, 110, 100, 100, 100, 90, 90, 80, X/* 20 21 22 23 24 25 26 27 28 29 */ X 95, 70, 45, 20, 100, 150, 50, 99, 100, 100, X/* 30 31 32 33 34 35 36 37 38 39 */ X 5, 0, 0, 0, 0, 20, 0, 0, 0, 0, X/* 40 41 42 43 44 45 46 47 48 49 */ X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X/* 50 51 52 53 54 55 56 57 58 59 */ X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, X}; X X/* X** slow() - make the game go faster as they go deeper X** - assuming that they WILL NOT GET DEEPER THAN X** 60 LEVELS!!!! if they are, then they have been X** tying up the computer long enough and should stop X** playing anyway X*/ Xstatic long tp; X Xvoid initslow() X{ X tp = times(&garbage); X} X Xvoid slow() X{ X reg int ms, num; X auto long tp2; X X if (level > 59) X quitit(); X num = (fast ? rates[level]/4 : rates[level]/2); X for(;;) { X tp2 = times(&garbage); X if (TIMEBS(tp2, tp) >= num) { X tp = tp2; X return; X } X } X} X X/* X** slowness() - sets delay in rates X** X*/ Xvoid slowness () X{ X auto char buf[BUFSIZ]; X X doclear(); X nocrmode(); X printf("old delay: %d, new delay: ", rates[level]); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X Echo(); /* defined to echo() on machines without bug */ X if (!gets(buf)) X msg("EOF in slowness"); X if (buf[0]) X if (sscanf(buf, "%d", &(rates[level])) == EOF) X msg("EOF2 in slowness"); X#if SYSV|SYSIII X fcntl(0, F_SETFL, O_NDELAY); X#endif X noecho(); X crmode(); X redraw(); X} X END-of-timing.c echo file: warning.c sed 's/^X//' >warning.c << 'END-of-warning.c' X/* X** warning.c - code dealing with the energizers and them being eaten and X** wearing out and informing the player his time is ending X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include "pm.h" X X/* X** warning() - warn pm that energizers are about to wear off X*/ Xvoid warning () X{ X reg int i; X X for (i = 0; i < MAX_MONS; i++) X { X if (!ghosts[i].mo_run) X continue; X if (!ghosts[i].mo_tunn) X mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, X toupper(ghosts[i].mo_name)); X } X draw(); X /* X msleep(10l); X */ X for (i = 0; i < MAX_MONS; i++) X { X if (!ghosts[i].mo_run) X continue; X if (!ghosts[i].mo_tunn) X mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, X ghosts[i].mo_name); X } X draw(); X} X X/* X** aggressive() - perform all the house keeping when the enegizers X** wear off the pm X*/ Xvoid aggressive () X{ X reg int i; X X mons_eaten = -1; X timer = 0; /* reset the timer */ X for (i = 0; i < MAX_MONS; i++) X { X ghosts[i].mo_run = FALSE; X if (islower(ghosts[i].mo_name)) X ghosts[i].mo_name = toupper(ghosts[i].mo_name); X else X continue; X if (!ghosts[i].mo_tunn) X mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, ghosts[i].mo_name); X } X} X X/* X** submissive() - make the ghosts eatable X*/ Xvoid submissive () X{ X reg int i; X X if (level >= MAX_LEVEL) X timer = eat_times[MAX_LEVEL - 1]; X else X timer = eat_times[level]; X pm_run = FALSE; X mons_eaten = -1; X for (i = 0; i < MAX_MONS; i++) X { X ghosts[i].mo_run = TRUE; X if (isupper(ghosts[i].mo_name)) X ghosts[i].mo_name = tolower(ghosts[i].mo_name); X else X continue; X if (!ghosts[i].mo_tunn) X mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, ghosts[i].mo_name); X } X} END-of-warning.c exit -- Eric Safern ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern
safern@aecom2.UUCP (Eric Safern) (03/14/86)
#!/bin/sh : "This is a shell archive, meaning: " : "1. Remove everything above the #! /bin/sh line. " : "2. Save the resulting test in a file. " : "3. Execute the file with /bin/sh (not csh) to create the files:" : " config.c" : " flsbuf.c" : " init.c" : " make_moves.c" : " misc.c" : " monsters.c" : " msg.c" : "This archive created: Fri Mar 14 01:19:51 EST 1986 " echo file: config.c sed 's/^X//' >config.c << 'END-of-config.c' X/* X** config.c - installation dependent parameters X** X** PM_ROLL: full pathname of score file X** X** PM_USER: full pathname of user log file. used to keep X** track of who has played X** X** WIZARD_UID: if argv[0] == "tester" and getuid() = WIZARD_UID X** then game runs in diagnostic mode where special X** commands take effect X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ Xchar *pm_roll = PM_ROLL, /* score file */ X#ifdef PM_USER X *pm_user = PM_USER; /* user file */ X#else X *pm_user = NULL; /* no user file */ X#endif X#ifdef WIZARD_UID Xint wizard_uid = WIZARD_UID; X#else Xint wizard_uid = -1; X#endif END-of-config.c echo file: flsbuf.c sed 's/^X//' >flsbuf.c << 'END-of-flsbuf.c' X/* empty file */ END-of-flsbuf.c echo file: init.c sed 's/^X//' >init.c << 'END-of-init.c' X/* X** init.c - game initializations X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X X#include "pm.h" X X#if SYSV|SYSIII X X# ifdef SYSIII X# include <sys/ioctl.h> X# endif SYSIII X X# ifdef SYSV XWINDOW *cls; X# endif SYSV X Xstruct termio tty; Xunss btmp = 0; /* temporary variable for symbolic baud */ Xint oldfl, baud = 0; Xlong _tp; /* used for timing */ Xstruct tbuffer { X long utime; X long stime; X long cutime; X long cstime; X} garbage; X#else Xstruct timeb _tp; /* used for timing */ X#endif Xcoord pm_pos; Xlong thescore = 0L, /* player's score */ X hi_score = 0L, /* high score so far */ X move_cntr = 0L, /* # of moves made by player */ X chcnt = 0L, /* character count */ X demon = 0L; /* # of loops game made (psuedo time) */ Xchar fr_ch, /* fruit character */ X ch = ' ', /* current move of pm */ X oldch = '\0', /* old (temporary) move */ X newch = '\0', /* new move (future) */ X X#if !SYSV && !SYSIII X baud = '\0', /* output baud rate of terminal */ X#endif X X *argv0, /* argv[0] */ X *mesg; /* pointer to last message */ Xint timeit = FALSE, /* printing loop/move counter? */ X quiet = TRUE, /* bells and whistles */ X fast = FALSE, /* skip senseless looping */ X timer = 0, /* duration timer for energizers */ X level = 0, /* level (board) number */ X seed, /* rnd num seed */ X fr_val, /* fruit value */ X d_left = MAX_DOTS, /* number of dots left on board */ X e_left = MAX_ENERGY, /* number of energizers left on board */ X mons_eaten = -1, /* number of monsters eaten (<= 4) */ X pm_eaten = FALSE, /* got eaten */ X pms_left = 3, /* pm's left (you start with three) */ X pm_bonus = TRUE, /* can get a bonus pm */ X pm_run = TRUE, /* TRUE if eatable */ X pm_tunn = FALSE, /* " if in tunnel */ X pm_extunn, /* how long left in tunnel */ X is_wiz = FALSE, /* TRUE if currently wizard */ X was_wiz = FALSE, /* TRUE if ever was wizard */ X uid; /* user's uid */ Xmons ghosts[MAX_MONS], /* array of monsters */ X *h, *g, *c, *z; /* pointers into array of monsters */ Xchar fruit[] = "%&00++$$~~^^_", X fruit_eaten[15] = " ", X moves[] = "hjkl"; Xint fruit_val[] = /* the values of each succeeding fruit */ X{ X 100, 300, 500, 500, 700, 700, 1000, 1000, 2000, 2000, 3000, 3000, 5000 X}; Xint mons_val[] = /* the values of the monsters when eaten */ X{ X 200, 400, 800, 1600 X}; Xint eat_times[] = /* the duration of the power pill */ X{ X 100, 95, 90, 75, 90, 85, 85, 85, 75, 70, 65, 55, 45 X}; X X#if !SYSV && !SYSIII Xint bauds[] = X{ X 0, 0, 0, 0, 0, 0, 0, 0, 0, 1200, 1800, 2400, 4800, 9600, 19200, 0 X}; X#endif X X/* X** init() - perform necessary intializations X*/ Xvoid init () X{ X if ((uid = getuid()) < 0) X { X fprintf(stderr, "Who the hell are you???\n"); X exit(1); X } X randomize(SEED); X initslow(); /* set the time for the first call to slow */ X X if (pm_user) X if (chk_pm_user()) /* check user log file */ X fprintf(stderr, "%s: Can not make entry into user log file\n", X argv0); X X#if SYSV|SYSIII X oldfl = fcntl(0, F_GETFL); X fcntl(0, F_SETFL, O_NDELAY); X#endif X X hi_score = get_hi_scr(); X if (initscr() == (WINDOW *) ERR) X { X fprintf(stderr, "initscr() error\n"); X perror(argv0); X X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X X exit(1); X } X# ifdef SYSV X cls = newwin(0, 0, 0, 0); X# endif SYSV X if (!baud) /* if baud, then we are trying to simulate another */ X { X X#if SYSV|SYSIII X if ((ioctl(fileno(stdout), TCGETA, &tty)) == -1) { X perror("pm: "); X endwin(); X fcntl(0, F_SETFL, oldfl); X exit(1); X } X switch(btmp = (tty.c_cflag & CBAUD)) { X case B0: X case B50: X case B75: X case B110: X case B134: X case B150: X case B200: X case B300: X case B600: X fprintf(stderr, "pm: baud rate must be at least %d\n", MIN_BAUD); X endwin(); X fcntl(0, F_SETFL, oldfl); X exit(1); X case B1200: X baud = 1200; X break; X case B1800: X baud = 1800; X break; X case B2400: X baud = 2400; X break; X case B4800: X baud = 4800; X break; X case B9600: X baud = 9600; X break; X } X X#else X if (!bauds[_tty.sg_ospeed]) X { X fprintf(stderr, "pm: baud rate must be at least %d(%d)\n", MIN_BAUD, _tty.sg_ospeed); X endwin(); X exit(1); X } X baud = _tty.sg_ospeed; X#endif X X } X trap(0); X h = &ghosts[0]; X g = &ghosts[1]; X c = &ghosts[2]; X z = &ghosts[3]; X mons_init(); X crmode(); X noecho(); X mesg = NULL; X draw_screen(); X} X X/* X** mons_init() - initialize the monsters X** - MUST be called before monsters()!!! X** Note: I am sure that there was a reason why I did not statically X** initialize these structures. When I remember the reason X** I will mention it here at a later date. X*/ Xvoid mons_init () X{ X reg int i; X X h->mo_attrib = SMART | SLOW; X g->mo_attrib = SMART | FAST; X c->mo_attrib = NORMAL | FAST; X z->mo_attrib = DUMB | MED; X h->mo_name = HARPO; X g->mo_name = GROUCHO; X c->mo_name = CHICO; X z->mo_name = ZEPPO; X for (i = 0; i < 4; i++) X m_init(&ghosts[i]); X g->mo_inside = FALSE; X} X X/* X** m_init() - initialize a single monster X** - this function is called from p_monsters() (every time a X** new screen is entered) X*/ Xm_init (m) Xreg mons *m; X{ X m->mo_inch = EMPTY; X m->mo_run = FALSE; X m->mo_tunn = FALSE; X m->mo_eaten = FALSE; X m->mo_inside = TRUE; X m->mo_ch = ' '; X m->mo_cnt = 0; X m->mo_extunn = 0; X} END-of-init.c echo file: make_moves.c sed 's/^X//' >make_moves.c << 'END-of-make_moves.c' X/* X** make_moves - code relating to player movement X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include "pm.h" X#ifdef SYSIII X# include <sys/ioctl.h> X#endif SYSIII X X/* X** make_moves - `ch' is the global variable designating the move of the X** player. perhaps it would have been better to pass this X** character to this function instead of using a global. X*/ Xint make_moves () X{ X reg char what; X reg int quit = FALSE; X auto coord tmp_pos; X X if (pm_tunn) X { /* in tunnel */ X if (pm_extunn--) X { /* still in tunnel, move over a square */ X switch (ch) X { X when MLEFT: X if (--pm_pos.x < LEFT) X pm_pos.x = RIGHT; X when MRIGHT: X if (++pm_pos.x > RIGHT) X pm_pos.x = LEFT; X otherwise: X msg("case error in tunnel"); X } X /* X ** check for monsters here!!!! X ** to see if they have run into any in the tunnel X */ XAbove: /* sorry about this! */ X switch (what = tunn_look(&pm_pos)) X { X case EMPTY: X return(msg("In and out of tunn"),quit); X case TUNNEL: X return(quit); X case PM: /* nothing here but me! */ X return(quit); X } X if (!is_mons(what)) X return(msg("found: %s in tunn", punctrl(what)),quit); X if (islower(what)) /* we have caught a monster */ X return(pm_eat_m(what), quit); X if (isupper(what)) X { X pm_eaten = TRUE; X m_eat_pm(wh_mons(what)); X return(quit); X } X msg("What was that???"); X } X else X { X pm_tunn = FALSE; X tmp_pos.x = pm_pos.x; X tmp_pos.y = pm_pos.y; X newch = ch; X goto here; X } X } X else X mvaddch(pm_pos.y, pm_pos.x, ' '); X if (pending()) X { X#if !SYSV && !SYSIII X newch = getchar(); X#endif X if (isupper(newch)) X newch = tolower(newch); X else if (isdigit(newch)) X newch = toletter(newch); X move_cntr++; X } X if (newch) X { X oldch = ch; X ch = newch; X } X if (!ch) X return(quit); Xtop: X tmp_pos.x = pm_pos.x; X tmp_pos.y = pm_pos.y; X switch (moveit(ch, &tmp_pos)) X { X when FINE: X when ERROR: X ch = oldch; X oldch = '\0'; X newch = '\0'; X goto top; X when QUIT: X quit = TRUE; X } Xhere: X if (is_safe(&tmp_pos)) X pm_pos.x = tmp_pos.x, pm_pos.y = tmp_pos.y; X else if (pm_eaten) X return(0); X else if (pm_tunn) X goto Above; X else X { X#ifdef USELESS X/* X** maybe the next `if' statement should have been an `else if', X** but until that has been ascertained, this one is useless X*/ X if ((newch == ch) && oldch) X { X ch = oldch; X oldch = '\0'; X goto top; X } X#endif X if (oldch) X { X ch = oldch; X oldch = '\0'; X goto top; X } X } X /* X ** redraw pm and leave cursor there if not in tunnel X */ X if (!pm_tunn) X { X move(pm_pos.y, pm_pos.x); X addch(PM); X move(pm_pos.y, pm_pos.x); X } X draw(); X return(quit); X} X X/* X * pending() - return TRUE if the user wants service, else return FALSE X * - i realize that this function could be simpler (ex. just X * return((int) l)), but i wanted to minimize lint's complaints. X * on SYSTEM V, actually does a read into newch if possible X */ Xint pending () X{ X X#if !SYSV && !SYSIII X auto long l; X X if (ioctl(0, FIONREAD, &l) == -1) X return(FALSE); X return(l > 0 ? TRUE : FALSE); X#else X return(read(0, &newch, 1)); X X/* X * This must be done elsewhere, otherwise the read hangs. X * if this is done, the read returns a zero if nothing pending, X * if there is a char, it's put in ch and a one is returned. X * oldfl=(fcntl(0, F_GETFL)); X * fcntl(0, F_SETFL, O_NDELAY); X * X * Also, in die, include this: X * fcntl(0, F_SETFL, oldfl); X */ X#endif X} X X/* X** is_safe() - returns TRUE if location is safe X** - also assumes that move will be made regardless X** of safeness X*/ Xint is_safe (where) Xreg coord *where; X{ X reg char what; X X move(where->y, where->x); X what = INCH(); X move(pm_pos.y, pm_pos.x); X switch (what) X { X case BLOCK: X case DOOR: X return(FALSE); X case TUNNEL: X pm_tunn = TRUE; X pm_extunn = TUNN_TIME; X return(TRUE); X case DOT: X thescore += V_DOT; X d_left--; X if (!quiet) X beep(); X return(TRUE); X case ENERGY: X thescore += V_ENERGY; X e_left--; X if (!quiet) X beep(); X submissive(); X return(TRUE); X case EMPTY: X return(TRUE); X case PM: X msg("I'm going skitzo"); X return(FALSE); X } X if (IS_FRUIT(what)) /* check to see if it is a fruit */ X { X thescore += fr_val; X if (!quiet) X beep(); X fr_val = 0; /* shows fruit has been eaten */ X add_fruit(fr_ch); X return(TRUE); X } X#ifdef DEBUG X if (!is_mons(what)) X return(msg("found a %s in @ 226", punctrl(what)), FALSE); X#endif X if (islower(what)) /* we have caught a monster */ X { X#ifdef DEBUG X if (pm_run) /* remove message later on### */ X msg("Eatable, but not running"); X /* X ** may need this X pm_pos.x = where->x; X pm_pos.y = where->y; X */ X#endif X return(pm_eat_m(what), TRUE); X } X pm_eaten = TRUE; X pm_pos.x = where->x; X pm_pos.y = where->y; X m_eat_pm(wh_mons(what)); X return(FALSE); X} X X/* X** pm_eat_m() - the pm ate the m!!! X** - the variable flag is used to indicate that the X** monsters (including the one eaten) must become X** submissive (after the eaten one has been initialized) X*/ Xvoid pm_eat_m (who) Xreg char who; X{ X reg mons *m; X reg int flag = FALSE; X X thescore += mons_val[++mons_eaten]; X if (mons_eaten == 3) X { /* all the monsters are eaten, reset the timer */ X timer = 0; X mons_eaten = -1; X } X if (!(m = wh_mons(who))) X { X msg("Lost monster in pm_eat_m()"); X return; X } X if (!quiet) X beep(); X switch (m->mo_inch) /* check what was underneath him*/ X { X when DOT: X thescore += V_DOT; X d_left--; X when ENERGY: X flag = TRUE; X thescore += V_ENERGY; X e_left--; X } X m->mo_name = toupper(who); X m_init(m); X place_m(m); X m->mo_eaten = TRUE; X if (flag) X submissive(); X} X X/* X** moveit() - evaluate move and return status X*/ Xmoveit (what, where) Xreg char what; Xreg coord *where; X{ X switch (what) X { X case MUP: X return(where->y--, FINE); X case MDOWN: X return(where->y++, FINE); X case MLEFT: X return(where->x--, FINE); X case MRIGHT: X return(where->x++, FINE); X case MSTOP: X return(STOP); X case MQUIT: X return(QUIT); X case MREDRAW: X return(redraw(), ERROR); X case MSHELL: X return(shell(), ERROR); X case MHELP: X return(commands(), ERROR); X case MFAST: X return(fast = !fast, ERROR); X case MQUIET: X return(quiet = !quiet, ERROR); X case MPAUSE: X /* X ** they are not allowed to pause to examine X ** the board (for potential moves), so clear X ** the screen (and go to the bottom) while X ** they are paused X */ X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X if (is_wiz) { /* wizard is an exception! */ X trash(getchar()); X#if SYSV|SYSIII X fcntl(0, F_SETFL, O_NDELAY); X#endif X return(ERROR); X } X move(LINES - 1, 0); X draw(); X doclear(); X printf("[Press return to continue]"); X trash(getchar()); X redraw(); X#if SYSV|SYSIII X fcntl(0, F_SETFL, O_NDELAY); X#endif X return(ERROR); X case MHUH: X return(re_msg(), ERROR); X case MWIZARD: X if (is_wiz) X { X msg(""); X is_wiz = FALSE; X return(ERROR); X } X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X msg("Wizard's Password: "); X if (!strcmp(W_PASSWD, crypt(get_pass(), SALT))) X { X was_wiz = TRUE; X is_wiz = TRUE; X if (getuid() != wizard_uid) X msg("Are you trying to cheat?"); X else X msg("Hi wiz!"); X } X else X msg("Who are you kidding?"); X#if SYSV|SYSIII X fcntl(0, F_SETFL, O_NDELAY); X#endif X return(ERROR); X default: X if (!is_wiz) X return(ERROR); X } X /* X ** since they are wizard, lets try some of these X */ X switch (what) X { X when MPM: X pms_left++, p_pms(); X when MSLOW: X slowness(); X when MSTATUS: X status(); X when MMONS: X X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl), X p_info(getchar()), X fcntl(0, F_SETFL, O_NDELAY); X#else X p_info(getchar()), X#endif X X when MUP_LVL: X chg_lvl(1); X when MDN_LVL: X chg_lvl(-1); X when MEAT: X submissive(); X when MMEAN: X aggressive(); X } X return(ERROR); X} X X/* X** commands() - print a list of the users commands X** - erase the screen by hand X*/ Xvoid commands () X{ X static char *cmds[] = X { "---------------------------------------------------", X "| Movement: | Misc: |", X "---------------------------------------------------", X "| | |", X "| k | ! shell |", X "| ^ | q quit |", X "| ^ | <SPACE> stop |", X "| ^ | f faster |", X "| ^ | b quiet |", X "| h < < < < * > > > > l | p pause |", X "| v | |", X "| v | |", X "| v | |", X "| v | |", X "| j | |", X "| | |", X "---------------------------------------------------", X NULL X }; X reg char **s = cmds; X X doclear(); X while (*s) X printf("%s\n", *s++); X printf("[Press return to continue]"); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X trash(getchar()); X fcntl(0, F_SETFL, O_NDELAY); X#else X trash(getchar()); X#endif X chcnt = 0L; X redraw(); X} X X/* X** status() - print out a bunch of debugging info X*/ Xvoid status () X{ X alarm(0); X doclear(); X move(0, 0); X printf(" Diagnostics\n\n"); X printf("Fruit: %c\n", fr_ch); X printf("Fruit value: %d\n", fr_val); X printf("Level: %d\n", level); X printf("Moves: %ld\n", move_cntr); X printf("Time: %ld\n", demon); X printf("Timeit: %s\n", (timeit ? "Yes" : "No")); X printf("Fast: %s\n", (fast ? "Yes" : "No")); X printf("Beeping: %s\n", (quiet ? "No" : "Yes")); X printf("Dots left: %d\n", d_left); X printf("Energizers left: %d\n", e_left); X printf("Pm's left: %d\n", pms_left); X printf("Time left: %d\n", timer); X printf("Score: %ld\n", thescore); X printf("Pos.: (%d, %d)\n", pm_pos.x, pm_pos.y); X printf("Tunn.: %s\n",TF(pm_tunn)); X printf("Baud: %d\n", baud); X printf("Screen dimension %d x %d\n", LINES, COLS); X printf("High score: %ld\n", hi_score); X printf("Max's: %d,%d\n", stdscr->_maxy, stdscr->_maxx); X printf("\n"); X printf("\n[Press return to continue]"); X X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X trash(getchar()); X fcntl(0, F_SETFL, O_NDELAY); X#else X trash(getchar()); X#endif X X chcnt = 0L; X redraw(); X} END-of-make_moves.c echo file: misc.c sed 's/^X//' >misc.c << 'END-of-misc.c' X/* X** misc.c - miscellaneous functions X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include <signal.h> X#include "pm.h" X X/* X** chg_lvl() - change the level by the desired ammount X*/ Xvoid chg_lvl (delta) Xreg int delta; X{ X level += delta; X if (level < 0) X level = 0; X ch = ' '; X oldch = ' '; X newch = ' '; X pm_run = TRUE; X sleep(1); X new_screen(); X} X X/* X** scrcpy() - copy score structures X** - there are more efficient ways todo this, but lint doesn't X** like any of the ones i came up with X*/ Xvoid scrcpy (to, from) Xreg score *to, *from; X{ X to->sc_uid = from->sc_uid; X to->sc_score = from->sc_score; X to->sc_level = from->sc_level; X to->sc_flags = from->sc_flags; X to->sc_mons = from->sc_mons; X strncpy(to->sc_name, from->sc_name, sizeof(from->sc_name)); X return; X} X X/* X** dir_int() - changes a char direction indicator to a int X*/ Xint dir_int (dir) Xreg char dir; X{ X switch (dir) X { X case MUP: X return(0); X case MDOWN: X return(1); X case MLEFT: X return(2); X case MRIGHT: X return(3); X default: X return(-1); X } X /*NOTREACHED*/ X} X Xstatic char *dirs[] = X{ X "\n\n\t\tWelcome to the game of pm\n", X "Just a few words of information and caution to", X "beginning pm players. This game is very expensive!!!", X "It is so expensive that your usercode may not last", X "more than a few medium length games. The arcade", X "equivalent of this costs a quarter per game, no", X "matter how long it lasts. With pm it is the other way", X "around, the longer you play the more expensive it gets!", X "If you are unfamilar with the commands for movement, I", X "suggest that you try getting the hang of them by playing", X "other games (such as rogue, snake, or tank) that use", X "similar commands. It could get very expensive getting", X "the hang of moving around by learning on pm.", X "The higher your baud rate, the better the game performs.", X "9600 is a pretty good speed to run at.", X "For a summary of valid commands, type in a '?'.", X "\nHappy packing!!!", X NULL X}; X X/* X** directions() - print out any opening messages to beginners X*/ Xvoid directions () X{ X reg char **s = dirs; X X while (*s) X printf("%s\n", *s++); X printf("[Press return to continue] "); X trash(getchar()); /* no fcntl needed here */ X} X X/* X** get_pass() - read in the password X** - only read in 8 characters! X*/ Xchar *get_pass () X{ X static char buf[9]; X reg int i; X X nocrmode(); X for (i = 0; i < 9; i++) X buf[i] = '\0'; X for (i = 0; i < 8; i++) X { X reg char in; X X if ((in = getchar()) == '\n') X break; X buf[i] = in; X } X crmode(); X return(buf); X} X X/* X** int_dir() - changes an int to a char direction indicator X** - the % insures that it is in range X*/ Xchar int_dir (dir) Xreg int dir; X{ X static char _dirs[] = X { X MUP, MDOWN, MLEFT, MRIGHT, 0 X }; X X return(_dirs[dir % MAX_DIRS]); X} X X/* X** lturn() - return the direction to the left, relative to X** the given direction X*/ Xchar lturn (dir) Xreg char dir; X{ X switch (dir) X { X case MUP: X return(MLEFT); X case MDOWN: X return(MRIGHT); X case MLEFT: X return(MDOWN); X case MRIGHT: X return(MUP); X default: X return(MSTOP); X } X /*NOTREACHED*/ X} X X/* X** mons_str() - return the (full) name of the given monster X*/ Xchar *mons_str (mon) Xreg char mon; X{ X switch (mon) X { case HARPO: X return("Harpo"); X case GROUCHO: X return("Groucho"); X case CHICO: X return("Chico"); X case ZEPPO: X return("Zeppo"); X default: X return("Anonymous"); X } X /*NOTREACHED*/ X} X X/* X** opposite() - return the direction opposite to that specified X*/ Xchar opposite (dir) Xreg char dir; X{ X switch (dir) X { X case MUP: X return(MDOWN); X case MDOWN: X return(MUP); X case MLEFT: X return(MRIGHT); X case MRIGHT: X return(MLEFT); X default: X return(MSTOP); X } X} X X/* X** rturn() - return the direction to the right, relative to X** the given direction X*/ Xchar rturn (dir) Xreg char dir; X{ X switch (dir) X { X case MUP: X return(MRIGHT); X case MDOWN: X return(MLEFT); X case MLEFT: X return(MUP); X case MRIGHT: X return(MDOWN); X default: X return(MSTOP); X } X} X X/* X** quit_it() - stop the game X*/ Xvoid quit_it () X{ X endwin(); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X exit(0); X} X X/* X** shell() - set their uid to their real uid and give them a shell X*/ Xvoid shell () X{ X reg char *sh; X reg int pid; X extern char *getenv(); X X doclear(); X#if SYSV|SYSIII X fcntl(0, F_SETFL, oldfl); X#endif X Echo(); /* defined to echo() on machines without bug */ X nocrmode(); X if ((sh = getenv("SHELL")) == NULL) /* check for a preferred shell */ X sh = DEFAULT_SH; X move(LINES - 1, 0); /* and go to the bottom */ X draw(); X if ((pid = fork()) == -1) X { X fprintf(stderr, "fork failed, bye bye\n"); X quit_it(); X } X if (!pid) /* if child */ X { X if (setuid(uid) == -1) /* in case we are running setuid */ X exit((fprintf(stderr, "Can't setuid(%d)\n", uid), 1)); X if (setgid(getgid()) == -1) /* in case we are running setgid */ X exit((fprintf(stderr,"Can't setgid(%d)\n",getgid()),1)); X#ifndef LINT X signal(SIGINT, SIG_DFL); X signal(SIGQUIT, SIG_DFL); X#endif X execle(sh, "shell", "-i", 0, environ); X perror("pm:"); X exit(1); X } X#ifndef LINT X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X#endif X wait(0); X trap(0); /* reset signals */ X noecho(); X crmode(); X printf("[Press return to continue]"); X trash(getchar()); X redraw(); X#if SYSV|SYSIII X fcntl(0, F_SETFL, O_NDELAY); X#endif X} X X/* X** toletter() - translate the numeric move (key pad) to a letter move X** - this is to facilitate the use of keypads if the X** terminal is so equipped X** - return NULL for invalid X*/ Xchar toletter (in) Xreg char in; X{ X static char mvs[] = X { X NULL, NULL, MDOWN, NULL, MLEFT, MSTOP, MRIGHT, NULL, MUP, NULL X }; X X return(mvs[(in - '0') % 9]); X} X X/* X** trap() - catches signals X** - flag is zero for the initial call, non-zero X** when an interrupt is recieved X*/ Xvoid trap (flag) Xreg int flag; X{ X if (!flag) X { X#ifndef LINT X signal(SIGINT, trap); X signal(SIGHUP, trap); X#endif X return; X } X#ifndef LINT X signal(SIGINT, SIG_IGN); X signal(SIGHUP, SIG_IGN); X#endif X doclear(); X refresh(); X quit_it(); X} X X/* X** tunn_look() - return what is at the given location in the tunnel X*/ Xchar tunn_look (pos) Xreg coord *pos; X{ X reg int i; X X for (i = 0; i < MAX_MONS; i++) X if (AT(pos, &ghosts[i].mo_pos)) X return(ghosts[i].mo_name); X if (AT(pos, &pm_pos)) X return(PM); X move(pos->y, pos->x); X return(INCH()); X} X X#ifdef BAD_OVERLAY X/* X** overlay() - a bug exist[s,ed] in the curses function overlay() on the X** 4.2 machine on which this program was updated. this file X** is included in case this bug is not just local to this X** system. X** X*/ X X/* X** In the outer `for' loop the "<" should have been a "<=". X*/ X# define min(a,b) (a < b ? a : b) X# define max(a,b) (a > b ? a : b) X X/* X * This routine writes win1 on win2 non-destructively. X * X * 11/5/82 (Berkeley) @(#)overlay.c 1.4 X */ Xoverlay(win1, win2) Xreg WINDOW *win1, *win2; { X X reg char *sp, *end; X reg int x, y, endy, endx, starty, startx; X X# ifdef DEBUG X fprintf(outf, "OVERLAY(%0.2o, %0.2o);\n", win1, win2); X# endif X starty = max(win1->_begy, win2->_begy) - win1->_begy; X startx = max(win1->_begx, win2->_begx) - win1->_begx; X endy = min(win1->_maxy, win2->_maxy) - win1->_begy - 1; X endx = min(win1->_maxx, win2->_maxx) - win1->_begx - 1; X/* X** this is what was erroneously here: X** X** for (y = starty; y < endy; y++) { X** X** below you will find the correct code (s/</<=/) X*/ X for (y = starty; y <= endy; y++) { X end = &win1->_y[y][endx]; X x = startx + win1->_begx; X for (sp = &win1->_y[y][startx]; sp <= end; sp++) { X if (!isspace(*sp)) X mvwaddch(win2, y + win1->_begy, x, *sp); X x++; X } X } X} X#endif X Xvoid Necho() X{ X#ifdef ECHOBUG X# ifdef SYSIII X _tty.c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL); X _echoit = TRUE; X Stty(_tty_ch, &_tty); X# else X# ifdef SYSV X echo(); X# endif X#else X echo(); X#endif X} X X/* X** to_baud() - convert the given string to an appropriate baud define X*/ Xchar to_baud (s) Xreg char *s; X{ X reg int i; X static char *sbauds[] = X { X "0", "50", "75", "110", "134.5", "150", X "200", "300", "600", "1200", "1800", "2400", X "4800", "9600", X NULL X }; X X for (i = 0; sbauds[i]; i++) X if (!strcmp(sbauds[i], s)) X return(atoi(s)); X return('\0'); X} END-of-misc.c echo file: monsters.c sed 's/^X//' >monsters.c << 'END-of-monsters.c' X/* X** monsters.c - X** This file contains the necessary functions X** to deal with the monsters and how they move. X** If the flag variable is TRUE, then they are after X** you, else you are after them. X** X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include "pm.h" X X/* X** harpo - will try his best to get you, but is slow X** - smart, slow X** X** groucho - is always behind you, it is hard to shake him X** - smart, fast X** X** chico - he's fast X** - fast X** X** zeppo - terribly shy and will actually run away from you X** - medium, dumb X*/ X X/* X** p_monsters() - place and initialize the monsters for new screens X*/ Xvoid p_monsters () X{ X reg int i; X X for (i = 0; i < MAX_MONS; i++) X m_init(&ghosts[i]); X h->mo_pos.x = 26; X h->mo_pos.y = 11; X g->mo_pos.x = 26; X g->mo_pos.y = 9; X c->mo_pos.x = 28; X c->mo_pos.y = 11; X z->mo_pos.x = 24; X z->mo_pos.y = 11; X h->mo_name = HARPO; X g->mo_name = GROUCHO; X c->mo_name = CHICO; X z->mo_name = ZEPPO; X g->mo_inside = FALSE; X for (i = 0; i < MAX_MONS; i++) X { X move(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x); X addch(ghosts[i].mo_name); X } X} X X/* X** m_move() - move the monsters X** - if they are eatable, then skip them half the time X** - if they are inside, skip them a third of the time X*/ Xvoid m_move () X{ X reg int i; X X for (i = 0; i < MAX_MONS; i++) X { X if (ghosts[i].mo_run && (demon % 4L)) X continue; X mv_mon(&ghosts[i]); X if (pm_eaten) X return; X } X} X X/* X** mv_mon() - make the given monster move X** - maybe change the "mod 5"'s to randomness X** - intelligence is simulated by choosing the best X** possible move, and then occasionally doing some- X** thing random. occasionally is defined relative X** to the intelligence of the monster! X** - when a monster is in the tunnel, it continues X** moving in its original direction X** - returns FALSE if pm is eaten (everything must stop!) X*/ Xvoid mv_mon (m) Xreg mons *m; X{ X#ifdef FASTER X reg int once = FALSE; X#endif X X#ifdef FASTER Xtop: X#endif X if (!get_move(m)) X { X m->mo_ch = moves[rnd(0, 3)]; X m->mo_cnt = 0; X } X /* X ** take care of slow monsters X */ X if ((!(demon % SPEED)) && X (m->mo_attrib & SLOW) && X !m->mo_run && !m->mo_tunn) X return; X#ifdef FASTER X /* X ** take care of fast monsters X */ X if (!(demon % SPEED) && X (m->mo_attrib & FAST) && X !once && !m->mo_run && !m->mo_tunn) X once = TRUE; X else if (once) X once = FALSE; X#endif X /* X ** take care of dumb monsters X ** - 25% of time they move randomly, and if they are inside, X ** then 50% of the time they move wrong X */ X if ((m->mo_attrib & DUMB) && !m->mo_tunn) X { X if (rnd(1,100) < 25) X { X if ((m->mo_ch = gen_mv(m)) != MSTOP) X m->mo_cnt = DIST(); X else X m->mo_cnt = 0; X } X else if (m->mo_inside && (rnd(1, 100) < 45)) X { X m->mo_ch = opposite(m->mo_ch); X m->mo_cnt = 2; X } X } X /* X ** take care of medium monsters X */ X else if ((m->mo_attrib & NORMAL) && !m->mo_tunn) X { X if (rnd(1,100) < 10) X { X if ((m->mo_ch = gen_mv(m)) != MSTOP) X m->mo_cnt = DIST(); X else X m->mo_cnt = 0; X } X else if (m->mo_inside && (rnd(1, 100) < 37)) X { X m->mo_ch = opposite(m->mo_ch); X m->mo_cnt = 2; X } X else if ((rnd(1, 100) < 15) && (m->mo_ch != MSTOP)) X m->mo_cnt = rnd(2, 4); X } X else if (m->mo_attrib & SMART) X { X if (m->mo_inside && (rnd(1, 100) < 27)) X { X m->mo_ch = opposite(m->mo_ch); X m->mo_cnt = 2; X } X } X if (!_mv_mon(m)) X return; X#ifdef FASTER X if (once) X goto top; X#endif X} X X/* X** p_info() - prints information about the given monster X*/ Xvoid p_info (name) Xreg char name; X{ X reg mons *m; X X m = wh_mons(name); X if (m == NULL) X msg("No such monster: %s", punctrl(name)); X doclear(); X fprintf(stderr, "Name: %c\n", m->mo_name); X fprintf(stderr, "Place: (%d,%d)\n", m->mo_pos.x, m->mo_pos.y); X fprintf(stderr, "Inch: %c\n", m->mo_inch); X fprintf(stderr, "Run: %s\n", TF(m->mo_run)); X fprintf(stderr, "Tunn: %s\n", TF(m->mo_tunn)); X fprintf(stderr, "Eaten: %s\n", TF(m->mo_eaten)); X fprintf(stderr, "Inside: %s\n", TF(m->mo_inside)); X fprintf(stderr, "Move: %c\n", m->mo_ch); X fprintf(stderr, "Count: %d\n", m->mo_cnt); X fprintf(stderr, "Attrib: %o\n\n", m->mo_attrib); X printf("[Press return to continue]"); X trash(getchar()); /* no need to add fcntl here */ X redraw(); X} X X/* X** _mv_mon() - lower level routine to move a monster X** - checks for barriers and stuff... X** - returns FALSE if pm is eaten (everything must stop!) X** - if in tunnel, it will only move half as fast X*/ X_mv_mon (m) Xreg mons *m; X{ X reg char what; X auto coord pos; XAbove: X if (m->mo_tunn) X { X if (!(demon % 4)) X { X pos.x = m->mo_pos.x; X pos.y = m->mo_pos.y; X switch (m->mo_ch) X { X when MLEFT: X if (--pos.x < LEFT) X pos.x = RIGHT; X when MRIGHT: X if (++pos.x > RIGHT) X pos.x = LEFT; X otherwise: X msg("Bad move in tunn"); X } X } X else /* not moving this time around */ X return(TRUE); X switch (what = tunn_look(&pos)) X { X case PM: X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X if (OUTOFTUNN(&m->mo_pos)) X m->mo_tunn = FALSE; X if (m->mo_run) X { X pm_eat_m(m->mo_name); X return(FALSE); X } X m_eat_pm(m); X return(FALSE); X case EMPTY: X if (!OUTOFTUNN(&pos)) X msg("Lost out of tunn"); X m->mo_tunn = FALSE; X goto ok; X case TUNNEL: X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X m->mo_inch = what; X return(TRUE); X default: X /* X ** ran into another monster? X */ X m->mo_ch = opposite(m->mo_ch); X msg("Ran into another monster"); X return(TRUE); X } X } X else X { X if (move_to(m->mo_ch, &m->mo_pos, &pos) == -1) X return(-1); X move(pos.y, pos.x); X what = INCH(); X } X switch (what) X { X when PM: X mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch); X draw(); X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X if (m->mo_run) X return(pm_eat_m(m->mo_name), FALSE); X mvaddch(pos.y, pos.x, m->mo_name); X draw(); X m->mo_inch = EMPTY; X m_eat_pm(m); X return(FALSE); X when BLOCK: X m->mo_cnt = 0; X when DOT: X case ENERGY: X case EMPTY: Xok: X mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch); X#ifdef MAX_UPDATE X draw(); X#endif X mvaddch(pos.y, pos.x, m->mo_name); X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X m->mo_inch = what; X when DOOR: X if (!m->mo_inside) /* can only exit, not enter */ X { X m->mo_cnt = 0; X break; X } X mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch); X#ifdef MAX_UPDATE X draw(); X#endif X mvaddch(pos.y, pos.x, m->mo_name); X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X m->mo_inch = what; X m->mo_inside = FALSE; X when TUNNEL: X if (tunn_look(&pos) != TUNNEL) X break; X mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch); X m->mo_inch = TUNNEL; X m->mo_tunn = TRUE; X m->mo_pos.x = pos.x; X m->mo_pos.y = pos.y; X goto Above; X otherwise: X if (IS_FRUIT(what)) X goto ok; X if (!is_mons(what)) X { X msg("_mv_mon(): default"); X break; X } X m->mo_cnt = 0; X } X draw(); X return(TRUE); X} X X/* X** get_move() - find a smart move for the given monster X** - return FALSE if none was determined X*/ Xint get_move (m) Xreg mons *m; X{ X reg int i, dist = 0; X auto char tmp; X X if (m->mo_cnt > 0) X m->mo_cnt--; X /* X ** must always move up when on the door!!! X */ X if (m->mo_inch == DOOR) X { X m->mo_ch = MUP; X m->mo_cnt = 0; X return(TRUE); X } X if (m->mo_tunn) X { /* if in tunnel, keep moving */ X#ifdef DEBUG X if (!m->mo_ch) X msg("m's in tunn, & mo_ch is null"); X#endif X return(TRUE); X } X if (m->mo_inside) X { X /* X ** still inside their little cave (box) X ** make them move to space under the door if they X ** are not there already X */ X if (m->mo_pos.x > DOOR_COL) X { X m->mo_ch = MLEFT; X m->mo_cnt = 1; X return(TRUE); X } X if (m->mo_pos.x < DOOR_COL) X { X m->mo_ch = MRIGHT; X m->mo_cnt = 1; X return(TRUE); X } X /* X ** they're under the door, try to move up! X */ X m->mo_ch = MUP; X m->mo_cnt = 1; X return(TRUE); X } X tmp = m->mo_ch; X for (i = 0; i < 4; i++) X { X m->mo_ch = int_dir(i); X if (dist = can_see(m, &pm_pos)) X break; X } X if (m->mo_run && dist) /* running away from him */ X { X m->mo_cnt = 0; X if (m_is_safe(m, opposite(m->mo_ch))) X return(m->mo_ch = opposite(m->mo_ch), TRUE); X m->mo_ch = tmp; X if ((m->mo_ch = gen_mv(m)) != MSTOP) X return(TRUE); X m->mo_ch = (rnd(0, 1) ? lturn(m->mo_ch) : rturn(m->mo_ch)); X return(FALSE); X } X if (dist) X return(m->mo_cnt = dist, TRUE); X m->mo_ch = tmp; X if (m->mo_cnt) /* check for predetermination */ X return(TRUE); X m->mo_cnt = 0; X if ((m->mo_ch = gen_mv(m)) != MSTOP) X return(TRUE); X m->mo_ch = tmp; X if (m_is_safe(m, m->mo_ch)) X return(TRUE); X return(FALSE); X} X X/* X** gen_mv() - generate a move when no heuristics are available X** - turn 80% of the time, 50% of that left, 50% right X** - 20% of the time just keep going X*/ Xchar gen_mv (m) Xreg mons *m; X{ X if (rnd(0, 99) < 40) X { X if (m_is_safe(m, rturn(m->mo_ch))) X return(rturn(m->mo_ch)); X if (m_is_safe(m, lturn(m->mo_ch))) X return(lturn(m->mo_ch)); X return(MSTOP); X } X if (rnd(0, 59) < 40) X { X if (m_is_safe(m, lturn(m->mo_ch))) X return(lturn(m->mo_ch)); X if (m_is_safe(m, rturn(m->mo_ch))) X return(rturn(m->mo_ch)); X return(MSTOP); X } X if (m_is_safe(m, m->mo_ch)) X return(m->mo_ch); X if (rnd(0, 1)) X { X if (m_is_safe(m, rturn(m->mo_ch))) X return(rturn(m->mo_ch)); X if (m_is_safe(m, lturn(m->mo_ch))) X return(lturn(m->mo_ch)); X return(MSTOP); X } X if (m_is_safe(m, lturn(m->mo_ch))) X return(lturn(m->mo_ch)); X if (m_is_safe(m, rturn(m->mo_ch))) X return(rturn(m->mo_ch)); X return(MSTOP); X} X X/* X** m_is_safe() - return TRUE if monster can move in the indicated X** direction X*/ Xint m_is_safe (m, dir) Xreg mons *m; Xreg char dir; X{ X reg char what; X auto coord pos; X X if (move_to(dir, &m->mo_pos, &pos)) X return(FALSE); X move(pos.y, pos.x); X switch (what = INCH()) X { X case HARPO: X case _HARPO: X case GROUCHO: X case _GROUCHO: X case CHICO: X case _CHICO: X case ZEPPO: X case _ZEPPO: X case BLOCK: X case DOOR: X return(FALSE); X case DOT: X case ENERGY: X case EMPTY: X case TUNNEL: X return(TRUE); X case PM: X if (m->mo_run) X return(FALSE); X return(TRUE); X default: X if (IS_FRUIT(what)) X return(TRUE); X msg("m_is_safe default"); X return(FALSE); X } X} X X/* X** can_see() - returns distance the monster is from the pm if X** the pm can be seen X** - look in the direction the monster is facing X** - special case for when pm is in tunnel, then the X** monster must be facing the tunnel X*/ Xint can_see (m, pos) Xreg mons *m; Xreg coord *pos; X{ X auto int dist = 0; X auto coord mv; X X switch (m->mo_ch) X { X when MUP: X if (m->mo_pos.x != pos->x) /* not in line */ X return(FALSE); X if ((m->mo_pos.y == TUNN_ROW) && pm_tunn) X break; X if (m->mo_pos.y < pos->y) /* not facing */ X return(FALSE); X when MDOWN: X if (m->mo_pos.x != pos->x) /* not in line */ X return(FALSE); X if ((m->mo_pos.y == TUNN_ROW) && pm_tunn) X break; X if (m->mo_pos.y > pos->y) /* not facing */ X return(FALSE); X when MLEFT: X if (m->mo_pos.y != pos->y) /* not in line */ X return(FALSE); X if ((m->mo_pos.y == TUNN_ROW) && pm_tunn) X break; X if (m->mo_pos.x < pos->x) /* not facing */ X return(FALSE); X when MRIGHT: X if (m->mo_pos.y != pos->y) /* not in line */ X return(FALSE); X if ((m->mo_pos.y == TUNN_ROW) && pm_tunn) X break; X if (m->mo_pos.x > pos->x) /* not facing */ X return(FALSE); X otherwise: X msg("default in can_see: \%03o", m->mo_ch); X return(FALSE); X } X /* X ** at this point, they are in direct line and X ** facing in the right direction, we need to X ** see if anything is in the way X */ X mv.x = m->mo_pos.x; X mv.y = m->mo_pos.y; X do X { X reg char what; X X if (move_to(m->mo_ch, &mv, &mv) == -1) X return(msg("move_to=-1"), FALSE); X move(mv.y, mv.x); X switch (what = INCH()) X { X case PM: X case HARPO: X case _HARPO: X case GROUCHO: X case _GROUCHO: X case CHICO: X case _CHICO: X case ZEPPO: X case _ZEPPO: X case DOT: X case ENERGY: X case DOOR: X case EMPTY: X break; X case TUNNEL: X if (pm_tunn) X return(dist); X else X msg("can_see(): what???"); X case BLOCK: X return(0); X default: X if (IS_FRUIT(what)) /* its the fruit */ X break; X msg("can_see(): case (2)"); X return(0); X } X dist++; X } while ((mv.x != pos->x) || (mv.y != pos->y)); X return(dist); X} X X/* X** move_to() - X*/ Xint move_to (dir, pos1, pos2) Xreg int dir; /* declared int to pacify lint */ Xreg coord *pos1, *pos2; X{ X static int offset[2][4] = X { {0, 0, -1, 1}, X {-1, 1, 0, 0} X }; X X if ((dir = dir_int((char) dir)) == -1) X return(-1); X pos2->x = pos1->x + offset[0][dir]; X pos2->y = pos1->y + offset[1][dir]; X return(0); X} X X/* X** wh_mons() - return pointer to ch's monster struct X*/ Xmons *wh_mons (mch) Xreg char mch; X{ X switch (mch) X { X case HARPO: X case _HARPO: X return(h); X case GROUCHO: X case _GROUCHO: X return(g); X case CHICO: X case _CHICO: X return(c); X case ZEPPO: X case _ZEPPO: X return(z); X default: X msg("Unknown monster!!!"); X return((mons *) NULL); X } X} X X/* X** m_eat_pm() - a monster has eaten the pm X*/ Xvoid m_eat_pm (m) Xreg mons *m; X{ X eat_pm(); X sleep(1); X if (!--pms_left) /* if no more pm's then quit immediately */ X die(m->mo_name); X#if !SYSV && !SYSIII X flush(); /* this totally destroys crmode on System V */ X#endif X old_screen(); X pm_eaten = FALSE; X ch = ' '; X oldch = '\0'; X newch = '\0'; X} X X/* X** eat_pm() - make the pm look eaten X*/ Xvoid eat_pm () X{ X reg int i; X X if (pm_tunn) X return; X for (i = 0; i < 5; i++) X { X mvaddch(pm_pos.y, pm_pos.x, 'O'); X draw(); X/* msleep(EAT_PAUSE); */ X mvaddch(pm_pos.y, pm_pos.x, '='); X draw(); X/* msleep(EAT_PAUSE); */ X mvaddch(pm_pos.y, pm_pos.x, 'O'); X draw(); X/* msleep(EAT_PAUSE); */ X mvaddch(pm_pos.y, pm_pos.x, '='); X draw(); X/* msleep(EAT_PAUSE); */ X } X sleep(2); X mvaddch(pm_pos.y, pm_pos.x, EMPTY); X draw(); X} X X/* X** is_mons() - return TRUE if ch is a monster X*/ Xint is_mons (whoru) Xreg char whoru; X{ X switch (whoru) X { X case HARPO: X case _HARPO: X case GROUCHO: X case _GROUCHO: X case CHICO: X case _CHICO: X case ZEPPO: X case _ZEPPO: X return(TRUE); X } X return(FALSE); X} X X/* X** place_m() - put a monster back in its box X** - an infinite loop can occur if the box is full! X** - the box should never be full!!! X*/ Xvoid place_m (m) Xreg mons *m; X{ X reg int xx; X X m->mo_pos.y = 11; X while (TRUE) X { X move(m->mo_pos.y, (xx = rnd(19, 33))); X if (INCH() == EMPTY) X break; X } X m->mo_pos.x = xx; X move(m->mo_pos.y, m->mo_pos.x); X addch(m->mo_name); X} END-of-monsters.c echo file: msg.c sed 's/^X//' >msg.c << 'END-of-msg.c' X/* X** msg.c - code dealing with the printing of messages X** X** [pm by Peter Costantinidis, Jr. @ University of California at Davis] X*/ X#include <signal.h> X#include "pm.h" X X#ifdef SYSV X# define _IOSTRG _IOLBF X#endif X X/* X** strucpy() - copy string using punctrl for things X** - ctrl chars count double X*/ Xvoid strucpy (s1, s2, len) Xreg char *s1, *s2; Xreg int len; X{ X reg char *sp; X X while (len-- && *s2) X { X if ((*s2 < ' ') && !len--) /* if len = 0, then no room */ X return; X strcpy(s1, sp = punctrl(*s2++)); X s1 += strlen(sp); X } X *s1 = '\0'; X} X X/* X** msg: X** Display a message on the screen. X*/ X X#ifdef SYSV Xstatic unsigned char msgbuf[BUFSIZ]; X#else Xstatic char msgbuf[BUFSIZ]; X#endif X X/*VARARGS1*/ Xvoid msg (fmt, args) Xchar *fmt; Xint args; X{ X alarm(0); X /* X ** if the string is "", just clear the line X */ X if (*fmt == '\0') X { X move(5, 55); X clrtoeol(); X return; X } X /* X ** otherwise print the message and flush it out X */ X doadd(fmt, &args); X move(5, 55); X addstr(msgbuf); X clrtoeol(); X refresh(); X /* X ** set off an alarm to erase it X */ X#ifndef LINT X signal(SIGALRM, msg_erase); X#endif X alarm(ALARM_TIME); X} X X/* X** msg_erase() - erase the msg line X*/ Xvoid msg_erase () X{ X alarm(0); X move(5, 55); X addstr(" "); X} X Xvoid doadd (fmt, args) Xchar **fmt; Xint ***args; X{ X static FILE junk; X X /* X ** Do the printf into buf X */ X junk._flag = _IOWRT + _IOSTRG; X junk._ptr = &msgbuf[0]; X junk._cnt = 32767; X _doprnt(fmt, args, &junk); X putc('\0', &junk); X} X X/* X** re_msg() - reprint the last message X*/ Xvoid re_msg () X{ X msg(msgbuf); X} X X/* X** punctrl() - print a readable version of a certain character X** X** Note: Due to the inconsistent availability of a function to perform X** this, my own version has been built in and used in place of X** any pre-existing function. I believe that this particular X** version suts down on data space considerably from the versions X** I have found on the Berkley systems. X*/ Xchar *punctrl (chr) Xchar chr; X{ X static char *str = "^ "; X X chr &= 0177; X if (chr >= ' ' && chr <= '~') X { X static char *str1 = " "; X X *str1 = chr; X return(str1); X } X if (chr == CTRL(?)) X return("^?"); X *(str+1) = chr + '@'; X return(str); X} END-of-msg.c exit -- Eric Safern ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern