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