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