1 /*
2  *                               Alizarin Tetris
3  * For the management of high scores.
4  *
5  * Copyright 2000, Kiri Wagstaff & Westley Weimer
6  */
7 
8 #define BUFSIZE 256 /* 255 letters for each name */
9 
10 #define NUM_HIGH_SCORES 10 /* number of scores to save */
11 
12 #include <config.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "atris.h"
17 #include "button.h"
18 #include "display.h"
19 #include "identity.h"
20 #include "grid.h"
21 #include "highscore.h"
22 
23 #include "display.pro"
24 #include "identity.pro"
25 
26 static char  loaded = FALSE;
27 static int   high_scores[NUM_HIGH_SCORES];
28 static char* high_names[NUM_HIGH_SCORES];
29 static char* high_dates[NUM_HIGH_SCORES];
30 
31 static SDL_Rect hs, hs_border;	/* where are the high scores? */
32 static int score_height;	/* how high is each score? */
33 
34 #define FIRST_SCORE_Y	(hs.y + 60)
35 
36 /***************************************************************************
37  *      prep_hs_bg()
38  * Prepare the high score background.
39  ***************************************************************************/
40 static void
prep_hs_bg()41 prep_hs_bg()
42 {
43     hs.x = screen->w/20; hs.y = screen->h/20;
44     hs.w = 9*screen->w/10; hs.h = 18*screen->h/20;
45 
46     draw_bordered_rect(&hs, &hs_border, 2);
47 }
48 
49 /***************************************************************************
50  *      save_high_scores()
51  * Save the high scores out to the disk.
52  ***************************************************************************/
53 static void
save_high_scores()54 save_high_scores()
55 {
56     FILE *fout;
57     int i;
58 
59     if (!loaded)
60 	return;
61 
62     fout = fopen("Atris.Scores","wt");
63     if (!fout) {
64 	Debug("Unable to write High Score file [Atris.Scores]: %s\n", strerror(errno));
65 	return;
66     }
67 
68     fprintf(fout,"# Alizarin Tetris High Score File\n");
69     for (i=0; i<NUM_HIGH_SCORES; i++)
70 	fprintf(fout,"%04d|%s|%s\n",high_scores[i], high_dates[i], high_names[i]);
71     fclose(fout);
72 }
73 
74 
75 /***************************************************************************
76  *      load_high_scores()
77  * Load the high scores from disk (and allocate space).
78  ***************************************************************************/
79 static void
load_high_scores()80 load_high_scores()
81 {
82     FILE* fin;
83     int i = 0;
84     char buf[2048];
85 
86     if (!loaded) {
87 	/* make up a dummy high score template first */
88 
89 	for (i=0; i<NUM_HIGH_SCORES; i++) {
90 	    high_names[i] = strdup("No one yet..."); Assert(high_names[i]);
91 	    high_dates[i] = strdup("Never"); Assert(high_dates[i]);
92 	    high_scores[i] = 0;
93 	}
94 	loaded = TRUE;
95     }
96 
97     fin = fopen("Atris.Scores", "r");
98     if (fin) {
99 
100 	for (i=0; !feof(fin) && i < NUM_HIGH_SCORES; i++) {
101 	    char *p, *q;
102 	    /* read in a line of text, but skip comments and blanks */
103 	    do {
104 		fgets(buf, sizeof(buf), fin);
105 	    } while (!feof(fin) && (buf[0] == '\n' || buf[0] == '#'));
106 	    /* are we done with this file? */
107 	    if (feof(fin)) break;
108 	    /* strip the newline */
109 	    if (strchr(buf,'\n'))
110 		*(strchr(buf,'\n')) = 0;
111 	    /* format: "score|date|name" */
112 
113 	    sscanf(buf,"%d",&high_scores[i]);
114 	    p = strchr(buf,'|');
115 	    if (!p) break;
116 	    p++;
117 	    q = strchr(p, '|');
118 	    if (!q) break;
119 	    Free(high_dates[i]); Free(high_names[i]);
120 	    *q = 0;
121 	    high_dates[i] = strdup(p); Assert(high_dates[i]);
122 	    q++;
123 	    high_names[i] = strdup(q); Assert(high_names[i]);
124 	}
125 	fclose(fin);
126     }
127 }
128 
129 /***************************************************************************
130  *      show_high_scores()
131  * Display the current high score list.
132  ***************************************************************************/
133 static void
show_high_scores()134 show_high_scores()
135 {
136   char buf[256];
137   int i, base;
138   int delta;
139 
140   if (!loaded) load_high_scores();
141   prep_hs_bg();
142 
143   draw_string("Alizarin Tetris High Scores", color_purple, screen->w/2,
144 	  hs.y, DRAW_LARGE | DRAW_UPDATE | DRAW_CENTER );
145 
146   base = FIRST_SCORE_Y;
147 
148   for (i=0; i<NUM_HIGH_SCORES; i++)
149     {
150       SPRINTF(buf, "%-2d)", i+1);
151       delta = draw_string(buf, color_blue, 3*screen->w/40, base, DRAW_UPDATE );
152 
153       score_height = delta + 3;
154 
155       SPRINTF(buf, "%-20s", high_names[i]);
156       draw_string(buf, color_red, 3*screen->w/20, base, DRAW_UPDATE );
157 
158       SPRINTF(buf, "%.4d", high_scores[i]);
159       draw_string(buf, color_blue, 11*screen->w/20, base, DRAW_UPDATE );
160 
161       SPRINTF(buf, "%s", high_dates[i]);
162       draw_string(buf, color_red, 7*screen->w/10, base, DRAW_UPDATE );
163 
164       base += score_height;
165     }
166 }
167 
168 /***************************************************************************
169  *      is_high_score()
170  * Checks whether score qualifies as a high score.
171  * Returns 1 if so, 0 if not.
172  ***************************************************************************/
173 static int
is_high_score(int score)174 is_high_score(int score)
175 {
176   if (!loaded) load_high_scores();
177   return (score >= high_scores[NUM_HIGH_SCORES-1]);
178 }
179 
180 /***************************************************************************
181  *      update_high_scores()
182  * Modifies the high score list, adding in the current score and
183  * querying for a name.
184  ***************************************************************************/
185 static void
update_high_scores(int score)186 update_high_scores(int score)
187 {
188   unsigned int i, j;
189   char buf[256];
190 #if HAVE_STRFTIME
191   const struct tm* tm;
192   time_t t;
193 #endif
194 
195   if (!is_high_score(score)) return;
196   if (!loaded) load_high_scores();
197 
198   for (i=0; i<NUM_HIGH_SCORES; i++)
199     {
200       if (score >= high_scores[i])
201 	{
202 	  prep_hs_bg();
203 	  /* move everything down */
204 
205 	  Free(high_names[NUM_HIGH_SCORES-1]);
206 	  Free(high_dates[NUM_HIGH_SCORES-1]);
207 
208 	  for (j=NUM_HIGH_SCORES-1; j>i; j--)
209 	    {
210 	      high_scores[j] = high_scores[j-1];
211 	      high_names[j] = high_names[j-1];
212 	      high_dates[j] = high_dates[j-1];
213 	    }
214 	  /* Get the date */
215 #if HAVE_STRFTIME
216 	  t = time(NULL); tm = localtime(&t);
217 	  strftime(buf, sizeof(buf), "%b %d %H:%M", tm);
218 	  high_dates[i] = strdup(buf);
219 #else
220 #warning  "Since you do not have strftime(), you will not have accurate times in the high score list."
221 	  high_dates[i] = "Unknown Time";
222 #endif
223 
224 	  high_scores[i] = score;
225 	  /* Display the high scores */
226 	  high_names[i] = " ";
227 	  show_high_scores();
228 	  /* get the new name */
229 
230 	  draw_string("Enter your name!", color_purple, screen->w / 2, hs.y
231 		  + hs.h - 10, DRAW_CENTER | DRAW_ABOVE | DRAW_UPDATE);
232 
233 	  high_names[i] = input_string(screen, 3*screen->w/20,
234 		  FIRST_SCORE_Y + score_height * i, 1);
235 	  break;
236 	}
237     }
238   save_high_scores();
239 }
240 
241 /***************************************************************************
242  *      high_score_check()
243  * Checks for a high score; if so, updates the list. Displays list.
244  *********************************************************************PROTO*/
high_score_check(int level,int new_score)245 void high_score_check(int level, int new_score)
246 {
247   SDL_Event event;
248 
249   clear_screen_to_flame();
250 
251   if (level < 0) {
252       return;
253   }
254 
255   /* Clear the queue */
256   while (SDL_PollEvent(&event)) {
257       poll_and_flame(&event);
258   }
259 
260   /* Check for a new high score.  If so, update the list. */
261   if (is_high_score(new_score)) {
262       update_high_scores(new_score);
263       show_high_scores();
264   } else {
265       char buf[BUFSIZE];
266 
267       show_high_scores();
268       SPRINTF(buf,"Your score: %d", new_score);
269 
270       draw_string(buf, color_purple, screen->w / 2, hs.y
271 	      + hs.h - 10, DRAW_CENTER | DRAW_ABOVE | DRAW_UPDATE);
272 
273     }
274   /* wait for any key */
275   do {
276       poll_and_flame(&event);
277   } while (event.type != SDL_KEYDOWN);
278 }
279 
280 /*
281  * $Log: highscore.c,v $
282  * Revision 1.31  2000/11/06 01:22:40  wkiri
283  * Updated menu system.
284  *
285  * Revision 1.30  2000/10/29 21:23:28  weimer
286  * One last round of header-file changes to reflect my newest and greatest
287  * knowledge of autoconf/automake. Now if you fail to have some bizarro
288  * function, we try to go on anyway unless it is vastly needed.
289  *
290  * Revision 1.29  2000/10/29 19:04:33  weimer
291  * minor highscore handling changes: new filename, use the draw_string() and
292  * draw_bordered_rect() and input_string() interfaces, handle the widget layer
293  * and the flame layer, etc. Also fix a minor bug where you would be prevented
294  * from settling if you pressed a key even if it didn't really move you. :-)
295  *
296  * Revision 1.28  2000/10/21 01:14:43  weimer
297  * massic autoconf/automake restructure ...
298  *
299  * Revision 1.27  2000/10/18 23:57:49  weimer
300  * general fixup, color changes, display changes.
301  * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors,
302  * we hope!
303  *
304  * Revision 1.26  2000/09/09 17:05:35  wkiri
305  * Hideous log changes (Wes: how dare you include a comment character!)
306  *
307  * Revision 1.25  2000/09/09 16:58:27  weimer
308  * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up
309  * main(), isolate the behavior of the three game types. Move graphic files
310  * into graphics/-, style files into styles/-, remove some unused files,
311  * add game flow support (breaks between games, remembering your level within
312  * this game), multiplayer support for the event loop, some global variable
313  * cleanup. All that and a bag of chips!
314  *
315  * Revision 1.24  2000/09/04 19:48:02  weimer
316  * interim menu for choosing among game styles, button changes (two states)
317  *
318  * Revision 1.23  2000/09/03 19:41:30  wkiri
319  * Now allows you to choose the game type (though it doesn't do anything yet).
320  *
321  * Revision 1.22  2000/09/03 18:44:36  wkiri
322  * Cleaned up atris.c (see high_score_check()).
323  * Added game type (defaults to MARATHON).
324  *
325  * Revision 1.21  2000/09/03 18:26:10  weimer
326  * major header file and automatic prototype generation changes, restructuring
327  *
328  * Revision 1.20  2000/09/03 17:56:36  wkiri
329  * Reorganization of files (display.[ch], event.c are new).
330  *
331  * Revision 1.19  2000/08/26 03:11:34  wkiri
332  * Buttons now use borders.
333  *
334  * Revision 1.18  2000/08/26 02:45:28  wkiri
335  * Beginnings of button class; also modified atris to query for 'new
336  * game' vs. 'quit'.  (New Game doesn't work yet...)
337  *
338  * Revision 1.17  2000/08/26 00:54:00  wkiri
339  * Now high-scoring user enters name at the proper place.
340  * Also, score is shown even if user doesn't make the high score list.
341  *
342  * Revision 1.16  2000/08/20 23:45:58  wkiri
343  * High scores reports the hour:minute rather than the year.
344  *
345  * Revision 1.15  2000/08/20 23:39:00  wkiri
346  * Different color for the scores in the score display; positions adjusted.
347  *
348  * Revision 1.14  2000/08/20 19:00:29  weimer
349  * text output changes (moved from _Solid to _Blended)
350  *
351  * Revision 1.13  2000/08/20 17:16:25  wkiri
352  * Can quit using 'q' and this will skip the high score list.
353  * Adjusted the high score display positions to fit in the window.
354  *
355  * Revision 1.12  2000/08/20 16:49:52  wkiri
356  * Now supports dates as well.
357  *
358  * Revision 1.11  2000/08/20 16:20:46  wkiri
359  * Now displays the new score correctly.
360  *
361  * Revision 1.10  2000/08/20 16:16:04  wkiri
362  * Better display of high scores.
363  *
364  * Revision 1.9  2000/08/15 01:58:11  wkiri
365  * Not allowed to backspace before the beginning of the array! :)
366  * Handles this gracefully (i.e. ignores any such keypresses)
367  * and clears the display correctly even when fewer letters are present
368  * than were present previously (when BS is pressed).
369  *
370  * Revision 1.8  2000/08/15 01:30:33  wkiri
371  * Handles shift keys (i.e. empty messages) properly.
372  *
373  * Revision 1.7  2000/08/15 01:22:59  wkiri
374  * High scores can handle punctuation, backspace, and spaces in names.
375  * Also, display is nicer (got rid of the weird boxes).
376  *
377  * Revision 1.6  2000/08/15 00:52:33  wkiri
378  * Ooops! Now properly checks for new high scores.
379  *
380  * Revision 1.5  2000/08/15 00:50:49  wkiri
381  * Now displays your high score name as you type it in.
382  * No support for backspace, though.
383  *
384  * Revision 1.4  2000/08/15 00:37:01  wkiri
385  * Wes found nasty memory bug!  Wes is my hero!
386  * Also, changes to high scores to get input from the window.
387  *
388  * Revision 1.3  2000/08/15 00:22:15  wkiri
389  * Now handles non-regular-ascii input to high scores.
390  *
391  * Revision 1.2  2000/08/14 23:37:06  wkiri
392  * Removed debugging output from high score module.
393  *
394  * Revision 1.1  2000/08/14 01:07:17  wkiri
395  * High score files.
396  *
397  */
398 
399