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