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