1 /*
2  * Tux Racer
3  * Copyright (C) 1999-2001 Jasmin F. Patry
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 #include "tuxracer.h"
21 #include "course_mgr.h"
22 #include "list.h"
23 #include "tcl_util.h"
24 #include "textures.h"
25 #include "save.h"
26 #include "multiplayer.h"
27 
28 static char err_buff[BUFF_LEN];
29 
30 struct event_data_ {
31     char *name;          		/* event name */
32     list_t cup_list;			/* list of cups */
33 };
34 
35 struct cup_data_ {
36     char *name;				/* name of cup */
37     list_t race_list;			/* list of races */
38 };
39 
40 static bool_t initialized = False;	/* has module been initialized? */
41 static list_t event_list = NULL;	/* list of events */
42 static list_t open_course_list = NULL;	/* list of open courses */
43 
44 static char *race_condition_names[RACE_CONDITIONS_NUM_CONDITIONS] =
45 {
46     "sunny",
47     "cloudy",
48     "night"
49 };
50 
51 
52 /*---------------------------------------------------------------------------*/
53 /*!
54   Initializes the course manager module
55   \return  None
56   \author  jfpatry
57   \date    Created:  2000-09-19
58   \date    Modified: 2000-09-19
59 */
init_course_manager()60 void init_course_manager()
61 {
62     check_assertion( initialized == False,
63 		     "Attempt to initialize course manager twice" );
64 
65     initialized = True;
66 
67     event_list = create_list();
68     open_course_list = create_list();
69 }
70 
71 
72 /*---------------------------------------------------------------------------*/
73 /*!
74   Returns the list of open courses
75   \return  List of open courses
76   \author  jfpatry
77   \date    Created:  2000-09-19
78   \date    Modified: 2000-09-19
79 */
get_open_courses_list()80 list_t get_open_courses_list( )
81 {
82     return open_course_list;
83 }
84 
85 
86 /*---------------------------------------------------------------------------*/
87 /*!
88   Returns the list of events
89   \return  List of events
90   \author  jfpatry
91   \date    Created:  2000-09-19
92   \date    Modified: 2000-09-19
93 */
get_events_list()94 list_t get_events_list(  )
95 {
96     return event_list;
97 }
98 
99 
100 /*---------------------------------------------------------------------------*/
101 /*!
102   Returns the event data for the event with specified name
103   \author  jfpatry
104   \date    Created:  2000-09-23
105   \date    Modified: 2000-09-23
106 */
get_event_by_name(char * event_name)107 list_elem_t get_event_by_name( char *event_name )
108 {
109     list_elem_t cur_event = NULL;
110     event_data_t *data;
111 
112     cur_event = get_list_head( event_list );
113 
114     while (1) {
115 	if ( cur_event == NULL ) {
116 	    return NULL;
117 	}
118 
119 	data = (event_data_t*) get_list_elem_data( cur_event );
120 
121 	if ( strcmp( data->name, event_name ) == 0 ) {
122 	    return cur_event;
123 	}
124 
125 	cur_event = get_next_list_elem( event_list, cur_event );
126     }
127 
128     return NULL;
129 }
130 
131 
132 /*---------------------------------------------------------------------------*/
133 /*!
134   Returns the name of an event
135   \return  Name of event
136   \author  jfpatry
137   \date    Created:  2000-09-19
138   \date    Modified: 2000-09-19
139 */
get_event_name(event_data_t * event)140 char* get_event_name( event_data_t *event )
141 {
142     return event->name;
143 }
144 
145 
146 /*---------------------------------------------------------------------------*/
147 /*!
148   Returns the texture binding name for the event icon
149   \pre
150   \arg \c  foo
151   \arg \c  foo
152   \arg \c  foo
153 
154   \return  texture binding name for event icon
155   \author  jfpatry
156   \date    Created:  2000-09-19
157   \date    Modified: 2000-09-19
158 */
get_event_icon_texture_binding(event_data_t * event)159 char*  get_event_icon_texture_binding( event_data_t *event )
160 {
161     /* Same as name, for now */
162     return event->name;
163 }
164 
165 
166 /*---------------------------------------------------------------------------*/
167 /*!
168   Returns the cup list for the event
169   \author  jfpatry
170   \date    Created:  2000-09-19
171   \date    Modified: 2000-09-19
172 */
get_event_cup_list(event_data_t * event)173 list_t get_event_cup_list( event_data_t *event )
174 {
175     return event->cup_list;
176 }
177 
178 
179 
180 /*---------------------------------------------------------------------------*/
181 /*!
182   Returns the cup data for the specified cup name
183   \author  jfpatry
184   \date    Created:  2000-09-23
185   \date    Modified: 2000-09-23
186 */
get_event_cup_by_name(event_data_t * event,char * name)187 list_elem_t get_event_cup_by_name( event_data_t *event, char *name )
188 {
189     list_elem_t cur_cup;
190     cup_data_t *data;
191 
192     cur_cup = get_list_head( event->cup_list );
193 
194     while (1) {
195 	if ( cur_cup == NULL ) {
196 	    return NULL;
197 	}
198 
199 	data = (cup_data_t*)get_list_elem_data( cur_cup );
200 
201 	if ( strcmp( data->name, name ) == 0 ) {
202 	    return cur_cup;
203 	}
204 
205 	cur_cup = get_next_list_elem( event->cup_list, cur_cup );
206     }
207 
208     return NULL;
209 }
210 
211 
212 /*---------------------------------------------------------------------------*/
213 /*!
214   Returns the name of the specified cup
215   \author  jfpatry
216   \date    Created:  2000-09-19
217   \date    Modified: 2000-09-19
218 */
get_cup_name(cup_data_t * cup)219 char* get_cup_name( cup_data_t *cup )
220 {
221     return cup->name;
222 }
223 
224 
225 /*---------------------------------------------------------------------------*/
226 /*!
227   Returns the texture binding name for the cup icon
228   \author  jfpatry
229   \date    Created:  2000-09-19
230   \date    Modified: 2000-09-19
231 */
get_cup_icon_texture_binding(cup_data_t * cup)232 char* get_cup_icon_texture_binding( cup_data_t *cup )
233 {
234     /* Same as cup name for now */
235     return cup->name;
236 
237 }
238 
239 
240 /*---------------------------------------------------------------------------*/
241 /*!
242   Returns the list of races for the specified cup
243   \author  jfpatry
244   \date    Created:  2000-09-19
245   \date    Modified: 2000-09-19
246 */
get_cup_race_list(cup_data_t * cup)247 list_t get_cup_race_list( cup_data_t *cup )
248 {
249     return cup->race_list;
250 }
251 
252 
253 
254 /*---------------------------------------------------------------------------*/
255 /*!
256   Returns the last complete cup for the specified event
257   \author  jfpatry
258   \date    Created:  2000-09-24
259   \date    Modified: 2000-09-24
260 */
get_last_complete_cup_for_event(event_data_t * event_data)261 list_elem_t get_last_complete_cup_for_event( event_data_t *event_data )
262 {
263     char *last_cup;
264     list_elem_t cup;
265 
266     if ( get_last_completed_cup( g_game.player[local_player()].name,
267 				 get_event_name( event_data ),
268 				 g_game.difficulty,
269 				 &last_cup ) )
270     {
271 	cup = get_event_cup_by_name( event_data, last_cup );
272 
273 	if ( cup == NULL ) {
274 	    print_warning( IMPORTANT_WARNING,
275 			   "Couldn't find saved cup `%s'", last_cup );
276 	}
277 	return cup;
278     } else {
279 	return NULL;
280     }
281 }
282 
283 
284 /*---------------------------------------------------------------------------*/
285 /*!
286   Returns true iff the cup is complete
287   \author  jfpatry
288   \date    Created:  2000-09-24
289   \date    Modified: 2000-09-24
290 */
is_cup_complete(event_data_t * event_data,list_elem_t cup)291 bool_t is_cup_complete( event_data_t *event_data, list_elem_t cup )
292 {
293     list_elem_t cur_elem;
294     list_elem_t last_complete_cup =
295 	get_last_complete_cup_for_event( event_data );
296 
297     if ( last_complete_cup == NULL ) {
298 	return False;
299     }
300 
301     if ( cup == last_complete_cup ) {
302 	return True;
303     }
304 
305     cur_elem = get_prev_list_elem( event_data->cup_list, cup );
306 
307     while (1) {
308 	if ( cur_elem == NULL ) {
309 	    return True;
310 	}
311 
312 	if ( cur_elem == last_complete_cup ) {
313 	    return False;
314 	}
315 
316 	cur_elem = get_prev_list_elem( event_data->cup_list, cur_elem );
317     }
318 }
319 
320 
321 
322 /*---------------------------------------------------------------------------*/
323 /*!
324   Returns true iff the specified cup is the first incomplete cup
325   \author  jfpatry
326   \date    Created:  2000-09-24
327   \date    Modified: 2000-09-24
328 */
is_cup_first_incomplete_cup(event_data_t * event_data,list_elem_t cup)329 bool_t is_cup_first_incomplete_cup( event_data_t *event_data,
330 				    list_elem_t cup )
331 {
332     list_elem_t elem = get_last_complete_cup_for_event( event_data );
333 
334     if ( elem == NULL ) {
335 	if ( cup == get_list_head( event_data->cup_list ) ) {
336 	    return True;
337 	} else {
338 	    return False;
339 	}
340     }
341 
342     elem = get_next_list_elem( event_data->cup_list, elem );
343 
344     if ( elem == NULL ) {
345 	/* All cups complete */
346 	return False;
347     }
348 
349     if ( cup == elem ) {
350 	return True;
351     } else {
352 	return False;
353     }
354 }
355 
356 
357 
358 /*---------------------------------------------------------------------------*/
359 /*!
360   Compares the positions of two races in a cup
361   \return  pos(race2)-pos(race1)
362   \author  jfpatry
363   \date    Created:  2000-09-24
364   \date    Modified: 2000-09-24
365 */
compare_race_positions(cup_data_t * cup_data,list_elem_t race1,list_elem_t race2)366 int compare_race_positions( cup_data_t *cup_data,
367 			    list_elem_t race1, list_elem_t race2 )
368 {
369     int incr = 1;
370     list_elem_t cur_elem = NULL;
371     bool_t found1 = False;
372     bool_t found2 = False;
373     int diff;
374 
375     check_assertion( race1 != NULL, "race is null" );
376     check_assertion( race2 != NULL, "race is null" );
377     check_assertion( cup_data != NULL, "null data" );
378     check_assertion( cup_data->race_list != NULL, "null list" );
379 
380     diff = 0;
381     cur_elem = get_list_head( cup_data->race_list );
382     while( 1 ) {
383 
384 	if ( cur_elem == NULL ) {
385 	    check_assertion( 0, "race1 or race2 aren't races in the cup" );
386 	}
387 
388 	if ( cur_elem == race1 ) {
389 	    if ( found2 ) {
390 		return diff;
391 	    }
392 
393 	    found1 = True;
394 
395 	    incr = 1;
396 	}
397 
398 	if ( cur_elem == race2 ) {
399 	    if ( found1 ) {
400 		return diff;
401 	    }
402 
403 	    found2 = True;
404 
405 	    incr = -1;
406 	}
407 
408 	cur_elem = get_next_list_elem( cup_data->race_list, cur_elem );
409 
410 	if ( found1 || found2 ) {
411 	    diff += incr;
412 	}
413     }
414 
415     code_not_reached();
416 }
417 
418 
419 
420 /*---------------------------------------------------------------------------*/
421 /*!
422   Creates an open_course_data_t object from a Tcl string.
423   \author  jfpatry
424   \date    Created:  2000-09-21
425   \date    Modified: 2000-09-21
426 */
create_open_course_data(Tcl_Interp * ip,char * string,char ** err_msg)427 open_course_data_t* create_open_course_data( Tcl_Interp *ip, char *string,
428 					     char **err_msg )
429 {
430     char **argv = NULL;
431     char **orig_argv = NULL;
432     int argc = 0;
433 
434     char *course = NULL;
435     char *name = NULL;
436     char *description = NULL;
437     scalar_t par_time = 120;
438 
439     open_course_data_t *open_course_data = NULL;
440 
441     if ( Tcl_SplitList( ip, string, &argc, &argv ) == TCL_ERROR ) {
442 	*err_msg = "open course data is not a list";
443 	goto bail_open_course_data;
444     }
445 
446     orig_argv = argv;
447 
448     while ( *argv != NULL ) {
449 	if ( strcmp( *argv, "-course" ) == 0 ) {
450 	    NEXT_ARG;
451 
452 	    if ( *argv == NULL ) {
453 		*err_msg = "No data supplied for -course in open course data";
454 		goto bail_open_course_data;
455 	    }
456 
457 	    course = string_copy( *argv );
458 	} else if ( strcmp( *argv, "-name" ) == 0 ) {
459 	    NEXT_ARG;
460 
461 	    if ( *argv == NULL ) {
462 		*err_msg = "No data supplied for -name in open course data";
463 		goto bail_open_course_data;
464 	    }
465 
466 	    name = string_copy( *argv );
467 	} else if ( strcmp( *argv, "-description" ) == 0 ) {
468 	    NEXT_ARG;
469 
470 	    if ( *argv == NULL ) {
471 		*err_msg = "No data supplied for -description in open course data";
472 		goto bail_open_course_data;
473 	    }
474 
475 	    description = string_copy( *argv );
476 	} else if ( strcmp( *argv, "-par_time" ) == 0 ) {
477 	    NEXT_ARG;
478 
479 	    if ( *argv == NULL ) {
480 		par_time = 120.0;
481 		print_warning( PEDANTIC_WARNING,
482 			       "No data supplied for -par_time in open course "
483 			       "data.  Using %g seconds.", par_time );
484 	    } else if ( Tcl_GetDouble( ip, *argv, &par_time ) != TCL_OK ) {
485 		*err_msg = "Invalid value for -par_time in open course data";
486 		goto bail_open_course_data;
487 	    }
488 	} else {
489 	    sprintf( err_buff, "unrecognized option `%s' in open course data",
490 		     *argv );
491 	    *err_msg = err_buff;
492 	    goto bail_open_course_data;
493 	}
494 
495 	NEXT_ARG;
496     }
497 
498     /* Check mandatory arguments */
499     if ( course == NULL ) {
500 	*err_msg = "No course specified in open course data";
501 	goto bail_open_course_data;
502     }
503 
504     if ( name == NULL ) {
505 	*err_msg = "No name specified in open course data";
506 	goto bail_open_course_data;
507     }
508 
509 
510     /* Create new open_course_data_t object */
511     open_course_data = (open_course_data_t*) malloc( sizeof(open_course_data_t) );
512     check_assertion( open_course_data != NULL, "out of memory" );
513 
514     open_course_data->course = course;
515     open_course_data->name = name;
516     open_course_data->description = description;
517     open_course_data->par_time = par_time;
518 
519     Tcl_Free( (char*) orig_argv );
520 
521     return open_course_data;
522 
523 bail_open_course_data:
524 
525     if ( orig_argv ) {
526 	Tcl_Free( (char*) orig_argv );
527     }
528 
529     if ( course ) {
530 	free( course );
531     }
532 
533     if ( name ) {
534 	free( name );
535     }
536 
537     if ( description ) {
538 	free( description );
539     }
540 
541     if ( open_course_data ) {
542 	free( open_course_data );
543     }
544 
545     return NULL;
546 }
547 
548 /*---------------------------------------------------------------------------*/
549 /*!
550   tux_open_courses Tcl callback
551   \author  jfpatry
552   \date    Created:  2000-09-19
553   \date    Modified: 2000-09-19
554 */
open_courses_cb(ClientData cd,Tcl_Interp * ip,int argc,char ** argv)555 static int open_courses_cb( ClientData cd, Tcl_Interp *ip,
556 			    int argc, char **argv )
557 {
558     char *err_msg;
559     char **list = NULL;
560     int num_courses;
561     list_elem_t last_elem = NULL;
562     int i;
563 
564     check_assertion( initialized,
565 		     "course_mgr module not initialized" );
566 
567     if ( argc != 2 ) {
568 	err_msg = "Wrong number of arguments";
569 	goto bail_open_courses;
570     }
571 
572     if ( Tcl_SplitList( ip, argv[1], &num_courses, &list ) == TCL_ERROR ) {
573 	err_msg = "Argument is not a list";
574 	goto bail_open_courses;
575     }
576 
577     /* Add items to end of list */
578     last_elem = get_list_tail( open_course_list );
579 
580     for ( i=0; i<num_courses; i++ ) {
581 	open_course_data_t *data;
582 	data = create_open_course_data( ip, list[i], &err_msg );
583 
584 	if ( data == NULL ) {
585 	    goto bail_open_courses;
586 	}
587 
588 	last_elem = insert_list_elem(
589 	    open_course_list,
590 	    last_elem,
591 	    (list_elem_data_t) data );
592     }
593 
594     Tcl_Free( (char*) list );
595     list = NULL;
596 
597     return TCL_OK;
598 
599 bail_open_courses:
600 
601     /* We'll leave the data that was successfully added in the list. */
602 
603     Tcl_AppendResult(
604 	ip,
605 	"Error in call to tux_open_courses: ",
606 	err_msg,
607 	"\n",
608 	"Usage: tux_open_courses { list of open courses }",
609 	(NULL) );
610     return TCL_ERROR;
611 }
612 
613 
614 /*---------------------------------------------------------------------------*/
615 /*!
616   Creates a race_data_t object from a Tcl string.
617   \return  New race_data_t object if successful, or NULL if error
618   \author  jfpatry
619   \date    Created:  2000-09-19
620   \date    Modified: 2000-09-19
621 */
622 static
create_race_data(Tcl_Interp * ip,char * string,char ** err_msg)623 race_data_t* create_race_data ( Tcl_Interp *ip, char *string, char **err_msg )
624 {
625     char **argv = NULL;
626     char **orig_argv = NULL;
627     int argc = 0;
628 
629     char *course = NULL;
630     char *name = NULL;
631     char *description = NULL;
632 
633     int      herring_req[DIFFICULTY_NUM_LEVELS];
634     bool_t   herring_req_init = False;
635     scalar_t time_req[DIFFICULTY_NUM_LEVELS];
636     bool_t   time_req_init = False;
637     scalar_t score_req[DIFFICULTY_NUM_LEVELS];
638     bool_t   score_req_init = False;
639 
640     bool_t   mirrored = False;
641     race_conditions_t conditions = RACE_CONDITIONS_SUNNY;
642     bool_t   windy = False;
643     bool_t   snowing = False;
644 
645     race_data_t *race_data = NULL;
646 
647     if ( Tcl_SplitList( ip, string, &argc, &argv ) == TCL_ERROR ) {
648 	*err_msg = "race data is not a list";
649 	goto bail_race_data;
650     }
651 
652     orig_argv = argv;
653 
654     while ( *argv != NULL ) {
655 	if ( strcmp( *argv, "-course" ) == 0 ) {
656 	    NEXT_ARG;
657 
658 	    if ( *argv == NULL ) {
659 		*err_msg = "No data supplied for -course in race data";
660 		goto bail_race_data;
661 	    }
662 
663 	    course = string_copy( *argv );
664 	} else if ( strcmp( *argv, "-name" ) == 0 ) {
665 	    NEXT_ARG;
666 
667 	    if ( *argv == NULL ) {
668 		*err_msg = "No data supplied for -name in race data";
669 		goto bail_race_data;
670 	    }
671 
672 	    name = string_copy( *argv );
673 	} else if ( strcmp( *argv, "-description" ) == 0 ) {
674 	    NEXT_ARG;
675 
676 	    if ( *argv == NULL ) {
677 		*err_msg = "No data supplied for -description in race data";
678 		goto bail_race_data;
679 	    }
680 
681 	    description = string_copy( *argv );
682 	} else if ( strcmp( *argv, "-herring" ) == 0 ) {
683 	    NEXT_ARG;
684 
685 	    if ( *argv == NULL ) {
686 		*err_msg = "No data supplied for -herring in race data";
687 		goto bail_race_data;
688 	    }
689 
690 	    if ( get_tcl_int_tuple(
691 		ip, *argv, herring_req,
692 		sizeof(herring_req)/sizeof(herring_req[0]) ) == TCL_ERROR )
693 	    {
694 		*err_msg = "Value for -herring is not a list or has "
695 		    "the wrong number of elements";
696 		goto bail_race_data;
697 	    }
698 
699 	    herring_req_init = True;
700 	} else if ( strcmp( *argv, "-time" ) == 0 ) {
701 	    NEXT_ARG;
702 
703 	    if ( *argv == NULL ) {
704 		*err_msg = "No data supplied for -time in race data" ;
705 		goto bail_race_data;
706 	    }
707 
708 	    if ( get_tcl_tuple( ip, *argv, time_req,
709 				sizeof(time_req)/sizeof(time_req[0]) )
710 		 == TCL_ERROR )
711 	    {
712 		*err_msg = "Value for -time is not a list or hsa the "
713 		    "wrong number of elements";
714 		goto bail_race_data;
715 	    }
716 
717 	    time_req_init = True;
718 	} else if ( strcmp( *argv, "-score" ) == 0 ) {
719 	    NEXT_ARG;
720 
721 	    if ( *argv == NULL ) {
722 		*err_msg = "No data supplied for -score in race data";
723 		goto bail_race_data;
724 	    }
725 
726 	    if ( get_tcl_tuple( ip, *argv, score_req,
727 				sizeof(score_req)/sizeof(score_req[0]) )
728 		 == TCL_ERROR )
729 	    {
730 		*err_msg = "Value for -score is not a list or has the "
731 		    "wrong number of elements";
732 		goto bail_race_data;
733 	    }
734 
735 	    score_req_init = True;
736 	} else if ( strcmp( *argv, "-mirrored" ) == 0 ) {
737 	    NEXT_ARG;
738 
739 	    if ( *argv == NULL ) {
740 		*err_msg = "No data supplied for -mirrored in race data";
741 		goto bail_race_data;
742 	    }
743 
744 	    if ( string_cmp_no_case( *argv, "yes" ) == 0 ) {
745 		mirrored = True;
746 	    } else {
747 		mirrored = False;
748 	    }
749 	} else if ( strcmp( *argv, "-conditions" ) == 0 ) {
750 	    int i;
751 	    NEXT_ARG;
752 
753 	    if ( *argv == NULL ) {
754 		*err_msg = "No data supplied for -conditions in race data";
755 		goto bail_race_data;
756 	    }
757 
758 	    for ( i=0; i<RACE_CONDITIONS_NUM_CONDITIONS; i++ ) {
759 		if ( string_cmp_no_case( race_condition_names[i],
760 					 *argv ) == 0 )
761 		{
762 		    break;
763 		}
764 	    }
765 
766 	    if ( i == RACE_CONDITIONS_NUM_CONDITIONS ) {
767 		*err_msg = "Invalid value for -conditions in race data";
768 		goto bail_race_data;
769 	    }
770 
771 	    conditions = (race_conditions_t)i;
772 	} else if ( strcmp( *argv, "-windy" ) == 0 ) {
773 	    NEXT_ARG;
774 
775 	    if ( *argv == NULL ) {
776 		*err_msg = "No data supplied for -windy in race data";
777 		goto bail_race_data;
778 	    }
779 
780 	    if ( string_cmp_no_case( *argv, "yes" ) == 0 ) {
781 		windy = True;
782 	    } else {
783 		windy = False;
784 	    }
785 	} else if ( strcmp( *argv, "-snowing" ) == 0 ) {
786 	    NEXT_ARG;
787 
788 	    if ( *argv == NULL ) {
789 		*err_msg = "No data supplied for -snowing in race data";
790 		goto bail_race_data;
791 	    }
792 
793 	    if ( string_cmp_no_case( *argv, "yes" ) == 0 ) {
794 		snowing = True;
795 	    } else {
796 		snowing = False;
797 	    }
798 	} else {
799 	    sprintf( err_buff, "unrecognized option `%s' in race data",
800 		     *argv );
801 	    *err_msg = err_buff;
802 	    goto bail_race_data;
803 	}
804 
805 	NEXT_ARG;
806     }
807 
808     /* Check mandatory arguments */
809     if ( course == NULL ) {
810 	*err_msg = "No course specified in race data";
811 	goto bail_race_data;
812     }
813 
814     if ( !herring_req_init ||
815 	 !time_req_init ||
816 	 !score_req_init )
817     {
818 	*err_msg = "Must specify requirement for herring, time, and score.";
819 	goto bail_race_data;
820     }
821 
822     /* Create new race_data_t object */
823 
824     race_data = (race_data_t*) malloc( sizeof(race_data_t) );
825     check_assertion( race_data != NULL, "out of memory" );
826 
827     race_data->course = course;
828     race_data->name = name;
829     race_data->description = description;
830 
831     memcpy( race_data->herring_req, herring_req, sizeof(herring_req) );
832     memcpy( race_data->time_req, time_req, sizeof(time_req) );
833     memcpy( race_data->score_req, score_req, sizeof(score_req) );
834 
835     race_data->mirrored = mirrored;
836     race_data->conditions = conditions;
837     race_data->windy = windy;
838     race_data->snowing = snowing;
839 
840     Tcl_Free( (char*) orig_argv );
841 
842     return race_data;
843 
844 bail_race_data:
845 
846     if ( orig_argv ) {
847 	Tcl_Free( (char*) orig_argv );
848     }
849 
850     if ( course ) {
851 	free( course );
852     }
853 
854     if ( name ) {
855 	free( name );
856     }
857 
858     if ( description ) {
859 	free( description );
860     }
861 
862     if ( race_data ) {
863 	free( race_data );
864     }
865 
866     return NULL;
867 }
868 
869 
870 /*---------------------------------------------------------------------------*/
871 /*!
872   Creates a cup_data_t object from a Tcl string.
873   \return  New cup_data_t object if successful, or NULL if error
874   \author  jfpatry
875   \date    Created:  2000-09-19
876   \date    Modified: 2000-09-19
877 */
878 static
create_cup_data(Tcl_Interp * ip,char * string,char ** err_msg)879 cup_data_t* create_cup_data( Tcl_Interp *ip, char *string, char **err_msg )
880 {
881     char **argv = NULL;
882     char **orig_argv = NULL;
883     int argc = 0;
884 
885     char *name = NULL;
886     char *icon = NULL;
887     list_t race_list = NULL;
888     list_elem_t last_race = NULL;
889     char **races = NULL;
890     int num_races = 0;
891     int i;
892 
893     cup_data_t *cup_data = NULL;
894 
895     if ( Tcl_SplitList( ip, string, &argc, &argv ) == TCL_ERROR ) {
896 	*err_msg = "cup data is not a list";
897 	goto bail_cup_data;
898     }
899 
900     orig_argv = argv;
901 
902     while ( *argv != NULL ) {
903 	if ( strcmp( *argv, "-name" ) == 0 ) {
904 	    NEXT_ARG;
905 
906 	    if ( *argv == NULL ) {
907 		*err_msg = "No data supplied for -name in cup data";
908 		goto bail_cup_data;
909 	    }
910 
911 	    name = string_copy( *argv );
912 	} else if ( strcmp( *argv, "-icon" ) == 0 ) {
913 	    NEXT_ARG;
914 
915 	    if ( *argv == NULL ) {
916 		*err_msg = "No data supplied for -icon in cup data";
917 		goto bail_cup_data;
918 	    }
919 
920 	    icon = string_copy( *argv );
921 	} else if ( strcmp( *argv, "-races" ) == 0 ) {
922 	    NEXT_ARG;
923 
924 	    if ( *argv == NULL ) {
925 		*err_msg= "No data supplied for -races in cup data";
926 		goto bail_cup_data;
927 	    }
928 
929 	    race_list = create_list();
930 	    last_race = NULL;
931 
932 	    if ( Tcl_SplitList( ip, *argv, &num_races, &races ) == TCL_ERROR ) {
933 		*err_msg = "Race data is not a list in event data";
934 		goto bail_cup_data;
935 	    }
936 
937 	    for ( i=0; i<num_races; i++) {
938 		race_data_t *race_data;
939 		race_data = create_race_data( ip, races[i], err_msg );
940 		if ( race_data == NULL ) {
941 		    goto bail_cup_data;
942 		}
943 
944 		last_race = insert_list_elem( race_list, last_race,
945 					      (list_elem_data_t) race_data );
946 	    }
947 
948 	    Tcl_Free( (char*) races );
949 	    races = NULL;
950 	} else {
951 	    sprintf( err_buff, "Unrecognized argument `%s'", *argv );
952 	    *err_msg = err_buff;
953 	    goto bail_cup_data;
954 	}
955 
956 	NEXT_ARG;
957     }
958 
959     /* Make sure mandatory fields have been specified */
960     if ( name == NULL ) {
961 	*err_msg = "Must specify a name in cup data";
962 	goto bail_cup_data;
963     }
964 
965     if ( icon == NULL ) {
966 	*err_msg = "Must specify an icon texture in cup data";
967 	goto bail_cup_data;
968     }
969 
970     if ( race_list == NULL ) {
971 	*err_msg = "Must specify a race list in cup data";
972 	goto bail_cup_data;
973     }
974 
975     /* Create a new cup data object */
976     cup_data = (cup_data_t*) malloc( sizeof( cup_data_t ) );
977     check_assertion( cup_data != NULL, "out of memory" );
978 
979     cup_data->name = name;
980     cup_data->race_list = race_list;
981 
982     bind_texture( name, icon );
983 
984     Tcl_Free( (char*) orig_argv );
985     argv = NULL;
986 
987     return cup_data;
988 
989 bail_cup_data:
990 
991     if ( orig_argv ) {
992 	Tcl_Free( (char*) orig_argv );
993     }
994 
995     if ( name ) {
996 	free( name );
997     }
998 
999     if ( icon ) {
1000 	free( icon );
1001     }
1002 
1003     if ( races ) {
1004 	Tcl_Free( (char*) races );
1005     }
1006 
1007     /* Clean out race list */
1008     if ( race_list ) {
1009 	last_race = get_list_tail( race_list );
1010 	while ( last_race != NULL ) {
1011 	    race_data_t *data;
1012 	    data = (race_data_t*) delete_list_elem( race_list, last_race );
1013 	    free( data );
1014 	    last_race = get_list_tail( race_list );
1015 	}
1016 
1017 	del_list( race_list );
1018     }
1019 
1020     if ( cup_data ) {
1021 	free( cup_data );
1022     }
1023 
1024     return NULL;
1025 }
1026 
1027 /*---------------------------------------------------------------------------*/
1028 /*!
1029   Creates an event_data_t object from a Tcl string.
1030   \return  New event_data_t object if successful, or NULL on error
1031   \author  jfpatry
1032   \date    Created:  2000-09-19
1033   \date    Modified: 2000-09-19
1034 */
1035 static
create_event_data(Tcl_Interp * ip,char * string,char ** err_msg)1036 event_data_t* create_event_data( Tcl_Interp *ip, char *string, char **err_msg )
1037 {
1038     char **orig_argv = NULL;
1039     char **argv = NULL;
1040     int argc = 0;
1041 
1042     char *name = NULL;
1043     char *icon = NULL;
1044     list_t cup_list = NULL;
1045     list_elem_t last_cup = NULL;
1046     char **cups = NULL;
1047     int num_cups = 0;
1048     int i;
1049 
1050     event_data_t *event_data = NULL;
1051 
1052     if ( Tcl_SplitList( ip, string, &argc, &argv ) == TCL_ERROR ) {
1053 	*err_msg = "event data is not a list";
1054 	goto bail_event_data;
1055     }
1056 
1057     orig_argv = argv;
1058 
1059     while ( *argv != NULL ) {
1060 	if ( strcmp( *argv, "-name" ) == 0 ) {
1061 	    NEXT_ARG;
1062 
1063 	    if ( *argv == NULL ) {
1064 		*err_msg = "No data supplied for -name in event data";
1065 		goto bail_event_data;
1066 	    }
1067 
1068 	    name = string_copy( *argv );
1069 	} else if ( strcmp( *argv, "-icon" ) == 0 ) {
1070 	    NEXT_ARG;
1071 
1072 	    if ( *argv == NULL ) {
1073 		*err_msg = "No data supplied for -icon in event data";
1074 		goto bail_event_data;
1075 	    }
1076 
1077 	    icon = string_copy( *argv );
1078 	} else if ( strcmp( *argv, "-cups" ) == 0 ) {
1079 	    NEXT_ARG;
1080 
1081 	    if ( *argv == NULL ) {
1082 		*err_msg = "No data supplied for -cups in event data";
1083 		goto bail_event_data;
1084 	    }
1085 
1086 	    cup_list = create_list();
1087 	    last_cup = NULL;
1088 
1089 	    if ( Tcl_SplitList( ip, *argv, &num_cups, &cups ) == TCL_ERROR ) {
1090 		*err_msg = "Cup data is not a list in event data";
1091 		goto bail_event_data;
1092 	    }
1093 
1094 	    for ( i=0; i<num_cups; i++ ) {
1095 		cup_data_t *cup_data;
1096 		cup_data = create_cup_data( ip, cups[i], err_msg );
1097 		if ( cup_data == NULL ) {
1098 		    goto bail_event_data;
1099 		}
1100 
1101 		last_cup = insert_list_elem( cup_list, last_cup,
1102 					     (list_elem_data_t) cup_data );
1103 	    }
1104 
1105 	    Tcl_Free( (char*) cups );
1106 	    cups = NULL;
1107 	} else {
1108 	    sprintf( err_buff, "Unrecognized argument `%s'", *argv );
1109 	    *err_msg = err_buff;
1110 	    goto bail_event_data;
1111 	}
1112 
1113 	NEXT_ARG;
1114     }
1115 
1116     /* Make sure mandatory fields have been specified */
1117     if ( name == NULL ) {
1118 	*err_msg = "Must specify a name in event data";
1119 	goto bail_event_data;
1120     }
1121 
1122     if ( icon == NULL ) {
1123 	*err_msg = "Must specify an icon texture in event data";
1124 	goto bail_event_data;
1125     }
1126 
1127     if ( cup_list == NULL ) {
1128 	*err_msg = "Must specify a cup list in event data";
1129 	goto bail_event_data;
1130     }
1131 
1132     /* Create new event data object */
1133     event_data = (event_data_t*) malloc( sizeof( event_data_t ) );
1134     check_assertion( event_data != NULL, "out of memory" );
1135 
1136     event_data->name = name;
1137     event_data->cup_list = cup_list;
1138 
1139     bind_texture( name, icon );
1140 
1141     Tcl_Free( (char*) orig_argv );
1142     argv = NULL;
1143 
1144     return event_data;
1145 
1146 bail_event_data:
1147 
1148     if ( orig_argv ) {
1149 	Tcl_Free( (char*) orig_argv );
1150     }
1151 
1152     if ( name ) {
1153 	free( name );
1154     }
1155 
1156     if ( icon ) {
1157 	free( name );
1158     }
1159 
1160     if ( cups ) {
1161 	Tcl_Free( (char*) cups );
1162     }
1163 
1164     /* Clean out cup list */
1165     if ( cup_list ) {
1166 	last_cup = get_list_tail( cup_list );
1167 	while ( last_cup != NULL ) {
1168 	    cup_data_t *data;
1169 	    data = (cup_data_t*) delete_list_elem( cup_list,
1170 						   last_cup );
1171 	    free( data );
1172 	    last_cup = get_list_tail( cup_list );
1173 	}
1174 
1175 	del_list( cup_list );
1176     }
1177 
1178     if ( event_data ) {
1179 	free( event_data );
1180     }
1181 
1182     return NULL;
1183 }
1184 
1185 
1186 /*---------------------------------------------------------------------------*/
1187 /*!
1188   tux_events Tcl callback
1189   Here's a sample call to tux_events:
1190 
1191 tux_events {
1192     {
1193 	-name "Herring Run" -icon noicon -cups {
1194 	    {
1195 		-name "Cup 1" -icon noicon -races {
1196 		    {
1197 			-course path_of_daggers \
1198 				-description "nice long description" \
1199 				-herring { 15 20 25 30 } \
1200 				-time { 40.0 35.0 30.0 25.0 } \
1201 				-score { 0 0 0 0 } \
1202 				-mirrored yes -conditions cloudy \
1203 				-windy no -snowing no
1204 		    }
1205 		    {
1206 			-course ingos_speedway \
1207 				-description "nice long description" \
1208 				-herring { 15 20 25 30 } \
1209 				-time { 40.0 35.0 30.0 25.0 } \
1210 				-score { 0 0 0 0 } \
1211 				-mirrored yes -conditions cloudy \
1212 				-windy no -snowing no
1213 		    }
1214 		}
1215 		-name "Cup 2" -icon noicon -races {
1216 		    {
1217 			-course penguins_cant_fly \
1218 				-description "nice long description" \
1219 				-herring { 15 20 25 30 } \
1220 				-time { 40.0 35.0 30.0 25.0 } \
1221 				-score { 0 0 0 0 } \
1222 				-mirrored yes -conditions cloudy \
1223 				-windy no -snowing no
1224 		    }
1225 		    {
1226 			-course ingos_speedway \
1227 				-description "nice long description" \
1228 				-herring { 15 20 25 30 } \
1229 				-time { 40.0 35.0 30.0 25.0 } \
1230 				-score { 0 0 0 0 } \
1231 				-mirrored yes -conditions cloudy \
1232 				-windy no -snowing no
1233 		    }
1234 		}
1235 	    }
1236 	}
1237     }
1238 }
1239 
1240   \return  Tcl error code
1241   \author  jfpatry
1242   \date    Created:  2000-09-19
1243   \date    Modified: 2000-09-19
1244 */
events_cb(ClientData cd,Tcl_Interp * ip,int argc,char ** argv)1245 static int events_cb( ClientData cd, Tcl_Interp *ip,
1246 		      int argc, char **argv )
1247 {
1248     char *err_msg;
1249     char **list = NULL;
1250     int num_events;
1251     list_elem_t last_event = NULL;
1252     int i;
1253 
1254     /* Make sure module has been initialized */
1255     check_assertion( initialized,
1256 		     "course_mgr module not initialized" );
1257 
1258     if ( argc != 2 ) {
1259 	err_msg = "Incorrect number of arguments";
1260 	goto bail_events;
1261     }
1262 
1263     if ( Tcl_SplitList( ip, argv[1], &num_events, &list ) == TCL_ERROR ) {
1264 	err_msg = "Argument is not a list";
1265 	goto bail_events;
1266     }
1267 
1268     /* We currently only allow tux_events to be called once */
1269     last_event = get_list_tail( event_list );
1270 
1271     if ( last_event != NULL ) {
1272 	err_msg = "tux_events has already been called; it can only be called "
1273 	    "once.";
1274 	goto bail_events;
1275     }
1276 
1277     for (i=0; i<num_events; i++) {
1278 	event_data_t *data = create_event_data( ip, list[i], &err_msg );
1279 
1280 	if ( data == NULL ) {
1281 	    goto bail_events;
1282 	}
1283 
1284 	last_event = insert_list_elem( event_list, last_event,
1285 				       (list_elem_data_t) data );
1286     }
1287 
1288     Tcl_Free( (char*) list );
1289     list = NULL;
1290 
1291     return TCL_OK;
1292 
1293 bail_events:
1294     if ( list != NULL ) {
1295 	Tcl_Free( (char*) list );
1296     }
1297 
1298     /* Clean out event list */
1299     if ( event_list != NULL ) {
1300 	last_event = get_list_tail( event_list );
1301 	while ( last_event != NULL ) {
1302 	    event_data_t *data;
1303 	    data = (event_data_t*) delete_list_elem( event_list,
1304 						     last_event );
1305 	    free( data );
1306 	    last_event = get_list_tail( event_list );
1307 	}
1308     }
1309 
1310     Tcl_AppendResult(
1311 	ip,
1312 	"Error in call to tux_events: ",
1313 	err_msg,
1314 	"\n",
1315 	"Usage: tux_events { list of event data }",
1316 	(NULL) );
1317     return TCL_ERROR;
1318 }
1319 
1320 
1321 /*---------------------------------------------------------------------------*/
1322 /*!
1323   Returns the current race conditions (sunny, cloudy, etc.)
1324   \author  jfpatry
1325   \date    Created:  2000-09-25
1326   \date    Modified: 2000-09-25
1327 */
get_race_conditions_cb(ClientData cd,Tcl_Interp * ip,int argc,char ** argv)1328 static int get_race_conditions_cb( ClientData cd, Tcl_Interp *ip,
1329 				   int argc, char **argv )
1330 {
1331     char *err_msg;
1332     Tcl_Obj *result;
1333 
1334     if ( argc != 1 ) {
1335 	err_msg = "Incorrect number of arguments";
1336 	goto bail_race_conditions;
1337     }
1338 
1339     result = Tcl_NewStringObj(
1340 	race_condition_names[ g_game.race.conditions ],
1341 	strlen( race_condition_names[ g_game.race.conditions ] ) );;
1342 
1343     Tcl_SetObjResult( ip, result );
1344 
1345     return TCL_OK;
1346 
1347 bail_race_conditions:
1348 
1349     Tcl_AppendResult(
1350 	ip,
1351 	"Error in call to tux_get_race_conditions: ",
1352 	err_msg,
1353 	"\n",
1354 	"Usage: tux_get_race_conditions",
1355 	(NULL) );
1356     return TCL_ERROR;
1357 }
1358 
register_course_manager_callbacks(Tcl_Interp * ip)1359 void register_course_manager_callbacks( Tcl_Interp *ip )
1360 {
1361     Tcl_CreateCommand (ip, "tux_open_courses", open_courses_cb, 0,0);
1362     Tcl_CreateCommand (ip, "tux_events", events_cb, 0,0);
1363     Tcl_CreateCommand (ip, "tux_get_race_conditions",
1364 		       get_race_conditions_cb, 0,0);
1365 }
1366 
1367 /* EOF */
1368