Commit | Line | Data |
---|---|---|
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 | ||
14 | extern char *getlogin(), *getenv(); | |
15 | extern char plname[PL_NSIZ], pl_character[PL_CSIZ]; | |
16 | extern struct permonst mons[CMNUM+2]; | |
17 | extern char genocided[], fut_geno[]; | |
18 | ||
19 | int (*afternmv)(); | |
20 | int (*occupation)(); | |
21 | char *occtxt; /* defined when occupation != NULL */ | |
22 | ||
23 | void done1(); | |
24 | void hangup(); | |
25 | ||
26 | int hackpid; /* current pid */ | |
27 | int locknum; /* max num of players */ | |
28 | #ifdef DEF_PAGER | |
29 | char *catmore; /* default pager */ | |
30 | #endif | |
31 | char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */ | |
32 | char *hname; /* name of the game (argv[0] of call) */ | |
33 | char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ | |
34 | ||
35 | extern char *nomovemsg; | |
36 | extern long wailmsg; | |
37 | ||
38 | #ifdef CHDIR | |
39 | static void chdirx(); | |
40 | #endif | |
41 | ||
42 | main(argc,argv) | |
43 | int argc; | |
44 | char *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 { | |
253 | not_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 | ||
404 | glo(foo) | |
405 | register 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 | */ | |
420 | askname(){ | |
421 | register 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*/ | |
441 | impossible(s,x1,x2) | |
442 | register char *s; | |
443 | { | |
444 | pline(s,x1,x2); | |
445 | pline("Program in disorder - perhaps you'd better Quit."); | |
446 | } | |
447 | ||
448 | #ifdef CHDIR | |
449 | static void | |
450 | chdirx(dir, wr) | |
451 | char *dir; | |
452 | boolean 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 | ||
493 | stop_occupation() | |
494 | { | |
495 | if(occupation) { | |
496 | pline("You stop %s.", occtxt); | |
497 | occupation = 0; | |
498 | } | |
499 | } |