Merge from vendor branch TNFTP:
[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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#)score.c  8.1 (Berkeley) 5/31/93
37  * $FreeBSD: src/games/rogue/score.c,v 1.4 1999/11/30 03:49:27 billf Exp $
38  * $DragonFly: src/games/rogue/score.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
39  */
40
41 /*
42  * score.c
43  *
44  * This source herein may be modified and/or distributed by anybody who
45  * so desires, with the following restrictions:
46  *    1.)  No portion of this notice shall be removed.
47  *    2.)  Credit shall not be taken for the creation of this source.
48  *    3.)  This code is not to be traded, sold, or used for personal
49  *         gain or profit.
50  *
51  */
52
53 #include <stdio.h>
54 #include "rogue.h"
55 #include "pathnames.h"
56
57 extern char *m_names[];
58 extern short max_level;
59 extern boolean score_only, no_skull, msg_cleared;
60 extern char *byebye_string, *nick_name;
61
62 static void     insert_score(char [][82], char [][30], const char *, short, short,
63                              const object *, short);
64 static void     sell_pack(void);
65 static int      get_value(const object *);
66 static void     id_all(void);
67 static int      name_cmp(char *, const char *);
68 static void     nickize(char *, const char *, const char *);
69 static void     center(short, const char *);
70 static void     sf_error(void);
71
72 void
73 killed_by(const object *monster, short other)
74 {
75         char buf[128];
76
77         md_ignore_signals();
78
79         if (other != QUIT) {
80                 rogue.gold = ((rogue.gold * 9) / 10);
81         }
82
83         if (other) {
84                 switch(other) {
85                 case HYPOTHERMIA:
86                         strcpy(buf, "died of hypothermia");
87                         break;
88                 case STARVATION:
89                         strcpy(buf, "died of starvation");
90                         break;
91                 case POISON_DART:
92                         strcpy(buf, "killed by a dart");
93                         break;
94                 case QUIT:
95                         strcpy(buf, "quit");
96                         break;
97                 case KFIRE:
98                         strcpy(buf, "killed by fire");
99                         break;
100                 }
101         } else {
102                 strcpy(buf, "Killed by ");
103                 if (is_vowel(m_names[monster->m_char - 'A'][0])) {
104                         strcat(buf, "an ");
105                 } else {
106                         strcat(buf, "a ");
107                 }
108                 strcat(buf, m_names[monster->m_char - 'A']);
109         }
110         strcat(buf, " with ");
111         sprintf(buf+strlen(buf), "%ld gold", rogue.gold);
112         if ((!other) && (!no_skull)) {
113                 clear();
114                 mvaddstr(4, 32, "__---------__");
115                 mvaddstr(5, 30, "_~             ~_");
116                 mvaddstr(6, 29, "/                 \\");
117                 mvaddstr(7, 28, "~                   ~");
118                 mvaddstr(8, 27, "/                     \\");
119                 mvaddstr(9, 27, "|    XXXX     XXXX    |");
120                 mvaddstr(10, 27, "|    XXXX     XXXX    |");
121                 mvaddstr(11, 27, "|    XXX       XXX    |");
122                 mvaddstr(12, 28, "\\         @         /");
123                 mvaddstr(13, 29, "--\\     @@@     /--");
124                 mvaddstr(14, 30, "| |    @@@    | |");
125                 mvaddstr(15, 30, "| |           | |");
126                 mvaddstr(16, 30, "| vvVvvvvvvvVvv |");
127                 mvaddstr(17, 30, "|  ^^^^^^^^^^^  |");
128                 mvaddstr(18, 31, "\\_           _/");
129                 mvaddstr(19, 33, "~---------~");
130                 center(21, nick_name);
131                 center(22, buf);
132         } else {
133                 message(buf, 0);
134         }
135         message("", 0);
136         put_scores(monster, other);
137 }
138
139 void
140 win(void)
141 {
142         unwield(rogue.weapon);          /* disarm and relax */
143         unwear(rogue.armor);
144         un_put_on(rogue.left_ring);
145         un_put_on(rogue.right_ring);
146
147         clear();
148         mvaddstr(10, 11, "@   @  @@@   @   @      @  @  @   @@@   @   @   @");
149         mvaddstr(11, 11, " @ @  @   @  @   @      @  @  @  @   @  @@  @   @");
150         mvaddstr(12, 11, "  @   @   @  @   @      @  @  @  @   @  @ @ @   @");
151         mvaddstr(13, 11, "  @   @   @  @   @      @  @  @  @   @  @  @@");
152         mvaddstr(14, 11, "  @    @@@    @@@        @@ @@    @@@   @   @   @");
153         mvaddstr(17, 11, "Congratulations,  you have  been admitted  to  the");
154         mvaddstr(18, 11, "Fighters' Guild.   You return home,  sell all your");
155         mvaddstr(19, 11, "treasures at great profit and retire into comfort.");
156         message("", 0);
157         message("", 0);
158         id_all();
159         sell_pack();
160         put_scores((object *) 0, WIN);
161 }
162
163 void
164 quit(boolean from_intrpt)
165 {
166         char buf[128];
167         short i, orow, ocol;
168         boolean mc;
169  
170         orow = ocol = mc = 0;
171
172         md_ignore_signals();
173
174         if (from_intrpt) {
175                 orow = rogue.row;
176                 ocol = rogue.col;
177
178                 mc = msg_cleared;
179
180                 for (i = 0; i < DCOLS; i++) {
181                         buf[i] = mvinch(0, i);
182                 }
183         }
184         check_message();
185         message("really quit?", 1);
186         if (rgetchar() != 'y') {
187                 md_heed_signals();
188                 check_message();
189                 if (from_intrpt) {
190                         for (i = 0; i < DCOLS; i++) {
191                                 mvaddch(0, i, buf[i]);
192                         }
193                         msg_cleared = mc;
194                         move(orow, ocol);
195                         refresh();
196                 }
197                 return;
198         }
199         if (from_intrpt) {
200                 clean_up(byebye_string);
201         }
202         check_message();
203         killed_by((object *) 0, QUIT);
204 }
205
206 void
207 put_scores(const object *monster, short other)
208 {
209         short i, n, rank = 10, x, ne = 0, found_player = -1;
210         char scores[10][82];
211         char n_names[10][30];
212         char buf[128];
213         FILE *fp;
214         long s;
215         boolean pause = score_only;
216
217         md_lock(1);
218
219         if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
220             (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
221                 message("cannot read/write/create score file", 0);
222                 sf_error();
223         }
224         rewind(fp);
225         xxx(1);
226
227         for (i = 0; i < 10; i++) {
228                 if (((n = fread(scores[i], sizeof(char), 80, fp)) < 80) && (n != 0)) {
229                         sf_error();
230                 } else if (n != 0) {
231                         xxxx(scores[i], 80);
232                         if ((n = fread(n_names[i], sizeof(char), 30, fp)) < 30) {
233                                 sf_error();
234                         }
235                         xxxx(n_names[i], 30);
236                 } else {
237                         break;
238                 }
239                 ne++;
240                 if ((!score_only) && (found_player == -1)) {
241                         if (!name_cmp(scores[i]+15, login_name)) {
242                                 x = 5;
243                                 while (scores[i][x] == ' ') {
244                                         x++;
245                                 }
246                                 s = lget_number(scores[i] + x);
247                                 if (rogue.gold < s) {
248                                         score_only = 1;
249                                 } else {
250                                         found_player = i;
251                                 }
252                         }
253                 }
254         }
255         if (found_player != -1) {
256                 ne--;
257                 for (i = found_player; i < ne; i++) {
258                         strcpy(scores[i], scores[i+1]);
259                         strcpy(n_names[i], n_names[i+1]);
260                 }
261         }
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 = 0;
438
439         wc = obj->which_kind;
440
441         switch(obj->what_is) {
442         case WEAPON:
443                 val = id_weapons[wc].value;
444                 if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) ||
445                         (wc == DART)) {
446                         val *= obj->quantity;
447                 }
448                 val += (obj->d_enchant * 85);
449                 val += (obj->hit_enchant * 85);
450                 break;
451         case ARMOR:
452                 val = id_armors[wc].value;
453                 val += (obj->d_enchant * 75);
454                 if (obj->is_protected) {
455                         val += 200;
456                 }
457                 break;
458         case WAND:
459                 val = id_wands[wc].value * (obj->class + 1);
460                 break;
461         case SCROL:
462                 val = id_scrolls[wc].value * obj->quantity;
463                 break;
464         case POTION:
465                 val = id_potions[wc].value * obj->quantity;
466                 break;
467         case AMULET:
468                 val = 5000;
469                 break;
470         case RING:
471                 val = id_rings[wc].value * (obj->class + 1);
472                 break;
473         }
474         if (val <= 0) {
475                 val = 10;
476         }
477         return(val);
478 }
479
480 static void
481 id_all(void)
482 {
483         short i;
484
485         for (i = 0; i < SCROLS; i++) {
486                 id_scrolls[i].id_status = IDENTIFIED;
487         }
488         for (i = 0; i < WEAPONS; i++) {
489                 id_weapons[i].id_status = IDENTIFIED;
490         }
491         for (i = 0; i < ARMORS; i++) {
492                 id_armors[i].id_status = IDENTIFIED;
493         }
494         for (i = 0; i < WANDS; i++) {
495                 id_wands[i].id_status = IDENTIFIED;
496         }
497         for (i = 0; i < POTIONS; i++) {
498                 id_potions[i].id_status = IDENTIFIED;
499         }
500 }
501
502 static int
503 name_cmp(char *s1, const char *s2)
504 {
505         short i = 0;
506         int r;
507
508         while(s1[i] != ':') {
509                 i++;
510         }
511         s1[i] = 0;
512         r = strcmp(s1, s2);
513         s1[i] = ':';
514         return(r);
515 }
516
517 void
518 xxxx(char *buf, short n)
519 {
520         short i;
521         unsigned char c;
522
523         for (i = 0; i < n; i++) {
524
525                 /* It does not matter if accuracy is lost during this assignment */
526                 c = (unsigned char) xxx(0);
527
528                 buf[i] ^= c;
529         }
530 }
531
532 long
533 xxx(boolean st)
534 {
535         static long f, s;
536         long r;
537
538         if (st) {
539                 f = 37;
540                 s = 7;
541                 return(0L);
542         }
543         r = ((f * s) + 9337) % 8887;
544         f = s;
545         s = r;
546         return(r);
547 }
548
549 static void
550 nickize(char *buf, const char *score, const char *n_name)
551 {
552         short i = 15, j;
553
554         if (!n_name[0]) {
555                 strcpy(buf, score);
556         } else {
557                 strncpy(buf, score, 16);
558
559                 while (score[i] != ':') {
560                         i++;
561                 }
562
563                 strcpy(buf+15, n_name);
564                 j = strlen(buf);
565
566                 while (score[i]) {
567                         buf[j++] = score[i++];
568                 }
569                 buf[j] = 0;
570                 buf[79] = 0;
571         }
572 }
573
574 static void
575 center(short row, const char *buf)
576 {
577         short margin;
578
579         margin = ((DCOLS - strlen(buf)) / 2);
580         mvaddstr(row, margin, buf);
581 }
582
583 static void
584 sf_error(void)
585 {
586         md_lock(0);
587         message("", 1);
588         clean_up("sorry, score file is out of order");
589 }