0f8da203cb
Fix nonull flag detecting from termcap Use usleep to handle nonull case Prevent overflow on genocided getenv Install most files not owned by games.games to prevent data modifications Fix path for gethdate()
503 lines
10 KiB
C
503 lines
10 KiB
C
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* hack.main.c - version 1.0.3 */
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include "hack.h"
|
|
|
|
#ifdef QUEST
|
|
#define gamename "quest"
|
|
#else
|
|
#define gamename "hack"
|
|
#endif
|
|
|
|
extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
|
|
extern struct permonst mons[CMNUM+2];
|
|
extern char genocided[60], fut_geno[];
|
|
|
|
int (*afternmv)();
|
|
int (*occupation)();
|
|
char *occtxt; /* defined when occupation != NULL */
|
|
|
|
void done1();
|
|
void hangup();
|
|
|
|
int hackpid; /* current pid */
|
|
int locknum; /* max num of players */
|
|
#ifdef DEF_PAGER
|
|
char *catmore; /* default pager */
|
|
#endif
|
|
char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */
|
|
char *hname; /* name of the game (argv[0] of call) */
|
|
char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
|
|
|
|
extern char *nomovemsg;
|
|
extern long wailmsg;
|
|
|
|
#ifdef CHDIR
|
|
static void chdirx();
|
|
#endif
|
|
|
|
main(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
register int fd;
|
|
#ifdef CHDIR
|
|
register char *dir;
|
|
#endif
|
|
|
|
hname = argv[0];
|
|
hackpid = getpid();
|
|
|
|
#ifdef CHDIR /* otherwise no chdir() */
|
|
/*
|
|
* See if we must change directory to the playground.
|
|
* (Perhaps hack runs suid and playground is inaccessible
|
|
* for the player.)
|
|
* The environment variable HACKDIR is overridden by a
|
|
* -d command line option (must be the first option given)
|
|
*/
|
|
|
|
dir = getenv("HACKDIR");
|
|
if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
|
|
argc--;
|
|
argv++;
|
|
dir = argv[0]+2;
|
|
if(*dir == '=' || *dir == ':') dir++;
|
|
if(!*dir && argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
dir = argv[0];
|
|
}
|
|
if(!*dir)
|
|
error("Flag -d must be followed by a directory name.");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
|
|
* 2. Use $USER or $LOGNAME (if 1. fails)
|
|
* 3. Use getlogin() (if 2. fails)
|
|
* The resulting name is overridden by command line options.
|
|
* If everything fails, or if the resulting name is some generic
|
|
* account like "games", "play", "player", "hack" then eventually
|
|
* we'll ask him.
|
|
* Note that we trust him here; it is possible to play under
|
|
* somebody else's name.
|
|
*/
|
|
{ register char *s;
|
|
|
|
initoptions();
|
|
if(!*plname && (s = getenv("USER")))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
if(!*plname && (s = getenv("LOGNAME")))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
if(!*plname && (s = getlogin()))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
}
|
|
|
|
/*
|
|
* Now we know the directory containing 'record' and
|
|
* may do a prscore().
|
|
*/
|
|
if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
|
|
#ifdef CHDIR
|
|
chdirx(dir,0);
|
|
#endif
|
|
prscore(argc, argv);
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* It seems he really wants to play.
|
|
* Remember tty modes, to be restored on exit.
|
|
*/
|
|
gettty();
|
|
setbuf(stdout,obuf);
|
|
umask(007);
|
|
setrandom();
|
|
startup();
|
|
cls();
|
|
u.uhp = 1; /* prevent RIP on early quits */
|
|
u.ux = FAR; /* prevent nscr() */
|
|
(void) signal(SIGHUP, hangup);
|
|
|
|
/*
|
|
* Find the creation date of this game,
|
|
* so as to avoid restoring outdated savefiles.
|
|
*/
|
|
gethdate(hname);
|
|
|
|
/*
|
|
* We cannot do chdir earlier, otherwise gethdate will fail.
|
|
*/
|
|
#ifdef CHDIR
|
|
chdirx(dir,1);
|
|
#endif
|
|
|
|
/*
|
|
* Process options.
|
|
*/
|
|
while(argc > 1 && argv[1][0] == '-'){
|
|
argv++;
|
|
argc--;
|
|
switch(argv[0][1]){
|
|
#ifdef WIZARD
|
|
case 'D':
|
|
/* if(!strcmp(getlogin(), WIZARD)) */
|
|
wizard = TRUE;
|
|
/* else
|
|
printf("Sorry.\n"); */
|
|
break;
|
|
#endif
|
|
#ifdef NEWS
|
|
case 'n':
|
|
flags.nonews = TRUE;
|
|
break;
|
|
#endif
|
|
case 'u':
|
|
if(argv[0][2])
|
|
(void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
|
|
else if(argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
(void) strncpy(plname, argv[0], sizeof(plname)-1);
|
|
} else
|
|
printf("Player name expected after -u\n");
|
|
break;
|
|
default:
|
|
/* allow -T for Tourist, etc. */
|
|
(void) strncpy(pl_character, argv[0]+1,
|
|
sizeof(pl_character)-1);
|
|
|
|
/* printf("Unknown option: %s\n", *argv); */
|
|
}
|
|
}
|
|
|
|
if(argc > 1)
|
|
locknum = atoi(argv[1]);
|
|
#ifdef MAX_NR_OF_PLAYERS
|
|
if(!locknum || locknum > MAX_NR_OF_PLAYERS)
|
|
locknum = MAX_NR_OF_PLAYERS;
|
|
#endif
|
|
#ifdef DEF_PAGER
|
|
if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
|
|
catmore = DEF_PAGER;
|
|
#endif
|
|
#ifdef MAIL
|
|
getmailstatus();
|
|
#endif
|
|
#ifdef WIZARD
|
|
if(wizard) (void) strcpy(plname, "wizard"); else
|
|
#endif
|
|
if(!*plname || !strncmp(plname, "player", 4)
|
|
|| !strncmp(plname, "games", 4))
|
|
askname();
|
|
plnamesuffix(); /* strip suffix from name; calls askname() */
|
|
/* again if suffix was whole name */
|
|
/* accepts any suffix */
|
|
#ifdef WIZARD
|
|
if(!wizard) {
|
|
#endif
|
|
/*
|
|
* check for multiple games under the same name
|
|
* (if !locknum) or check max nr of players (otherwise)
|
|
*/
|
|
(void) signal(SIGQUIT,SIG_IGN);
|
|
(void) signal(SIGINT,SIG_IGN);
|
|
if(!locknum)
|
|
(void) strcpy(lock,plname);
|
|
getlock(); /* sets lock if locknum != 0 */
|
|
#ifdef WIZARD
|
|
} else {
|
|
register char *sfoo;
|
|
(void) strcpy(lock,plname);
|
|
if(sfoo = getenv("MAGIC"))
|
|
while(*sfoo) {
|
|
switch(*sfoo++) {
|
|
case 'n': (void) srandom(*sfoo++);
|
|
break;
|
|
}
|
|
}
|
|
if(sfoo = getenv("GENOCIDED")){
|
|
if(*sfoo == '!'){
|
|
register struct permonst *pm = mons;
|
|
register char *gp = genocided;
|
|
|
|
while(pm < mons+CMNUM+2){
|
|
if(!index(sfoo, pm->mlet))
|
|
*gp++ = pm->mlet;
|
|
pm++;
|
|
}
|
|
*gp = 0;
|
|
} else
|
|
(void) strncpy(genocided, sfoo, sizeof(genocided)-1);
|
|
(void) strcpy(fut_geno, genocided);
|
|
}
|
|
}
|
|
#endif
|
|
setftty();
|
|
(void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
|
|
regularize(SAVEF+5); /* avoid . or / in name */
|
|
if((fd = open(SAVEF,0)) >= 0 &&
|
|
(uptodate(fd) || unlink(SAVEF) == 666)) {
|
|
(void) signal(SIGINT,done1);
|
|
pline("Restoring old save file...");
|
|
(void) fflush(stdout);
|
|
if(!dorecover(fd))
|
|
goto not_recovered;
|
|
pline("Hello %s, welcome to %s!", plname, gamename);
|
|
flags.move = 0;
|
|
} else {
|
|
not_recovered:
|
|
fobj = fcobj = invent = 0;
|
|
fmon = fallen_down = 0;
|
|
ftrap = 0;
|
|
fgold = 0;
|
|
flags.ident = 1;
|
|
init_objects();
|
|
u_init();
|
|
|
|
(void) signal(SIGINT,done1);
|
|
mklev();
|
|
u.ux = xupstair;
|
|
u.uy = yupstair;
|
|
(void) inshop();
|
|
setsee();
|
|
flags.botlx = 1;
|
|
makedog();
|
|
{ register struct monst *mtmp;
|
|
if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */
|
|
}
|
|
seemons();
|
|
#ifdef NEWS
|
|
if(flags.nonews || !readnews())
|
|
/* after reading news we did docrt() already */
|
|
#endif
|
|
docrt();
|
|
|
|
/* give welcome message before pickup messages */
|
|
pline("Hello %s, welcome to %s!", plname, gamename);
|
|
|
|
pickup(1);
|
|
read_engr_at(u.ux,u.uy);
|
|
flags.move = 1;
|
|
}
|
|
|
|
flags.moonphase = phase_of_the_moon();
|
|
if(flags.moonphase == FULL_MOON) {
|
|
pline("You are lucky! Full moon tonight.");
|
|
u.uluck++;
|
|
} else if(flags.moonphase == NEW_MOON) {
|
|
pline("Be careful! New moon tonight.");
|
|
}
|
|
|
|
initrack();
|
|
|
|
for(;;) {
|
|
if(flags.move) { /* actual time passed */
|
|
|
|
settrack();
|
|
|
|
if(moves%2 == 0 ||
|
|
(!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
|
|
extern struct monst *makemon();
|
|
movemon();
|
|
if(!rn2(70))
|
|
(void) makemon((struct permonst *)0, 0, 0);
|
|
}
|
|
if(Glib) glibr();
|
|
timeout();
|
|
++moves;
|
|
if(flags.time) flags.botl = 1;
|
|
if(u.uhp < 1) {
|
|
pline("You die...");
|
|
done("died");
|
|
}
|
|
if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
|
|
wailmsg = moves;
|
|
if(u.uhp == 1)
|
|
pline("You hear the wailing of the Banshee...");
|
|
else
|
|
pline("You hear the howling of the CwnAnnwn...");
|
|
}
|
|
if(u.uhp < u.uhpmax) {
|
|
if(u.ulevel > 9) {
|
|
if(Regeneration || !(moves%3)) {
|
|
flags.botl = 1;
|
|
u.uhp += rnd((int) u.ulevel-9);
|
|
if(u.uhp > u.uhpmax)
|
|
u.uhp = u.uhpmax;
|
|
}
|
|
} else if(Regeneration ||
|
|
(!(moves%(22-u.ulevel*2)))) {
|
|
flags.botl = 1;
|
|
u.uhp++;
|
|
}
|
|
}
|
|
if(Teleportation && !rn2(85)) tele();
|
|
if(Searching && multi >= 0) (void) dosearch();
|
|
gethungry();
|
|
invault();
|
|
amulet();
|
|
}
|
|
if(multi < 0) {
|
|
if(!++multi){
|
|
pline(nomovemsg ? nomovemsg :
|
|
"You can move again.");
|
|
nomovemsg = 0;
|
|
if(afternmv) (*afternmv)();
|
|
afternmv = 0;
|
|
}
|
|
}
|
|
|
|
find_ac();
|
|
#ifndef QUEST
|
|
if(!flags.mv || Blind)
|
|
#endif
|
|
{
|
|
seeobjs();
|
|
seemons();
|
|
nscr();
|
|
}
|
|
if(flags.botl || flags.botlx) bot();
|
|
|
|
flags.move = 1;
|
|
|
|
if(multi >= 0 && occupation) {
|
|
if(monster_nearby())
|
|
stop_occupation();
|
|
else if ((*occupation)() == 0)
|
|
occupation = 0;
|
|
continue;
|
|
}
|
|
|
|
if(multi > 0) {
|
|
#ifdef QUEST
|
|
if(flags.run >= 4) finddir();
|
|
#endif
|
|
lookaround();
|
|
if(!multi) { /* lookaround may clear multi */
|
|
flags.move = 0;
|
|
continue;
|
|
}
|
|
if(flags.mv) {
|
|
if(multi < COLNO && !--multi)
|
|
flags.mv = flags.run = 0;
|
|
domove();
|
|
} else {
|
|
--multi;
|
|
rhack(save_cm);
|
|
}
|
|
} else if(multi == 0) {
|
|
#ifdef MAIL
|
|
ckmailstatus();
|
|
#endif
|
|
rhack((char *) 0);
|
|
}
|
|
if(multi && multi%7 == 0)
|
|
(void) fflush(stdout);
|
|
}
|
|
}
|
|
|
|
glo(foo)
|
|
register foo;
|
|
{
|
|
/* construct the string xlock.n */
|
|
register char *tf;
|
|
|
|
tf = lock;
|
|
while(*tf && *tf != '.') tf++;
|
|
(void) sprintf(tf, ".%d", foo);
|
|
}
|
|
|
|
/*
|
|
* plname is filled either by an option (-u Player or -uPlayer) or
|
|
* explicitly (-w implies wizard) or by askname.
|
|
* It may still contain a suffix denoting pl_character.
|
|
*/
|
|
askname(){
|
|
register int c,ct;
|
|
printf("\nWho are you? ");
|
|
(void) fflush(stdout);
|
|
ct = 0;
|
|
while((c = getchar()) != '\n'){
|
|
if(c == EOF) error("End of input\n");
|
|
/* some people get confused when their erase char is not ^H */
|
|
if(c == '\010') {
|
|
if(ct) ct--;
|
|
continue;
|
|
}
|
|
if(c != '-')
|
|
if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
|
|
if(ct < sizeof(plname)-1) plname[ct++] = c;
|
|
}
|
|
plname[ct] = 0;
|
|
if(ct == 0) askname();
|
|
}
|
|
|
|
/*VARARGS1*/
|
|
impossible(s,x1,x2)
|
|
register char *s;
|
|
{
|
|
pline(s,x1,x2);
|
|
pline("Program in disorder - perhaps you'd better Quit.");
|
|
}
|
|
|
|
#ifdef CHDIR
|
|
static void
|
|
chdirx(dir, wr)
|
|
char *dir;
|
|
boolean wr;
|
|
{
|
|
|
|
#ifdef SECURE
|
|
if(dir /* User specified directory? */
|
|
#ifdef HACKDIR
|
|
&& strcmp(dir, HACKDIR) /* and not the default? */
|
|
#endif
|
|
) {
|
|
/* revoke */
|
|
setgid(getgid());
|
|
setuid(getuid());
|
|
}
|
|
#endif
|
|
|
|
#ifdef HACKDIR
|
|
if(dir == NULL)
|
|
dir = HACKDIR;
|
|
#endif
|
|
|
|
if(dir && chdir(dir) < 0) {
|
|
perror(dir);
|
|
error("Cannot chdir to %s.", dir);
|
|
}
|
|
|
|
/* warn the player if he cannot write the record file */
|
|
/* perhaps we should also test whether . is writable */
|
|
/* unfortunately the access systemcall is worthless */
|
|
if(wr) {
|
|
register fd;
|
|
|
|
if(dir == NULL)
|
|
dir = ".";
|
|
if((fd = open(RECORD, 2)) < 0) {
|
|
printf("Warning: cannot write %s/%s", dir, RECORD);
|
|
getret();
|
|
} else
|
|
(void) close(fd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
stop_occupation()
|
|
{
|
|
if(occupation) {
|
|
pline("You stop %s.", occtxt);
|
|
occupation = 0;
|
|
}
|
|
}
|