Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / games / hack / hack.end.c
CommitLineData
984263bc
MD
1/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2/* hack.end.c - version 1.0.3 */
3/* $FreeBSD: src/games/hack/hack.end.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
1de703da 4/* $DragonFly: src/games/hack/hack.end.c,v 1.2 2003/06/17 04:25:24 dillon Exp $ */
984263bc
MD
5
6#include "hack.h"
7#include <stdio.h>
8#include <signal.h>
9#define Sprintf (void) sprintf
10extern char plname[], pl_character[];
11extern char *itoa(), *ordin(), *eos();
12
13xchar maxdlevel = 1;
14
15void
16done1()
17{
18 (void) signal(SIGINT,SIG_IGN);
19 pline("Really quit?");
20 if(readchar() != 'y') {
21 (void) signal(SIGINT,done1);
22 clrlin();
23 (void) fflush(stdout);
24 if(multi > 0) nomul(0);
25 return;
26 }
27 done("quit");
28 /* NOTREACHED */
29}
30
31int done_stopprint;
32int done_hup;
33
34void
35done_intr(){
36 done_stopprint++;
37 (void) signal(SIGINT, SIG_IGN);
38 (void) signal(SIGQUIT, SIG_IGN);
39}
40
41void
42done_hangup(){
43 done_hup++;
44 (void) signal(SIGHUP, SIG_IGN);
45 done_intr();
46}
47
48done_in_by(mtmp) struct monst *mtmp; {
49static char buf[BUFSZ];
50 pline("You die ...");
51 if(mtmp->data->mlet == ' '){
52 Sprintf(buf, "the ghost of %s", (char *) mtmp->mextra);
53 killer = buf;
54 } else if(mtmp->mnamelth) {
55 Sprintf(buf, "%s called %s",
56 mtmp->data->mname, NAME(mtmp));
57 killer = buf;
58 } else if(mtmp->minvis) {
59 Sprintf(buf, "invisible %s", mtmp->data->mname);
60 killer = buf;
61 } else killer = mtmp->data->mname;
62 done("died");
63}
64
65/* called with arg "died", "drowned", "escaped", "quit", "choked", "panicked",
66 "burned", "starved" or "tricked" */
67/* Be careful not to call panic from here! */
68done(st1)
69char *st1;
70{
71
72#ifdef WIZARD
73 if(wizard && *st1 == 'd'){
74 u.uswldtim = 0;
75 if(u.uhpmax < 0) u.uhpmax = 100; /* arbitrary */
76 u.uhp = u.uhpmax;
77 pline("For some reason you are still alive.");
78 flags.move = 0;
79 if(multi > 0) multi = 0; else multi = -1;
80 flags.botl = 1;
81 return;
82 }
83#endif WIZARD
84 (void) signal(SIGINT, done_intr);
85 (void) signal(SIGQUIT, done_intr);
86 (void) signal(SIGHUP, done_hangup);
87 if(*st1 == 'q' && u.uhp < 1){
88 st1 = "died";
89 killer = "quit while already on Charon's boat";
90 }
91 if(*st1 == 's') killer = "starvation"; else
92 if(*st1 == 'd' && st1[1] == 'r') killer = "drowning"; else
93 if(*st1 == 'p') killer = "panic"; else
94 if(*st1 == 't') killer = "trickery"; else
95 if(!index("bcd", *st1)) killer = st1;
96 paybill();
97 clearlocks();
98 if(flags.toplin == 1) more();
99 if(index("bcds", *st1)){
100#ifdef WIZARD
101 if(!wizard)
102#endif WIZARD
103 savebones();
104 if(!flags.notombstone)
105 outrip();
106 }
107 if(*st1 == 'c') killer = st1; /* after outrip() */
108 settty((char *) 0); /* does a clear_screen() */
109 if(!done_stopprint)
110 printf("Goodbye %s %s...\n\n", pl_character, plname);
111 { long int tmp;
112 tmp = u.ugold - u.ugold0;
113 if(tmp < 0)
114 tmp = 0;
115 if(*st1 == 'd' || *st1 == 'b')
116 tmp -= tmp/10;
117 u.urexp += tmp;
118 u.urexp += 50 * maxdlevel;
119 if(maxdlevel > 20)
120 u.urexp += 1000*((maxdlevel > 30) ? 10 : maxdlevel - 20);
121 }
122 if(*st1 == 'e') {
123 extern struct monst *mydogs;
124 struct monst *mtmp;
125 struct obj *otmp;
126 int i;
127 unsigned worthlessct = 0;
128 boolean has_amulet = FALSE;
129
130 killer = st1;
131 keepdogs();
132 mtmp = mydogs;
133 if(mtmp) {
134 if(!done_stopprint) printf("You");
135 while(mtmp) {
136 if(!done_stopprint)
137 printf(" and %s", monnam(mtmp));
138 if(mtmp->mtame)
139 u.urexp += mtmp->mhp;
140 mtmp = mtmp->nmon;
141 }
142 if(!done_stopprint)
143 printf("\nescaped from the dungeon with %ld points,\n",
144 u.urexp);
145 } else
146 if(!done_stopprint)
147 printf("You escaped from the dungeon with %ld points,\n",
148 u.urexp);
149 for(otmp = invent; otmp; otmp = otmp->nobj) {
150 if(otmp->olet == GEM_SYM){
151 objects[otmp->otyp].oc_name_known = 1;
152 i = otmp->quan*objects[otmp->otyp].g_val;
153 if(i == 0) {
154 worthlessct += otmp->quan;
155 continue;
156 }
157 u.urexp += i;
158 if(!done_stopprint)
159 printf("\t%s (worth %d Zorkmids),\n",
160 doname(otmp), i);
161 } else if(otmp->olet == AMULET_SYM) {
162 otmp->known = 1;
163 i = (otmp->spe < 0) ? 2 : 5000;
164 u.urexp += i;
165 if(!done_stopprint)
166 printf("\t%s (worth %d Zorkmids),\n",
167 doname(otmp), i);
168 if(otmp->spe >= 0) {
169 has_amulet = TRUE;
170 killer = "escaped (with amulet)";
171 }
172 }
173 }
174 if(worthlessct) if(!done_stopprint)
175 printf("\t%u worthless piece%s of coloured glass,\n",
176 worthlessct, plur(worthlessct));
177 if(has_amulet) u.urexp *= 2;
178 } else
179 if(!done_stopprint)
180 printf("You %s on dungeon level %d with %ld points,\n",
181 st1, dlevel, u.urexp);
182 if(!done_stopprint)
183 printf("and %ld piece%s of gold, after %ld move%s.\n",
184 u.ugold, plur(u.ugold), moves, plur(moves));
185 if(!done_stopprint)
186 printf("You were level %u with a maximum of %d hit points when you %s.\n",
187 u.ulevel, u.uhpmax, st1);
188 if(*st1 == 'e' && !done_stopprint){
189 getret(); /* all those pieces of coloured glass ... */
190 cls();
191 }
192#ifdef WIZARD
193 if(!wizard)
194#endif WIZARD
195 topten();
196 if(done_stopprint) printf("\n\n");
197 exit(0);
198}
199
200#define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
201#define NAMSZ 8
202#define DTHSZ 40
203#define PERSMAX 1
204#define POINTSMIN 1 /* must be > 0 */
205#define ENTRYMAX 100 /* must be >= 10 */
206#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */
207struct toptenentry {
208 struct toptenentry *tt_next;
209 long int points;
210 int level,maxlvl,hp,maxhp;
211 int uid;
212 char plchar;
213 char sex;
214 char name[NAMSZ+1];
215 char death[DTHSZ+1];
216 char date[7]; /* yymmdd */
217} *tt_head;
218
219topten(){
220 int uid = getuid();
221 int rank, rank0 = -1, rank1 = 0;
222 int occ_cnt = PERSMAX;
223 struct toptenentry *t0, *t1, *tprev;
224 char *recfile = RECORD;
225 char *reclock = "record_lock";
226 int sleepct = 300;
227 FILE *rfile;
228 int flg = 0;
229 extern char *getdate();
230#define HUP if(!done_hup)
231 while(link(recfile, reclock) == -1) {
232 HUP perror(reclock);
233 if(!sleepct--) {
234 HUP puts("I give up. Sorry.");
235 HUP puts("Perhaps there is an old record_lock around?");
236 return;
237 }
238 HUP printf("Waiting for access to record file. (%d)\n",
239 sleepct);
240 HUP (void) fflush(stdout);
241 sleep(1);
242 }
243 if(!(rfile = fopen(recfile,"r"))){
244 HUP puts("Cannot open record file!");
245 goto unlock;
246 }
247 HUP (void) putchar('\n');
248
249 /* create a new 'topten' entry */
250 t0 = newttentry();
251 t0->level = dlevel;
252 t0->maxlvl = maxdlevel;
253 t0->hp = u.uhp;
254 t0->maxhp = u.uhpmax;
255 t0->points = u.urexp;
256 t0->plchar = pl_character[0];
257 t0->sex = (flags.female ? 'F' : 'M');
258 t0->uid = uid;
259 (void) strncpy(t0->name, plname, NAMSZ);
260 (t0->name)[NAMSZ] = 0;
261 (void) strncpy(t0->death, killer, DTHSZ);
262 (t0->death)[DTHSZ] = 0;
263 (void) strcpy(t0->date, getdate());
264
265 /* assure minimum number of points */
266 if(t0->points < POINTSMIN)
267 t0->points = 0;
268
269 t1 = tt_head = newttentry();
270 tprev = 0;
271 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
272 for(rank = 1; ; ) {
273 if(fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
274 t1->date, &t1->uid,
275 &t1->level, &t1->maxlvl,
276 &t1->hp, &t1->maxhp, &t1->points,
277 &t1->plchar, &t1->sex, t1->name, t1->death) != 11
278 || t1->points < POINTSMIN)
279 t1->points = 0;
280 if(rank0 < 0 && t1->points < t0->points) {
281 rank0 = rank++;
282 if(tprev == 0)
283 tt_head = t0;
284 else
285 tprev->tt_next = t0;
286 t0->tt_next = t1;
287 occ_cnt--;
288 flg++; /* ask for a rewrite */
289 } else
290 tprev = t1;
291 if(t1->points == 0) break;
292 if(
293#ifdef PERS_IS_UID
294 t1->uid == t0->uid &&
295#else
296 strncmp(t1->name, t0->name, NAMSZ) == 0 &&
297#endif PERS_IS_UID
298 t1->plchar == t0->plchar && --occ_cnt <= 0){
299 if(rank0 < 0){
300 rank0 = 0;
301 rank1 = rank;
302 HUP printf("You didn't beat your previous score of %ld points.\n\n",
303 t1->points);
304 }
305 if(occ_cnt < 0){
306 flg++;
307 continue;
308 }
309 }
310 if(rank <= ENTRYMAX){
311 t1 = t1->tt_next = newttentry();
312 rank++;
313 }
314 if(rank > ENTRYMAX){
315 t1->points = 0;
316 break;
317 }
318 }
319 if(flg) { /* rewrite record file */
320 (void) fclose(rfile);
321 if(!(rfile = fopen(recfile,"w"))){
322 HUP puts("Cannot write record file\n");
323 goto unlock;
324 }
325
326 if(!done_stopprint) if(rank0 > 0){
327 if(rank0 <= 10)
328 puts("You made the top ten list!\n");
329 else
330 printf("You reached the %d%s place on the top %d list.\n\n",
331 rank0, ordin(rank0), ENTRYMAX);
332 }
333 }
334 if(rank0 == 0) rank0 = rank1;
335 if(rank0 <= 0) rank0 = rank;
336 if(!done_stopprint) outheader();
337 t1 = tt_head;
338 for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
339 if(flg) fprintf(rfile,"%6s %d %d %d %d %d %ld %c%c %s,%s\n",
340 t1->date, t1->uid,
341 t1->level, t1->maxlvl,
342 t1->hp, t1->maxhp, t1->points,
343 t1->plchar, t1->sex, t1->name, t1->death);
344 if(done_stopprint) continue;
345 if(rank > flags.end_top &&
346 (rank < rank0-flags.end_around || rank > rank0+flags.end_around)
347 && (!flags.end_own ||
348#ifdef PERS_IS_UID
349 t1->uid != t0->uid ))
350#else
351 strncmp(t1->name, t0->name, NAMSZ)))
352#endif PERS_IS_UID
353 continue;
354 if(rank == rank0-flags.end_around &&
355 rank0 > flags.end_top+flags.end_around+1 &&
356 !flags.end_own)
357 (void) putchar('\n');
358 if(rank != rank0)
359 (void) outentry(rank, t1, 0);
360 else if(!rank1)
361 (void) outentry(rank, t1, 1);
362 else {
363 int t0lth = outentry(0, t0, -1);
364 int t1lth = outentry(rank, t1, t0lth);
365 if(t1lth > t0lth) t0lth = t1lth;
366 (void) outentry(0, t0, t0lth);
367 }
368 }
369 if(rank0 >= rank) if(!done_stopprint)
370 (void) outentry(0, t0, 1);
371 (void) fclose(rfile);
372unlock:
373 (void) unlink(reclock);
374}
375
376outheader() {
377char linebuf[BUFSZ];
378char *bp;
379 (void) strcpy(linebuf, "Number Points Name");
380 bp = eos(linebuf);
381 while(bp < linebuf + COLNO - 9) *bp++ = ' ';
382 (void) strcpy(bp, "Hp [max]");
383 puts(linebuf);
384}
385
386/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */
387int
388outentry(rank,t1,so) struct toptenentry *t1; {
389boolean quit = FALSE, killed = FALSE, starv = FALSE;
390char linebuf[BUFSZ];
391 linebuf[0] = 0;
392 if(rank) Sprintf(eos(linebuf), "%3d", rank);
393 else Sprintf(eos(linebuf), " ");
394 Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name);
395 if(t1->plchar == 'X') Sprintf(eos(linebuf), " ");
396 else Sprintf(eos(linebuf), "-%c ", t1->plchar);
397 if(!strncmp("escaped", t1->death, 7)) {
398 if(!strcmp(" (with amulet)", t1->death+7))
399 Sprintf(eos(linebuf), "escaped the dungeon with amulet");
400 else
401 Sprintf(eos(linebuf), "escaped the dungeon [max level %d]",
402 t1->maxlvl);
403 } else {
404 if(!strncmp(t1->death,"quit",4)) {
405 quit = TRUE;
406 if(t1->maxhp < 3*t1->hp && t1->maxlvl < 4)
407 Sprintf(eos(linebuf), "cravenly gave up");
408 else
409 Sprintf(eos(linebuf), "quit");
410 }
411 else if(!strcmp(t1->death,"choked"))
412 Sprintf(eos(linebuf), "choked on %s food",
413 (t1->sex == 'F') ? "her" : "his");
414 else if(!strncmp(t1->death,"starv",5))
415 Sprintf(eos(linebuf), "starved to death"), starv = TRUE;
416 else Sprintf(eos(linebuf), "was killed"), killed = TRUE;
417 Sprintf(eos(linebuf), " on%s level %d",
418 (killed || starv) ? "" : " dungeon", t1->level);
419 if(t1->maxlvl != t1->level)
420 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
421 if(quit && t1->death[4]) Sprintf(eos(linebuf), t1->death + 4);
422 }
423 if(killed) Sprintf(eos(linebuf), " by %s%s",
424 (!strncmp(t1->death, "trick", 5) || !strncmp(t1->death, "the ", 4))
425 ? "" :
426 index(vowels,*t1->death) ? "an " : "a ",
427 t1->death);
428 Sprintf(eos(linebuf), ".");
429 if(t1->maxhp) {
430 char *bp = eos(linebuf);
431 char hpbuf[10];
432 int hppos;
433 Sprintf(hpbuf, (t1->hp > 0) ? itoa(t1->hp) : "-");
434 hppos = COLNO - 7 - strlen(hpbuf);
435 if(bp <= linebuf + hppos) {
436 while(bp < linebuf + hppos) *bp++ = ' ';
437 (void) strcpy(bp, hpbuf);
438 Sprintf(eos(bp), " [%d]", t1->maxhp);
439 }
440 }
441 if(so == 0) puts(linebuf);
442 else if(so > 0) {
443 char *bp = eos(linebuf);
444 if(so >= COLNO) so = COLNO-1;
445 while(bp < linebuf + so) *bp++ = ' ';
446 *bp = 0;
447 standoutbeg();
448 fputs(linebuf,stdout);
449 standoutend();
450 (void) putchar('\n');
451 }
452 return(strlen(linebuf));
453}
454
455char *
456itoa(a) int a; {
457static char buf[12];
458 Sprintf(buf,"%d",a);
459 return(buf);
460}
461
462char *
463ordin(n) int n; {
464int d = n%10;
465 return((d==0 || d>3 || n/10==1) ? "th" : (d==1) ? "st" :
466 (d==2) ? "nd" : "rd");
467}
468
469clearlocks(){
470int x;
471 (void) signal(SIGHUP,SIG_IGN);
472 for(x = maxdlevel; x >= 0; x--) {
473 glo(x);
474 (void) unlink(lock); /* not all levels need be present */
475 }
476}
477
478#ifdef NOSAVEONHANGUP
479hangup()
480{
481 (void) signal(SIGINT, SIG_IGN);
482 clearlocks();
483 exit(1);
484}
485#endif NOSAVEONHANGUP
486
487char *
488eos(s)
489char *s;
490{
491 while(*s) s++;
492 return(s);
493}
494
495/* it is the callers responsibility to check that there is room for c */
496charcat(s,c) char *s, c; {
497 while(*s) s++;
498 *s++ = c;
499 *s = 0;
500}
501
502/*
503 * Called with args from main if argc >= 0. In this case, list scores as
504 * requested. Otherwise, find scores for the current player (and list them
505 * if argc == -1).
506 */
507prscore(argc,argv) int argc; char **argv; {
508 extern char *hname;
509 char **players;
510 int playerct;
511 int rank;
512 struct toptenentry *t1, *t2;
513 char *recfile = RECORD;
514 FILE *rfile;
515 int flg = 0;
516 int i;
517#ifdef nonsense
518 long total_score = 0L;
519 char totchars[10];
520 int totcharct = 0;
521#endif nonsense
522 int outflg = (argc >= -1);
523#ifdef PERS_IS_UID
524 int uid = -1;
525#else
526 char *player0;
527#endif PERS_IS_UID
528
529 if(!(rfile = fopen(recfile,"r"))){
530 puts("Cannot open record file!");
531 return;
532 }
533
534 if(argc > 1 && !strncmp(argv[1], "-s", 2)){
535 if(!argv[1][2]){
536 argc--;
537 argv++;
538 } else if(!argv[1][3] && index("CFKSTWX", argv[1][2])) {
539 argv[1]++;
540 argv[1][0] = '-';
541 } else argv[1] += 2;
542 }
543 if(argc <= 1){
544#ifdef PERS_IS_UID
545 uid = getuid();
546 playerct = 0;
547#else
548 player0 = plname;
549 if(!*player0)
550 player0 = "hackplayer";
551 playerct = 1;
552 players = &player0;
553#endif PERS_IS_UID
554 } else {
555 playerct = --argc;
556 players = ++argv;
557 }
558 if(outflg) putchar('\n');
559
560 t1 = tt_head = newttentry();
561 for(rank = 1; ; rank++) {
562 if(fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
563 t1->date, &t1->uid,
564 &t1->level, &t1->maxlvl,
565 &t1->hp, &t1->maxhp, &t1->points,
566 &t1->plchar, &t1->sex, t1->name, t1->death) != 11)
567 t1->points = 0;
568 if(t1->points == 0) break;
569#ifdef PERS_IS_UID
570 if(!playerct && t1->uid == uid)
571 flg++;
572 else
573#endif PERS_IS_UID
574 for(i = 0; i < playerct; i++){
575 if(strcmp(players[i], "all") == 0 ||
576 strncmp(t1->name, players[i], NAMSZ) == 0 ||
577 (players[i][0] == '-' &&
578 players[i][1] == t1->plchar &&
579 players[i][2] == 0) ||
580 (digit(players[i][0]) && rank <= atoi(players[i])))
581 flg++;
582 }
583 t1 = t1->tt_next = newttentry();
584 }
585 (void) fclose(rfile);
586 if(!flg) {
587 if(outflg) {
588 printf("Cannot find any entries for ");
589 if(playerct < 1) printf("you.\n");
590 else {
591 if(playerct > 1) printf("any of ");
592 for(i=0; i<playerct; i++)
593 printf("%s%s", players[i], (i<playerct-1)?", ":".\n");
594 printf("Call is: %s -s [playernames]\n", hname);
595 }
596 }
597 return;
598 }
599
600 if(outflg) outheader();
601 t1 = tt_head;
602 for(rank = 1; t1->points != 0; rank++, t1 = t2) {
603 t2 = t1->tt_next;
604#ifdef PERS_IS_UID
605 if(!playerct && t1->uid == uid)
606 goto outwithit;
607 else
608#endif PERS_IS_UID
609 for(i = 0; i < playerct; i++){
610 if(strcmp(players[i], "all") == 0 ||
611 strncmp(t1->name, players[i], NAMSZ) == 0 ||
612 (players[i][0] == '-' &&
613 players[i][1] == t1->plchar &&
614 players[i][2] == 0) ||
615 (digit(players[i][0]) && rank <= atoi(players[i]))){
616 outwithit:
617 if(outflg)
618 (void) outentry(rank, t1, 0);
619#ifdef nonsense
620 total_score += t1->points;
621 if(totcharct < sizeof(totchars)-1)
622 totchars[totcharct++] = t1->plchar;
623#endif nonsense
624 break;
625 }
626 }
627 free((char *) t1);
628 }
629#ifdef nonsense
630 totchars[totcharct] = 0;
631
632 /* We would like to determine whether he is experienced. However,
633 the information collected here only tells about the scores/roles
634 that got into the topten (top 100?). We should maintain a
635 .hacklog or something in his home directory. */
636 flags.beginner = (total_score < 6000);
637 for(i=0; i<6; i++)
638 if(!index(totchars, "CFKSTWX"[i])) {
639 flags.beginner = 1;
640 if(!pl_character[0]) pl_character[0] = "CFKSTWX"[i];
641 break;
642 }
643#endif nonsense
644}