1 /* Asteroids3D - a first person game of blowing up asteroids
2 * Copyright (C) 2000 Stuart Mark Pomerantz <smp [at] psc edu>
3 * Copyright © Jan Engelhardt <jengelh [at] gmx de>, 2003 - 2005
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * Stuart Pomerantz
21 * 3935 Stonecliffe drive
22 * Monroeville, PA 15146
23 */
24
25 #include <GL/glut.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <math.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include "asteroids3D.h"
35 #include "vecops.h"
36 #ifndef S_IRUGO
37 # define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
38 #endif
39 #ifndef S_IWUGO
40 # define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
41 #endif
42
43 // This stuff is for the score file
44 #define MAX_FIELD_LEN 64
45 #define MAX_LINE_LEN 256
46 #define SCORE_FILE "/tmp/asteroids-scores"
47 #define NUM_SCORES 10
48 #define SCORE_FILE_SEPARATOR_CHAR ','
49 #define SCORE_FILE_COMMENT_CHAR '#'
50 #define SCORE_FILE_ESCAPE_CHAR '\\'
51
52 typedef struct score_file_entry {
53 char name[MAX_FIELD_LEN], date[MAX_FIELD_LEN];
54 unsigned int score;
55 int new_score_flag; // this value isn't saved to disk
56 } ScoreFileEntry;
57
58 static int compare_scores(const void *, const void *);
59 static void read_score_file(void);
60 static void score_display(void);
61 static void score_keyboard_handler(unsigned char, int, int);
62 static void write_score_file(void);
63
64 // last but not least, what would a game be without a score
65 unsigned int score = 0;
66 double difficulty_multiplier = 1;
67
68 // Data structure to hold the scores read from the score file
69 static ScoreFileEntry score_entry[NUM_SCORES] = {};
70
71 //-----------------------------------------------------------------------------
add_rock_to_score(const Asteroid * a)72 void add_rock_to_score(const Asteroid *a)
73 {
74 /* Scoring works like this:
75 *
76 * - small rocks are worth more than big ones
77 * - fast rocks are worth more than slow ones
78 *
79 * so I am doing (100/size)*speed <=> (100 * speed / size)
80 *
81 * Adding one below so I do not inadvertantly
82 * score a zero for a stationary or slow asertoid.
83 */
84 score += 100.0 * (vec_length(&a->velocity) + 1) / (a->type + 1.0);
85 return;
86 }
87
read_score_file(void)88 static void read_score_file(void) {
89 char buf[MAX_LINE_LEN];
90 int i = 0;
91 FILE *fp;
92
93 if ((fp = fopen(SCORE_FILE, "r")) == NULL)
94 return;
95
96 while (i < NUM_SCORES && fgets(buf, sizeof(buf), fp) != NULL) {
97 char *bp = buf, *res;
98 ScoreFileEntry *se;
99
100 strip_nl(bp);
101 if (*bp == '#' || *bp == '\0')
102 continue;
103
104 se = &score_entry[i++];
105
106 if ((res = strsep(&bp, ",")) != NULL) {
107 strncpy(se->name, res, sizeof(se->name) - 1);
108 se->name[sizeof(se->name) - 1] = '\0';
109 }
110 if ((res = strsep(&bp, ",")) != NULL)
111 se->score = strtol(res, NULL, 0);
112 if (bp != NULL) {
113 strncpy(se->date, bp, sizeof(se->date) - 1);
114 se->date[sizeof(se->date) - 1] = '\0';
115 }
116
117 se->new_score_flag = 0;
118 }
119
120 fclose(fp);
121 return;
122 }
123
write_score_file(void)124 static void write_score_file(void)
125 {
126 FILE *fp;
127 int i;
128
129 if ((fp = fopen(SCORE_FILE, "w")) == NULL) {
130 fprintf(stderr, "Could not open %s for writing: %s\n",
131 SCORE_FILE, strerror(errno));
132 return;
133 }
134
135 fprintf(fp,
136 "#\n"
137 "# asteroids score file\n"
138 "#\n"
139 "# This file is automatically generated and will be "
140 "recreated if deleted.\n"
141 "#\n"
142 "# name,score,date\n"
143 );
144
145 for (i = 0; i < NUM_SCORES; ++i) {
146 ScoreFileEntry *se = &score_entry[i];
147 if (*se->name != '\0' && *se->date != '\0')
148 fprintf(fp, "%s,%u,%s\n", se->name,
149 se->score, se->date);
150 }
151
152 fclose(fp);
153 chmod(SCORE_FILE, S_IRUGO | S_IWUGO);
154 return;
155 }
156
compare_scores(const void * pa,const void * pb)157 static int compare_scores(const void *pa, const void *pb)
158 {
159 const ScoreFileEntry *a = pa, *b = pb;
160 return b->score - a->score;
161 }
162
init_score_display(void)163 void init_score_display(void)
164 {
165 ScoreFileEntry *se;
166 time_t now;
167 char *user;
168
169 /* The player's score is scaled to reflect the difficulty of the game
170 starting with more asteroids gets a higher score than starting with fewer.
171 NUM_ASTEROIDS asteroids is the "base" score. The difficulty multiplier is
172 already calculated at game start so that we can display the correct score
173 already in-game. */
174 score *= difficulty_multiplier;
175
176 read_score_file();
177 glutDisplayFunc(score_display);
178 glutKeyboardFunc(score_keyboard_handler);
179
180 if (score <= score_entry[NUM_SCORES-1].score)
181 return;
182
183 se = &score_entry[NUM_SCORES-1];
184
185 if ((user = getenv("USER")) != NULL)
186 strncpy(se->name, user, sizeof(se->name) - 1);
187 else
188 strncpy(se->name, "(unknown)", sizeof(se->name) - 1);
189
190 se->name[sizeof(se->name) - 1] = '\0';
191 se->score = score;
192 se->new_score_flag = 1;
193 now = time(NULL);
194 strftime(se->date, sizeof(se->date), "%B %d %Y, %H:%M",
195 localtime(&now));
196
197 qsort(score_entry, NUM_SCORES, sizeof(ScoreFileEntry), compare_scores);
198 write_score_file();
199 return;
200 }
201
score_keyboard_handler(unsigned char key,int x,int y)202 static void score_keyboard_handler(unsigned char key, int x, int y)
203 {
204 if (key == keymap.end_game)
205 exit(EXIT_SUCCESS);
206 return;
207 }
208
score_display(void)209 static void score_display(void)
210 {
211 char buf[MAX_LINE_LEN];
212 double r, g, b, a;
213 int i;
214
215 /* do some error checking for each frame. */
216 glutReportErrors();
217 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
218 draw_text_in_color(-0.2, 0.9, "Top Ten Scores", 0, 0, 1, 1);
219
220 for (i = 0; i < NUM_SCORES; ++i) {
221 ScoreFileEntry *se = &score_entry[i];
222
223 if (*se->name == '\0' || *se->date == '\0')
224 continue;
225
226 if (!se->new_score_flag) {
227 r = g = b = a = 1;
228 } else {
229 r = a = 1;
230 g = b = 0;
231 }
232
233 draw_text_in_color(-0.7, 0.7 - static_cast(double, i) /
234 NUM_SCORES, se->name, r, g, b, a);
235
236 snprintf(buf, sizeof(buf), "%u", se->score);
237 draw_text_in_color(-0.2, 0.7 - static_cast(double, i) /
238 NUM_SCORES, buf, r, g, b, a);
239
240 draw_text_in_color(0.2, 0.7 - static_cast(double, i) /
241 NUM_SCORES, se->date, r, g, b, a);
242 }
243
244 key_char2str(keymap.end_game, buf, sizeof(buf));
245 strcat(buf, " key exits");
246 draw_text_in_color(-0.2, -0.7, buf, 0.5, 0.5, 0.5, 1);
247 glutSwapBuffers();
248 return;
249 }
250