kernel/pf: Check size of long at compile time (fixes i386 build).
[dragonfly.git] / games / rogue / score.c
1 /*-
2  * Copyright (c) 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)score.c  8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/score.c,v 1.4 1999/11/30 03:49:27 billf Exp $
34  * $DragonFly: src/games/rogue/score.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
35  */
36
37 /*
38  * score.c
39  *
40  * This source herein may be modified and/or distributed by anybody who
41  * so desires, with the following restrictions:
42  *    1.)  No portion of this notice shall be removed.
43  *    2.)  Credit shall not be taken for the creation of this source.
44  *    3.)  This code is not to be traded, sold, or used for personal
45  *         gain or profit.
46  *
47  */
48
49 #include <stdio.h>
50 #include "rogue.h"
51 #include "pathnames.h"
52
53 extern char *m_names[];
54 extern short max_level;
55 extern boolean score_only, no_skull, msg_cleared;
56 extern char *byebye_string, *nick_name;
57
58 static void center(short, const char *);
59 static int get_value(const object *);
60 static void id_all(void);
61 static void insert_score(char [][82], char [][30], const char *, short, short,
62                         const object *, short);
63 static int name_cmp(char *, const char *);
64 static void nickize(char *, const char *, const char *);
65 static void sell_pack(void);
66 static void sf_error(void);
67
68 void
69 killed_by(const object *monster, short other)
70 {
71         char buf[128];
72
73         md_ignore_signals();
74
75         if (other != QUIT) {
76                 rogue.gold = ((rogue.gold * 9) / 10);
77         }
78
79         if (other) {
80                 switch(other) {
81                 case HYPOTHERMIA:
82                         strcpy(buf, "died of hypothermia");
83                         break;
84                 case STARVATION:
85                         strcpy(buf, "died of starvation");
86                         break;
87                 case POISON_DART:
88                         strcpy(buf, "killed by a dart");
89                         break;
90                 case QUIT:
91                         strcpy(buf, "quit");
92                         break;
93                 case KFIRE:
94                         strcpy(buf, "killed by fire");
95                         break;
96                 }
97         } else {
98                 strcpy(buf, "Killed by ");
99                 if (is_vowel(m_names[monster->m_char - 'A'][0])) {
100                         strcat(buf, "an ");
101                 } else {
102                         strcat(buf, "a ");
103                 }
104                 strcat(buf, m_names[monster->m_char - 'A']);
105         }
106         strcat(buf, " with ");
107         sprintf(buf+strlen(buf), "%ld gold", rogue.gold);
108         if ((!other) && (!no_skull)) {
109                 clear();
110                 mvaddstr(4, 32, "__---------__");
111                 mvaddstr(5, 30, "_~             ~_");
112                 mvaddstr(6, 29, "/                 \\");
113                 mvaddstr(7, 28, "~                   ~");
114                 mvaddstr(8, 27, "/                     \\");
115                 mvaddstr(9, 27, "|    XXXX     XXXX    |");
116                 mvaddstr(10, 27, "|    XXXX     XXXX    |");
117                 mvaddstr(11, 27, "|    XXX       XXX    |");
118                 mvaddstr(12, 28, "\\         @         /");
119                 mvaddstr(13, 29, "--\\     @@@     /--");
120                 mvaddstr(14, 30, "| |    @@@    | |");
121                 mvaddstr(15, 30, "| |           | |");
122                 mvaddstr(16, 30, "| vvVvvvvvvvVvv |");
123                 mvaddstr(17, 30, "|  ^^^^^^^^^^^  |");
124                 mvaddstr(18, 31, "\\_           _/");
125                 mvaddstr(19, 33, "~---------~");
126                 center(21, nick_name);
127                 center(22, buf);
128         } else {
129                 message(buf, 0);
130         }
131         message("", 0);
132         put_scores(monster, other);
133 }
134
135 void
136 win(void)
137 {
138         unwield(rogue.weapon);          /* disarm and relax */
139         unwear(rogue.armor);
140         un_put_on(rogue.left_ring);
141         un_put_on(rogue.right_ring);
142
143         clear();
144         mvaddstr(10, 11, "@   @  @@@   @   @      @  @  @   @@@   @   @   @");
145         mvaddstr(11, 11, " @ @  @   @  @   @      @  @  @  @   @  @@  @   @");
146         mvaddstr(12, 11, "  @   @   @  @   @      @  @  @  @   @  @ @ @   @");
147         mvaddstr(13, 11, "  @   @   @  @   @      @  @  @  @   @  @  @@");
148         mvaddstr(14, 11, "  @    @@@    @@@        @@ @@    @@@   @   @   @");
149         mvaddstr(17, 11, "Congratulations,  you have  been admitted  to  the");
150         mvaddstr(18, 11, "Fighters' Guild.   You return home,  sell all your");
151         mvaddstr(19, 11, "treasures at great profit and retire into comfort.");
152         message("", 0);
153         message("", 0);
154         id_all();
155         sell_pack();
156         put_scores(NULL, WIN);
157 }
158
159 void
160 quit(boolean from_intrpt)
161 {
162         char buf[128];
163         short i, orow, ocol;
164         boolean mc;
165
166         orow = ocol = 0;
167         mc = FALSE;
168         md_ignore_signals();
169
170         if (from_intrpt) {
171                 orow = rogue.row;
172                 ocol = rogue.col;
173
174                 mc = msg_cleared;
175
176                 for (i = 0; i < DCOLS; i++) {
177                         buf[i] = mvinch(0, i);
178                 }
179         }
180         check_message();
181         message("really quit?", 1);
182         if (rgetchar() != 'y') {
183                 md_heed_signals();
184                 check_message();
185                 if (from_intrpt) {
186                         for (i = 0; i < DCOLS; i++) {
187                                 mvaddch(0, i, buf[i]);
188                         }
189                         msg_cleared = mc;
190                         move(orow, ocol);
191                         refresh();
192                 }
193                 return;
194         }
195         if (from_intrpt) {
196                 clean_up(byebye_string);
197         }
198         check_message();
199         killed_by(NULL, QUIT);
200 }
201
202 void
203 put_scores(const object *monster, short other)
204 {
205         short i, n, rank = 10, x, ne = 0, found_player = -1;
206         char scores[10][82];
207         char n_names[10][30];
208         char buf[128];
209         FILE *fp;
210         long s;
211         boolean pause = score_only;
212
213         md_lock(1);
214
215         if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
216             (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
217                 message("cannot read/write/create score file", 0);
218                 sf_error();
219         }
220         rewind(fp);
221         xxx(1);
222
223         for (i = 0; i < 10; i++) {
224                 if (((n = fread(scores[i], sizeof(char), 80, fp)) < 80) && (n != 0)) {
225                         sf_error();
226                 } else if (n != 0) {
227                         xxxx(scores[i], 80);
228                         if ((n = fread(n_names[i], sizeof(char), 30, fp)) < 30) {
229                                 sf_error();
230                         }
231                         xxxx(n_names[i], 30);
232                 } else {
233                         break;
234                 }
235                 ne++;
236                 if ((!score_only) && (found_player == -1)) {
237                         if (!name_cmp(scores[i]+15, login_name)) {
238                                 x = 5;
239                                 while (scores[i][x] == ' ') {
240                                         x++;
241                                 }
242                                 s = lget_number(scores[i] + x);
243                                 if (rogue.gold < s) {
244                                         score_only = 1;
245                                 } else {
246                                         found_player = i;
247                                 }
248                         }
249                 }
250         }
251
252         /* Remove a superseded entry, if any. */
253         if (found_player != -1) {
254                 ne--;
255                 for (i = found_player; i < ne; i++) {
256                         strcpy(scores[i], scores[i+1]);
257                         strcpy(n_names[i], n_names[i+1]);
258                 }
259         }
260
261         /* If we're going to insert ourselves, do it now */
262         if (!score_only) {
263                 for (i = 0; i < ne; i++) {
264                         x = 5;
265                         while (scores[i][x] == ' ') {
266                                 x++;
267                         }
268                         s = lget_number(scores[i] + x);
269
270                         if (rogue.gold >= s) {
271                                 rank = i;
272                                 break;
273                         }
274                 }
275                 if (ne == 0) {
276                         rank = 0;
277                 } else if ((ne < 10) && (rank == 10)) {
278                         rank = ne;
279                 }
280                 if (rank < 10) {
281                         insert_score(scores, n_names, nick_name, rank, ne, monster,
282                                 other);
283                         if (ne < 10) {
284                                 ne++;
285                         }
286                 }
287                 rewind(fp);
288         }
289
290         clear();
291         mvaddstr(3, 30, "Top  Ten  Rogueists");
292         mvaddstr(8, 0, "Rank   Score   Name");
293
294         md_ignore_signals();
295
296         xxx(1);
297
298         for (i = 0; i < ne; i++) {
299                 if (i == rank) {
300                         standout();
301                 }
302                 if (i == 9) {
303                         scores[i][0] = '1';
304                         scores[i][1] = '0';
305                 } else {
306                         scores[i][0] = ' ';
307                         scores[i][1] = i + '1';
308                 }
309                 nickize(buf, scores[i], n_names[i]);
310                 mvaddstr(i+10, 0, buf);
311                 if (rank < 10) {
312                         xxxx(scores[i], 80);
313                         fwrite(scores[i], sizeof(char), 80, fp);
314                         xxxx(n_names[i], 30);
315                         fwrite(n_names[i], sizeof(char), 30, fp);
316                 }
317                 if (i == rank) {
318                         standend();
319                 }
320         }
321         md_lock(0);
322         refresh();
323         fclose(fp);
324         message("", 0);
325         if (pause) {
326                 message("", 0);
327         }
328         clean_up("");
329 }
330
331 static void
332 insert_score(char scores[][82], char n_names[][30], const char *n_name,
333              short rank, short n, const object *monster, short other)
334 {
335         short i;
336         char buf[128];
337
338         if (n > 0) {
339                 for (i = n; i > rank; i--) {
340                         if ((i < 10) && (i > 0)) {
341                                 strcpy(scores[i], scores[i-1]);
342                                 strcpy(n_names[i], n_names[i-1]);
343                         }
344                 }
345         }
346         sprintf(buf, "%2d    %6ld   %s: ", rank+1, rogue.gold, login_name);
347
348         if (other) {
349                 switch(other) {
350                 case HYPOTHERMIA:
351                         strcat(buf, "died of hypothermia");
352                         break;
353                 case STARVATION:
354                         strcat(buf, "died of starvation");
355                         break;
356                 case POISON_DART:
357                         strcat(buf, "killed by a dart");
358                         break;
359                 case QUIT:
360                         strcat(buf, "quit");
361                         break;
362                 case WIN:
363                         strcat(buf, "a total winner");
364                         break;
365                 case KFIRE:
366                         strcpy(buf, "killed by fire");
367                         break;
368                 }
369         } else {
370                 strcat(buf, "killed by ");
371                 if (is_vowel(m_names[monster->m_char - 'A'][0])) {
372                         strcat(buf, "an ");
373                 } else {
374                         strcat(buf, "a ");
375                 }
376                 strcat(buf, m_names[monster->m_char - 'A']);
377         }
378         sprintf(buf+strlen(buf), " on level %d ",  max_level);
379         if ((other != WIN) && has_amulet()) {
380                 strcat(buf, "with amulet");
381         }
382         for (i = strlen(buf); i < 79; i++) {
383                 buf[i] = ' ';
384         }
385         buf[79] = 0;
386         strcpy(scores[rank], buf);
387         strcpy(n_names[rank], n_name);
388 }
389
390 boolean
391 is_vowel(short ch)
392 {
393         return( (ch == 'a') ||
394                 (ch == 'e') ||
395                 (ch == 'i') ||
396                 (ch == 'o') ||
397                 (ch == 'u') );
398 }
399
400 static void
401 sell_pack(void)
402 {
403         object *obj;
404         short row = 2, val;
405         char buf[DCOLS];
406
407         obj = rogue.pack.next_object;
408
409         clear();
410         mvaddstr(1, 0, "Value      Item");
411
412         while (obj) {
413                 if (obj->what_is != FOOD) {
414                         obj->identified = 1;
415                         val = get_value(obj);
416                         rogue.gold += val;
417
418                         if (row < DROWS) {
419                                 sprintf(buf, "%5d      ", val);
420                                 get_desc(obj, buf+11);
421                                 mvaddstr(row++, 0, buf);
422                         }
423                 }
424                 obj = obj->next_object;
425         }
426         refresh();
427         if (rogue.gold > MAX_GOLD) {
428                 rogue.gold = MAX_GOLD;
429         }
430         message("", 0);
431 }
432
433 static int
434 get_value(const object *obj)
435 {
436         short wc;
437         int val;
438
439         val = 0;
440         wc = obj->which_kind;
441
442         switch(obj->what_is) {
443         case WEAPON:
444                 val = id_weapons[wc].value;
445                 if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) ||
446                         (wc == DART)) {
447                         val *= obj->quantity;
448                 }
449                 val += (obj->d_enchant * 85);
450                 val += (obj->hit_enchant * 85);
451                 break;
452         case ARMOR:
453                 val = id_armors[wc].value;
454                 val += (obj->d_enchant * 75);
455                 if (obj->is_protected) {
456                         val += 200;
457                 }
458                 break;
459         case WAND:
460                 val = id_wands[wc].value * (obj->class + 1);
461                 break;
462         case SCROL:
463                 val = id_scrolls[wc].value * obj->quantity;
464                 break;
465         case POTION:
466                 val = id_potions[wc].value * obj->quantity;
467                 break;
468         case AMULET:
469                 val = 5000;
470                 break;
471         case RING:
472                 val = id_rings[wc].value * (obj->class + 1);
473                 break;
474         }
475         if (val <= 0) {
476                 val = 10;
477         }
478         return(val);
479 }
480
481 static void
482 id_all(void)
483 {
484         short i;
485
486         for (i = 0; i < SCROLS; i++) {
487                 id_scrolls[i].id_status = IDENTIFIED;
488         }
489         for (i = 0; i < WEAPONS; i++) {
490                 id_weapons[i].id_status = IDENTIFIED;
491         }
492         for (i = 0; i < ARMORS; i++) {
493                 id_armors[i].id_status = IDENTIFIED;
494         }
495         for (i = 0; i < WANDS; i++) {
496                 id_wands[i].id_status = IDENTIFIED;
497         }
498         for (i = 0; i < POTIONS; i++) {
499                 id_potions[i].id_status = IDENTIFIED;
500         }
501 }
502
503 static int
504 name_cmp(char *s1, const char *s2)
505 {
506         short i = 0;
507         int r;
508
509         while(s1[i] != ':') {
510                 i++;
511         }
512         s1[i] = 0;
513         r = strcmp(s1, s2);
514         s1[i] = ':';
515         return(r);
516 }
517
518 void
519 xxxx(char *buf, short n)
520 {
521         short i;
522         unsigned char c;
523
524         for (i = 0; i < n; i++) {
525
526                 /* It does not matter if accuracy is lost during this assignment */
527                 c = (unsigned char)xxx(0);
528
529                 buf[i] ^= c;
530         }
531 }
532
533 long
534 xxx(boolean st)
535 {
536         static long f, s;
537         long r;
538
539         if (st) {
540                 f = 37;
541                 s = 7;
542                 return(0L);
543         }
544         r = ((f * s) + 9337) % 8887;
545         f = s;
546         s = r;
547         return(r);
548 }
549
550 static void
551 nickize(char *buf, const char *score, const char *n_name)
552 {
553         short i = 15, j;
554
555         if (!n_name[0]) {
556                 strcpy(buf, score);
557         } else {
558                 strncpy(buf, score, 16);
559
560                 while (score[i] != ':') {
561                         i++;
562                 }
563
564                 strcpy(buf+15, n_name);
565                 j = strlen(buf);
566
567                 while (score[i]) {
568                         buf[j++] = score[i++];
569                 }
570                 buf[j] = 0;
571                 buf[79] = 0;
572         }
573 }
574
575 static void
576 center(short row, const char *buf)
577 {
578         short margin;
579
580         margin = ((DCOLS - strlen(buf)) / 2);
581         mvaddstr(row, margin, buf);
582 }
583
584 static void
585 sf_error(void)
586 {
587         md_lock(0);
588         message("", 1);
589         clean_up("sorry, score file is out of order");
590 }