1 /*
2 * Alizarin Tetris
3 * The user identity file.
4 *
5 * Copyright 2000, Kiri Wagstaff & Westley Weimer
6 */
7
8 #include <config.h> /* go autoconf! */
9 #include <ctype.h>
10
11 #include "atris.h"
12 #include "display.h"
13 #include "grid.h"
14 #include "identity.h"
15 #include "menu.h"
16
17 #include "xflame.pro"
18 #include "display.pro"
19
20 #define ID_FILENAME "Atris.Players"
21
22 /***************************************************************************
23 * input_string()
24 * Read input from the user ... on the widget layer.
25 *********************************************************************PROTO*/
26 char *
input_string(SDL_Surface * screen,int x,int y,int opaque)27 input_string(SDL_Surface *screen, int x, int y, int opaque)
28 {
29 int pos;
30 char c;
31 char retval[1024];
32 SDL_Surface *text, *ctext;
33 SDL_Color tc, cc;
34 SDL_Rect rect;
35 SDL_Event event;
36 Uint32 text_color = int_input_color0;
37 Uint32 cursor_color = int_input_color1;
38 Uint32 our_black = opaque ? int_solid_black : int_black;
39
40 memset(retval, 0, sizeof(retval));
41 retval[0] = ' ';
42 pos = 1;
43
44 SDL_GetRGB(text_color, screen->format, &tc.r, &tc.g, &tc.b);
45 SDL_GetRGB(cursor_color, screen->format, &cc.r, &cc.g, &cc.b);
46
47 ctext = TTF_RenderText_Blended(font, "_", cc); Assert(ctext);
48
49 while (1) {
50 int changed = 0; /* did they change the text string? */
51 int blink = 1; /* toggle the blinking the cursor */
52 Uint32 flip_when = SDL_GetTicks();
53 /* display the current string */
54
55 text = TTF_RenderText_Blended(font, retval, tc); Assert(text);
56
57 rect.x = x;
58 rect.y = y;
59 rect.w = text->w;
60 rect.h = text->h;
61
62 SDL_BlitSurface(text, NULL, widget_layer, &rect);
63 /* OK to ignore the intervening flame layer */
64 SDL_BlitSurface(text, NULL, screen, &rect);
65 SDL_UpdateSafe(screen, 1, &rect);
66
67 rect.x += rect.w;
68 rect.w = ctext->w;
69 rect.h = ctext->h;
70
71 changed = 0;
72 while (!changed) {
73 if (SDL_GetTicks() > flip_when) {
74 if (blink) {
75 SDL_BlitSurface(ctext, NULL, screen, &rect);
76 SDL_BlitSurface(ctext, NULL, widget_layer, &rect);
77 } else {
78 SDL_FillRect(widget_layer, &rect, our_black);
79 SDL_FillRect(screen, &rect, our_black);
80 SDL_BlitSurface(flame_layer, &rect, screen, &rect);
81 SDL_BlitSurface(widget_layer, &rect, screen, &rect);
82 }
83 SDL_UpdateSafe(screen, 1, &rect);
84 flip_when = SDL_GetTicks() + 400;
85 blink = !blink;
86 }
87 if (SDL_PollEvent(&event)) {
88 if (event.type == SDL_KEYDOWN) {
89 changed = 1;
90 switch (event.key.keysym.sym) {
91 case SDLK_RETURN:
92 return strdup(retval + 1);
93
94 case SDLK_BACKSPACE:
95 if (pos > 1) pos--;
96 retval[pos] = 0;
97
98 rect.x = x;
99 rect.w = text->w + ctext->w;
100 SDL_FillRect(widget_layer, &rect, our_black);
101 SDL_FillRect(screen, &rect, our_black);
102 SDL_BlitSurface(flame_layer, &rect, screen, &rect);
103 SDL_BlitSurface(widget_layer, &rect, screen, &rect);
104 SDL_UpdateSafe(screen, 1, &rect);
105 break;
106
107 default:
108 c = event.key.keysym.unicode;
109 if (c == 0) break;
110
111 SDL_FillRect(widget_layer, &rect, our_black);
112 SDL_FillRect(screen, &rect, our_black);
113 SDL_BlitSurface(flame_layer, &rect, screen, &rect);
114 SDL_BlitSurface(widget_layer, &rect, screen, &rect);
115 SDL_UpdateSafe(screen, 1, &rect);
116
117 if (isalpha(c) || isdigit(c) || isspace(c) || ispunct(c))
118 retval[pos++] = c;
119 break;
120 }
121 }
122 } else atris_run_flame();
123 }
124 SDL_FreeSurface(text);
125 }
126 }
127
128 /***************************************************************************
129 * load_identity_file()
130 * Parse an identity file.
131 *********************************************************************PROTO*/
132 identity *
load_identity_file()133 load_identity_file()
134 {
135 identity *retval;
136 char buf[2048];
137
138 FILE *fin = fopen(ID_FILENAME, "rt");
139 int count = 0;
140 int i;
141
142 if (!fin) {
143 Debug("fopen(%s)\n", ID_FILENAME);
144 Debug("Cannot open Identity File.\n");
145
146 Calloc(retval,identity *,sizeof(identity));
147 return retval;
148 }
149 Calloc(retval,identity *,sizeof(identity));
150
151 while (!(feof(fin))) {
152 do {
153 fgets(buf,sizeof(buf),fin);
154 } while (!feof(fin) &&
155 (buf[0] == '\n' || buf[0] == '#'));
156 if (feof(fin)) break;
157 if (strchr(buf,'\n'))
158 *(strchr(buf,'\n')) = 0;
159 count++;
160 }
161 rewind(fin);
162
163 retval->n = count;
164
165 if (!count) {
166 /* do nothing */
167 } else {
168 Calloc(retval->p,person *,sizeof(person)*count);
169 i = 0;
170 while (!(feof(fin))) {
171 char *p;
172 do {
173 fgets(buf,sizeof(buf),fin);
174 } while (!feof(fin) &&
175 (buf[0] == '\n' || buf[0] == '#'));
176 if (feof(fin)) break;
177 if (strchr(buf,'\n'))
178 *(strchr(buf,'\n')) = 0;
179
180 sscanf(buf,"%d",&retval->p[i].level);
181 p = strchr(buf,' ');
182 if (!p) {
183 retval->p[i].name = "-garbled-";
184 } else {
185 retval->p[i].name = strdup( p + 1 );
186 }
187 i++;
188 }
189 }
190 fclose(fin);
191
192 Debug("Identity File [%s] loaded (%d players).\n",ID_FILENAME, count);
193
194 return retval;
195 }
196
197 /***************************************************************************
198 * save_identity_file()
199 * Saves the information to an identity file.
200 *********************************************************************PROTO*/
201 void
save_identity_file(identity * id,char * new_name,int new_level)202 save_identity_file(identity *id, char *new_name, int new_level)
203 {
204 FILE *fin = fopen(ID_FILENAME,"wt");
205 int i;
206 if (!fin) {
207 Debug("fopen(%s): cannot write Identity File.\n",ID_FILENAME);
208 return;
209 }
210 fprintf(fin,"# Alizarin Tetris Identity File\n"
211 "#\n"
212 "# Format:\n"
213 "#[level] [name] (no space before level, one space after)\n"
214 );
215 for (i=0; i<id->n ;i++) {
216 fprintf(fin,"%d %s\n",id->p[i].level, id->p[i].name);
217 }
218 if (new_name) {
219 fprintf(fin,"%d %s\n",new_level, new_name);
220 }
221
222 #ifdef DEBUG
223 Debug("Identity File [%s] saved (%d players).\n",ID_FILENAME, id->n +
224 (new_name != NULL));
225 #endif
226 fclose(fin);
227 return;
228 }
229
230 /***************************************************************************
231 * network_choice_action()
232 * What to do when they click on the network choice button ...
233 ***************************************************************************/
234 static int
network_choice_action(WalkRadio * wr)235 network_choice_action(WalkRadio *wr)
236 {
237 if (wr->defaultchoice == 0) {
238 /* I am client */
239 clear_screen_to_flame();
240 draw_string("Who is the Server?", color_network_menu, screen->w/2,
241 screen->h/2, DRAW_LEFT | DRAW_UPDATE);
242 wr->data = input_string(screen, screen->w/2, screen->h/2, 0);
243 } else if (wr->data) {
244 Free(wr->data);
245 } else
246 wr->data = NULL;
247 return 1;
248 }
249
250 /***************************************************************************
251 * network_choice()
252 * Do you want to be the client or the server? Returns the hostname.
253 *********************************************************************PROTO*/
254 char *
network_choice(SDL_Surface * screen)255 network_choice(SDL_Surface *screen)
256 {
257 static WalkRadioGroup *wrg = NULL;
258 SDL_Event event;
259
260 if (!wrg) {
261 wrg = create_single_wrg( 2 ) ;
262 wrg->wr[0].label[0] = "I am the Client. I will specify a server.";
263 wrg->wr[0].label[1] = "I will be the Server.";
264
265 setup_radio(&wrg->wr[0]);
266 wrg->wr[0].x = (screen->w - wrg->wr[0].area.w) / 2;
267 wrg->wr[0].y = (screen->h - wrg->wr[0].area.h) / 2;
268 wrg->wr[0].action = network_choice_action;
269 }
270
271 clear_screen_to_flame();
272
273 draw_string("Who is the Server?", color_network_menu,
274 screen->w/2, wrg->wr[0].y, DRAW_CENTER | DRAW_UPDATE | DRAW_ABOVE);
275
276 draw_radio(&wrg->wr[0], 1);
277
278 while (1) {
279 int retval;
280 poll_and_flame(&event);
281 retval = handle_radio_event(wrg, &event);
282 if (retval != -1)
283 return wrg->wr[0].data;
284 }
285 }
286
287 /***************************************************************************
288 * new_player()
289 * Add another player ... Displays a little dialog so that the new player
290 * can enter a new name and a new level.
291 **************************************************************************/
292 static
new_player(SDL_Surface * screen,identity ** id)293 void new_player(SDL_Surface * screen, identity **id)
294 {
295 char *new_name, *new_level;
296 int level;
297 clear_screen_to_flame();
298
299 draw_string("Enter your name:", color_who_are_you, screen->w / 2,
300 screen->h / 2, DRAW_CLEAR | DRAW_LEFT);
301
302 new_name = input_string(screen, screen->w/2, screen->h/2, 0);
303
304 if (strlen(new_name)) {
305 clear_screen_to_flame();
306 draw_string("Welcome ", color_purple, screen->w/2,
307 screen->h/2, DRAW_CLEAR | DRAW_LEFT | DRAW_LARGE | DRAW_ABOVE);
308 draw_string(new_name, color_purple, screen->w/2,
309 screen->h/2, DRAW_CLEAR | DRAW_LARGE | DRAW_ABOVE);
310 draw_string("Starting level (2-10):", color_who_are_you, screen->w
311 / 2, screen->h / 2, DRAW_CLEAR | DRAW_LEFT);
312 new_level = input_string(screen, screen->w/2, screen->h/2, 0);
313 level = 0;
314 sscanf(new_level,"%d",&level);
315 if (level < 2) level = 2;
316 if (level > 10) level = 10;
317
318 save_identity_file(*id, new_name, level);
319
320 (*id) = load_identity_file();
321
322 free(new_level);
323 }
324 free(new_name);
325 }
326
327
328 /***************************************************************************
329 * who_are_you()
330 * Asks the player to choose an identity ...
331 * Returns -1 on "cancel".
332 *********************************************************************PROTO*/
333 int
who_are_you(SDL_Surface * screen,identity ** id,int taken,int p)334 who_are_you(SDL_Surface *screen, identity **id, int taken, int p)
335 {
336 WalkRadioGroup *wrg = NULL;
337 int i;
338 int retval;
339 SDL_Event event;
340 char buf[1024];
341
342 restart: /* sigh! */
343 wrg = create_single_wrg((*id)->n + 2);
344 for (i=0; i<(*id)->n; i++) {
345 if (i == taken)
346 SPRINTF(buf,"%s (already taken!)",(*id)->p[i].name);
347 else
348 SPRINTF(buf,"%s (Level %d)",(*id)->p[i].name, (*id)->p[i].level);
349 wrg->wr[0].label[i] = strdup(buf);
350 }
351 wrg->wr[0].label[(*id)->n] = "-- New Player --";
352 wrg->wr[0].label[(*id)->n+1] = "-- Cancel --";
353
354 setup_radio(&wrg->wr[0]);
355 wrg->wr[0].x = (screen->w - wrg->wr[0].area.w) / 2;
356 wrg->wr[0].y = (screen->h - wrg->wr[0].area.h) / 2;
357 wrg->wr[0].action = NULL /* return default choice */;
358
359 clear_screen_to_flame();
360
361 if (p == 1) {
362 draw_string("Left = A Rotate = W Right = D Drop = S", color_keys_explain,
363 screen->w / 2, 0, DRAW_CENTER | DRAW_UPDATE);
364 draw_string("Player 1: Who Are You?", color_who_are_you,
365 screen->w / 2, wrg->wr[0].y - 30, DRAW_CENTER | DRAW_UPDATE);
366 } else {
367 draw_string("Use the Arrow keys. Rotate = Up Drop = Down", color_keys_explain,
368 screen->w / 2, 0, DRAW_CENTER | DRAW_UPDATE);
369 draw_string("Player 2: Who Are You?", color_who_are_you,
370 screen->w / 2, wrg->wr[0].y - 30, DRAW_CENTER | DRAW_UPDATE);
371 }
372
373 draw_radio(&wrg->wr[0], 1);
374
375 while (1) {
376 poll_and_flame(&event);
377
378 retval = handle_radio_event(wrg, &event);
379 if (retval == -1 || retval == taken)
380 continue;
381 if (retval == (*id)->n) {
382 new_player(screen, id);
383 goto restart;
384 }
385 if (retval == (*id)->n+1)
386 return -1;
387 return retval;
388 }
389 return 0;
390 }
391
392 /*
393 * $Log: identity.c,v $
394 * Revision 1.16 2000/11/06 04:44:15 weimer
395 * fixed that who-is-the-server thing
396 *
397 * Revision 1.15 2000/11/02 03:06:20 weimer
398 * better interface for walk-radio menus: we are now ready for Kiri to change
399 * them to add run-time options ...
400 *
401 * Revision 1.14 2000/11/01 03:53:06 weimer
402 * modifications for version 1.0.1: you can pick your starting level, you can
403 * pick the AI difficulty factor, the game is better about placing new pieces
404 * when there is garbage, when things fall out of your control they now fall
405 * at a uniform rate ...
406 *
407 * Revision 1.13 2000/10/29 21:28:58 weimer
408 * fixed a few failures to clear the screen if we didn't have a flaming
409 * backdrop
410 *
411 * Revision 1.12 2000/10/29 21:23:28 weimer
412 * One last round of header-file changes to reflect my newest and greatest
413 * knowledge of autoconf/automake. Now if you fail to have some bizarro
414 * function, we try to go on anyway unless it is vastly needed.
415 *
416 * Revision 1.11 2000/10/29 19:04:33 weimer
417 * minor highscore handling changes: new filename, use the draw_string() and
418 * draw_bordered_rect() and input_string() interfaces, handle the widget layer
419 * and the flame layer, etc. Also fix a minor bug where you would be prevented
420 * from settling if you pressed a key even if it didn't really move you. :-)
421 *
422 * Revision 1.10 2000/10/29 17:23:13 weimer
423 * incorporate "xflame" flaming background for added spiffiness ...
424 *
425 * Revision 1.9 2000/10/28 22:33:18 weimer
426 * add a blinking cursor to the input string widget
427 *
428 * Revision 1.8 2000/10/28 16:40:17 weimer
429 * Further changes: we can now build .tar.gz files and RPMs!
430 *
431 * Revision 1.7 2000/10/21 01:14:43 weimer
432 * massic autoconf/automake restructure ...
433 *
434 * Revision 1.6 2000/10/18 23:57:49 weimer
435 * general fixup, color changes, display changes.
436 * Notable: "Safe" Blits and Updates now perform "clipping". No more X errors,
437 * we hope!
438 *
439 * Revision 1.5 2000/10/18 02:04:02 weimer
440 * playability changes ...
441 *
442 * Revision 1.4 2000/10/14 01:56:35 weimer
443 * remove dates from identity files
444 *
445 * Revision 1.3 2000/10/13 15:41:53 weimer
446 * revamped AI support, now you can pick your AI and have AI duels (such fun!)
447 * the mighty Aliz AI still crashes a bit, though ... :-)
448 *
449 * Revision 1.2 2000/10/13 02:26:54 weimer
450 * rudimentary identity functions, including adding new players
451 *
452 * Revision 1.1 2000/10/12 01:38:07 weimer
453 * added initial support for persistent player identities
454 *
455 */
456