date and time created 87/12/26 13:24:09 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];
16
17int (*afternmv)();
18int (*occupation)();
19char *occtxt; /* defined when occupation != NULL */
20
21int done1();
22int hangup();
23
24int hackpid; /* current pid */
25int locknum; /* max num of players */
26#ifdef DEF_PAGER
27char *catmore; /* default pager */
28#endif DEF_PAGER
29char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */
30char *hname; /* name of the game (argv[0] of call) */
31char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
32
33extern char *nomovemsg;
34extern long wailmsg;
35
36main(argc,argv)
37int argc;
38char *argv[];
39{
40 register int fd;
41#ifdef CHDIR
42 register char *dir;
43#endif CHDIR
44
45 hname = argv[0];
46 hackpid = getpid();
47
48#ifdef CHDIR /* otherwise no chdir() */
49 /*
50 * See if we must change directory to the playground.
51 * (Perhaps hack runs suid and playground is inaccessible
52 * for the player.)
53 * The environment variable HACKDIR is overridden by a
54 * -d command line option (must be the first option given)
55 */
56
57 dir = getenv("HACKDIR");
58 if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
59 argc--;
60 argv++;
61 dir = argv[0]+2;
62 if(*dir == '=' || *dir == ':') dir++;
63 if(!*dir && argc > 1) {
64 argc--;
65 argv++;
66 dir = argv[0];
67 }
68 if(!*dir)
69 error("Flag -d must be followed by a directory name.");
70 }
71#endif CHDIR
72
73 /*
74 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
75 * 2. Use $USER or $LOGNAME (if 1. fails)
76 * 3. Use getlogin() (if 2. fails)
77 * The resulting name is overridden by command line options.
78 * If everything fails, or if the resulting name is some generic
79 * account like "games", "play", "player", "hack" then eventually
80 * we'll ask him.
81 * Note that we trust him here; it is possible to play under
82 * somebody else's name.
83 */
84 { register char *s;
85
86 initoptions();
87 if(!*plname && (s = getenv("USER")))
88 (void) strncpy(plname, s, sizeof(plname)-1);
89 if(!*plname && (s = getenv("LOGNAME")))
90 (void) strncpy(plname, s, sizeof(plname)-1);
91 if(!*plname && (s = getlogin()))
92 (void) strncpy(plname, s, sizeof(plname)-1);
93 }
94
95 /*
96 * Now we know the directory containing 'record' and
97 * may do a prscore().
98 */
99 if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
100#ifdef CHDIR
101 chdirx(dir,0);
102#endif CHDIR
103 prscore(argc, argv);
104 exit(0);
105 }
106
107 /*
108 * It seems he really wants to play.
109 * Remember tty modes, to be restored on exit.
110 */
111 gettty();
112 setbuf(stdout,obuf);
113 setrandom();
114 startup();
115 cls();
116 u.uhp = 1; /* prevent RIP on early quits */
117 u.ux = FAR; /* prevent nscr() */
118 (void) signal(SIGHUP, hangup);
119
120 /*
121 * Find the creation date of this game,
122 * so as to avoid restoring outdated savefiles.
123 */
124 gethdate(hname);
125
126 /*
127 * We cannot do chdir earlier, otherwise gethdate will fail.
128 */
129#ifdef CHDIR
130 chdirx(dir,1);
131#endif CHDIR
132
133 /*
134 * Process options.
135 */
136 while(argc > 1 && argv[1][0] == '-'){
137 argv++;
138 argc--;
139 switch(argv[0][1]){
140#ifdef WIZARD
141 case 'D':
142/* if(!strcmp(getlogin(), WIZARD)) */
143 wizard = TRUE;
144/* else
145 printf("Sorry.\n"); */
146 break;
147#endif WIZARD
148#ifdef NEWS
149 case 'n':
150 flags.nonews = TRUE;
151 break;
152#endif NEWS
153 case 'u':
154 if(argv[0][2])
155 (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
156 else if(argc > 1) {
157 argc--;
158 argv++;
159 (void) strncpy(plname, argv[0], sizeof(plname)-1);
160 } else
161 printf("Player name expected after -u\n");
162 break;
163 default:
164 /* allow -T for Tourist, etc. */
165 (void) strncpy(pl_character, argv[0]+1,
166 sizeof(pl_character)-1);
167
168 /* printf("Unknown option: %s\n", *argv); */
169 }
170 }
171
172 if(argc > 1)
173 locknum = atoi(argv[1]);
174#ifdef MAX_NR_OF_PLAYERS
175 if(!locknum || locknum > MAX_NR_OF_PLAYERS)
176 locknum = MAX_NR_OF_PLAYERS;
177#endif MAX_NR_OF_PLAYERS
178#ifdef DEF_PAGER
179 if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
180 catmore = DEF_PAGER;
181#endif DEF_PAGER
182#ifdef MAIL
183 getmailstatus();
184#endif MAIL
185#ifdef WIZARD
186 if(wizard) (void) strcpy(plname, "wizard"); else
187#endif WIZARD
188 if(!*plname || !strncmp(plname, "player", 4)
189 || !strncmp(plname, "games", 4))
190 askname();
191 plnamesuffix(); /* strip suffix from name; calls askname() */
192 /* again if suffix was whole name */
193 /* accepts any suffix */
194#ifdef WIZARD
195 if(!wizard) {
196#endif WIZARD
197 /*
198 * check for multiple games under the same name
199 * (if !locknum) or check max nr of players (otherwise)
200 */
201 (void) signal(SIGQUIT,SIG_IGN);
202 (void) signal(SIGINT,SIG_IGN);
203 if(!locknum)
204 (void) strcpy(lock,plname);
205 getlock(); /* sets lock if locknum != 0 */
206#ifdef WIZARD
207 } else {
208 register char *sfoo;
209 (void) strcpy(lock,plname);
210 if(sfoo = getenv("MAGIC"))
211 while(*sfoo) {
212 switch(*sfoo++) {
213 case 'n': (void) srandom(*sfoo++);
214 break;
215 }
216 }
217 if(sfoo = getenv("GENOCIDED")){
218 if(*sfoo == '!'){
219 extern struct permonst mons[CMNUM+2];
220 extern char genocided[], fut_geno[];
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}