386BSD 0.1 development
[unix-history] / usr / othersrc / games / hack / hack.main.c
CommitLineData
684d40c6
WJ
1/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2/* hack.main.c - version 1.0.3 */
3
4#include <stdio.h>
5#include <signal.h>
6#include "hack.h"
7
8#ifdef QUEST
9#define gamename "quest"
10#else
11#define gamename "hack"
12#endif
13
14extern char *getlogin(), *getenv();
15extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
16extern struct permonst mons[CMNUM+2];
17extern char genocided[], fut_geno[];
18
19int (*afternmv)();
20int (*occupation)();
21char *occtxt; /* defined when occupation != NULL */
22
23void done1();
24void hangup();
25
26int hackpid; /* current pid */
27int locknum; /* max num of players */
28#ifdef DEF_PAGER
29char *catmore; /* default pager */
30#endif
31char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */
32char *hname; /* name of the game (argv[0] of call) */
33char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
34
35extern char *nomovemsg;
36extern long wailmsg;
37
38#ifdef CHDIR
39static void chdirx();
40#endif
41
42main(argc,argv)
43int argc;
44char *argv[];
45{
46 register int fd;
47#ifdef CHDIR
48 register char *dir;
49#endif
50
51 hname = argv[0];
52 hackpid = getpid();
53
54#ifdef CHDIR /* otherwise no chdir() */
55 /*
56 * See if we must change directory to the playground.
57 * (Perhaps hack runs suid and playground is inaccessible
58 * for the player.)
59 * The environment variable HACKDIR is overridden by a
60 * -d command line option (must be the first option given)
61 */
62
63 dir = getenv("HACKDIR");
64 if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
65 argc--;
66 argv++;
67 dir = argv[0]+2;
68 if(*dir == '=' || *dir == ':') dir++;
69 if(!*dir && argc > 1) {
70 argc--;
71 argv++;
72 dir = argv[0];
73 }
74 if(!*dir)
75 error("Flag -d must be followed by a directory name.");
76 }
77#endif
78
79 /*
80 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
81 * 2. Use $USER or $LOGNAME (if 1. fails)
82 * 3. Use getlogin() (if 2. fails)
83 * The resulting name is overridden by command line options.
84 * If everything fails, or if the resulting name is some generic
85 * account like "games", "play", "player", "hack" then eventually
86 * we'll ask him.
87 * Note that we trust him here; it is possible to play under
88 * somebody else's name.
89 */
90 { register char *s;
91
92 initoptions();
93 if(!*plname && (s = getenv("USER")))
94 (void) strncpy(plname, s, sizeof(plname)-1);
95 if(!*plname && (s = getenv("LOGNAME")))
96 (void) strncpy(plname, s, sizeof(plname)-1);
97 if(!*plname && (s = getlogin()))
98 (void) strncpy(plname, s, sizeof(plname)-1);
99 }
100
101 /*
102 * Now we know the directory containing 'record' and
103 * may do a prscore().
104 */
105 if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
106#ifdef CHDIR
107 chdirx(dir,0);
108#endif
109 prscore(argc, argv);
110 exit(0);
111 }
112
113 /*
114 * It seems he really wants to play.
115 * Remember tty modes, to be restored on exit.
116 */
117 gettty();
118 setbuf(stdout,obuf);
119 setrandom();
120 startup();
121 cls();
122 u.uhp = 1; /* prevent RIP on early quits */
123 u.ux = FAR; /* prevent nscr() */
124 (void) signal(SIGHUP, hangup);
125
126 /*
127 * Find the creation date of this game,
128 * so as to avoid restoring outdated savefiles.
129 */
130 gethdate(hname);
131
132 /*
133 * We cannot do chdir earlier, otherwise gethdate will fail.
134 */
135#ifdef CHDIR
136 chdirx(dir,1);
137#endif
138
139 /*
140 * Process options.
141 */
142 while(argc > 1 && argv[1][0] == '-'){
143 argv++;
144 argc--;
145 switch(argv[0][1]){
146#ifdef WIZARD
147 case 'D':
148/* if(!strcmp(getlogin(), WIZARD)) */
149 wizard = TRUE;
150/* else
151 printf("Sorry.\n"); */
152 break;
153#endif
154#ifdef NEWS
155 case 'n':
156 flags.nonews = TRUE;
157 break;
158#endif
159 case 'u':
160 if(argv[0][2])
161 (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
162 else if(argc > 1) {
163 argc--;
164 argv++;
165 (void) strncpy(plname, argv[0], sizeof(plname)-1);
166 } else
167 printf("Player name expected after -u\n");
168 break;
169 default:
170 /* allow -T for Tourist, etc. */
171 (void) strncpy(pl_character, argv[0]+1,
172 sizeof(pl_character)-1);
173
174 /* printf("Unknown option: %s\n", *argv); */
175 }
176 }
177
178 if(argc > 1)
179 locknum = atoi(argv[1]);
180#ifdef MAX_NR_OF_PLAYERS
181 if(!locknum || locknum > MAX_NR_OF_PLAYERS)
182 locknum = MAX_NR_OF_PLAYERS;
183#endif
184#ifdef DEF_PAGER
185 if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
186 catmore = DEF_PAGER;
187#endif
188#ifdef MAIL
189 getmailstatus();
190#endif
191#ifdef WIZARD
192 if(wizard) (void) strcpy(plname, "wizard"); else
193#endif
194 if(!*plname || !strncmp(plname, "player", 4)
195 || !strncmp(plname, "games", 4))
196 askname();
197 plnamesuffix(); /* strip suffix from name; calls askname() */
198 /* again if suffix was whole name */
199 /* accepts any suffix */
200#ifdef WIZARD
201 if(!wizard) {
202#endif
203 /*
204 * check for multiple games under the same name
205 * (if !locknum) or check max nr of players (otherwise)
206 */
207 (void) signal(SIGQUIT,SIG_IGN);
208 (void) signal(SIGINT,SIG_IGN);
209 if(!locknum)
210 (void) strcpy(lock,plname);
211 getlock(); /* sets lock if locknum != 0 */
212#ifdef WIZARD
213 } else {
214 register char *sfoo;
215 (void) strcpy(lock,plname);
216 if(sfoo = getenv("MAGIC"))
217 while(*sfoo) {
218 switch(*sfoo++) {
219 case 'n': (void) srandom(*sfoo++);
220 break;
221 }
222 }
223 if(sfoo = getenv("GENOCIDED")){
224 if(*sfoo == '!'){
225 register struct permonst *pm = mons;
226 register char *gp = genocided;
227
228 while(pm < mons+CMNUM+2){
229 if(!index(sfoo, pm->mlet))
230 *gp++ = pm->mlet;
231 pm++;
232 }
233 *gp = 0;
234 } else
235 (void) strcpy(genocided, sfoo);
236 (void) strcpy(fut_geno, genocided);
237 }
238 }
239#endif
240 setftty();
241 (void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
242 regularize(SAVEF+5); /* avoid . or / in name */
243 if((fd = open(SAVEF,0)) >= 0 &&
244 (uptodate(fd) || unlink(SAVEF) == 666)) {
245 (void) signal(SIGINT,done1);
246 pline("Restoring old save file...");
247 (void) fflush(stdout);
248 if(!dorecover(fd))
249 goto not_recovered;
250 pline("Hello %s, welcome to %s!", plname, gamename);
251 flags.move = 0;
252 } else {
253not_recovered:
254 fobj = fcobj = invent = 0;
255 fmon = fallen_down = 0;
256 ftrap = 0;
257 fgold = 0;
258 flags.ident = 1;
259 init_objects();
260 u_init();
261
262 (void) signal(SIGINT,done1);
263 mklev();
264 u.ux = xupstair;
265 u.uy = yupstair;
266 (void) inshop();
267 setsee();
268 flags.botlx = 1;
269 makedog();
270 { register struct monst *mtmp;
271 if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */
272 }
273 seemons();
274#ifdef NEWS
275 if(flags.nonews || !readnews())
276 /* after reading news we did docrt() already */
277#endif
278 docrt();
279
280 /* give welcome message before pickup messages */
281 pline("Hello %s, welcome to %s!", plname, gamename);
282
283 pickup(1);
284 read_engr_at(u.ux,u.uy);
285 flags.move = 1;
286 }
287
288 flags.moonphase = phase_of_the_moon();
289 if(flags.moonphase == FULL_MOON) {
290 pline("You are lucky! Full moon tonight.");
291 u.uluck++;
292 } else if(flags.moonphase == NEW_MOON) {
293 pline("Be careful! New moon tonight.");
294 }
295
296 initrack();
297
298 for(;;) {
299 if(flags.move) { /* actual time passed */
300
301 settrack();
302
303 if(moves%2 == 0 ||
304 (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
305 extern struct monst *makemon();
306 movemon();
307 if(!rn2(70))
308 (void) makemon((struct permonst *)0, 0, 0);
309 }
310 if(Glib) glibr();
311 timeout();
312 ++moves;
313 if(flags.time) flags.botl = 1;
314 if(u.uhp < 1) {
315 pline("You die...");
316 done("died");
317 }
318 if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
319 wailmsg = moves;
320 if(u.uhp == 1)
321 pline("You hear the wailing of the Banshee...");
322 else
323 pline("You hear the howling of the CwnAnnwn...");
324 }
325 if(u.uhp < u.uhpmax) {
326 if(u.ulevel > 9) {
327 if(Regeneration || !(moves%3)) {
328 flags.botl = 1;
329 u.uhp += rnd((int) u.ulevel-9);
330 if(u.uhp > u.uhpmax)
331 u.uhp = u.uhpmax;
332 }
333 } else if(Regeneration ||
334 (!(moves%(22-u.ulevel*2)))) {
335 flags.botl = 1;
336 u.uhp++;
337 }
338 }
339 if(Teleportation && !rn2(85)) tele();
340 if(Searching && multi >= 0) (void) dosearch();
341 gethungry();
342 invault();
343 amulet();
344 }
345 if(multi < 0) {
346 if(!++multi){
347 pline(nomovemsg ? nomovemsg :
348 "You can move again.");
349 nomovemsg = 0;
350 if(afternmv) (*afternmv)();
351 afternmv = 0;
352 }
353 }
354
355 find_ac();
356#ifndef QUEST
357 if(!flags.mv || Blind)
358#endif
359 {
360 seeobjs();
361 seemons();
362 nscr();
363 }
364 if(flags.botl || flags.botlx) bot();
365
366 flags.move = 1;
367
368 if(multi >= 0 && occupation) {
369 if(monster_nearby())
370 stop_occupation();
371 else if ((*occupation)() == 0)
372 occupation = 0;
373 continue;
374 }
375
376 if(multi > 0) {
377#ifdef QUEST
378 if(flags.run >= 4) finddir();
379#endif
380 lookaround();
381 if(!multi) { /* lookaround may clear multi */
382 flags.move = 0;
383 continue;
384 }
385 if(flags.mv) {
386 if(multi < COLNO && !--multi)
387 flags.mv = flags.run = 0;
388 domove();
389 } else {
390 --multi;
391 rhack(save_cm);
392 }
393 } else if(multi == 0) {
394#ifdef MAIL
395 ckmailstatus();
396#endif
397 rhack((char *) 0);
398 }
399 if(multi && multi%7 == 0)
400 (void) fflush(stdout);
401 }
402}
403
404glo(foo)
405register foo;
406{
407 /* construct the string xlock.n */
408 register char *tf;
409
410 tf = lock;
411 while(*tf && *tf != '.') tf++;
412 (void) sprintf(tf, ".%d", foo);
413}
414
415/*
416 * plname is filled either by an option (-u Player or -uPlayer) or
417 * explicitly (-w implies wizard) or by askname.
418 * It may still contain a suffix denoting pl_character.
419 */
420askname(){
421register int c,ct;
422 printf("\nWho are you? ");
423 (void) fflush(stdout);
424 ct = 0;
425 while((c = getchar()) != '\n'){
426 if(c == EOF) error("End of input\n");
427 /* some people get confused when their erase char is not ^H */
428 if(c == '\010') {
429 if(ct) ct--;
430 continue;
431 }
432 if(c != '-')
433 if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
434 if(ct < sizeof(plname)-1) plname[ct++] = c;
435 }
436 plname[ct] = 0;
437 if(ct == 0) askname();
438}
439
440/*VARARGS1*/
441impossible(s,x1,x2)
442register char *s;
443{
444 pline(s,x1,x2);
445 pline("Program in disorder - perhaps you'd better Quit.");
446}
447
448#ifdef CHDIR
449static void
450chdirx(dir, wr)
451char *dir;
452boolean wr;
453{
454
455#ifdef SECURE
456 if(dir /* User specified directory? */
457#ifdef HACKDIR
458 && strcmp(dir, HACKDIR) /* and not the default? */
459#endif
460 ) {
461 (void) setuid(getuid()); /* Ron Wessels */
462 (void) setgid(getgid());
463 }
464#endif
465
466#ifdef HACKDIR
467 if(dir == NULL)
468 dir = HACKDIR;
469#endif
470
471 if(dir && chdir(dir) < 0) {
472 perror(dir);
473 error("Cannot chdir to %s.", dir);
474 }
475
476 /* warn the player if he cannot write the record file */
477 /* perhaps we should also test whether . is writable */
478 /* unfortunately the access systemcall is worthless */
479 if(wr) {
480 register fd;
481
482 if(dir == NULL)
483 dir = ".";
484 if((fd = open(RECORD, 2)) < 0) {
485 printf("Warning: cannot write %s/%s", dir, RECORD);
486 getret();
487 } else
488 (void) close(fd);
489 }
490}
491#endif
492
493stop_occupation()
494{
495 if(occupation) {
496 pline("You stop %s.", occtxt);
497 occupation = 0;
498 }
499}