Commit | Line | Data |
---|---|---|
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 | ||
14 | extern char *getlogin(), *getenv(); | |
15 | extern char plname[PL_NSIZ], pl_character[PL_CSIZ]; | |
16 | ||
17 | int (*afternmv)(); | |
18 | int (*occupation)(); | |
19 | char *occtxt; /* defined when occupation != NULL */ | |
20 | ||
21 | int done1(); | |
22 | int hangup(); | |
23 | ||
24 | int hackpid; /* current pid */ | |
25 | int locknum; /* max num of players */ | |
26 | #ifdef DEF_PAGER | |
27 | char *catmore; /* default pager */ | |
28 | #endif DEF_PAGER | |
29 | char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */ | |
30 | char *hname; /* name of the game (argv[0] of call) */ | |
31 | char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ | |
32 | ||
33 | extern char *nomovemsg; | |
34 | extern long wailmsg; | |
35 | ||
36 | main(argc,argv) | |
37 | int argc; | |
38 | char *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 { | |
249 | not_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 | ||
400 | glo(foo) | |
401 | register 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 | */ | |
416 | askname(){ | |
417 | register 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*/ | |
437 | impossible(s,x1,x2) | |
438 | register char *s; | |
439 | { | |
440 | pline(s,x1,x2); | |
441 | pline("Program in disorder - perhaps you'd better Quit."); | |
442 | } | |
443 | ||
444 | #ifdef CHDIR | |
445 | static | |
446 | chdirx(dir, wr) | |
447 | char *dir; | |
448 | boolean 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 | ||
489 | stop_occupation() | |
490 | { | |
491 | if(occupation) { | |
492 | pline("You stop %s.", occtxt); | |
493 | occupation = 0; | |
494 | } | |
495 | } |