Tennix 1.1 "Classic Championship Tour 2011" released
[tennix.git] / tennix.cc
blobe0715f2e7d602a8ee3cadccb1af5f4f997529b11
2 /**
4 * Tennix! SDL Port
5 * Copyright (C) 2003, 2007, 2008, 2009 Thomas Perl <thp@thpinfo.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
22 **/
24 #include <stdio.h>
25 #include <time.h>
26 #include <libgen.h>
27 #include <string.h>
28 #include <stdlib.h>
30 #ifdef WIN32
31 #include <windows.h>
32 #endif
34 #include "tennix.h"
35 #include "archive.hh"
36 #include "game.h"
37 #include "graphics.h"
38 #include "sound.h"
39 #include "input.h"
40 #include "network.h"
41 #include "util.h"
42 #include "animation.h"
44 #include "locations.h"
46 SDL_Surface *screen;
48 #ifdef WIN32
50 /* IDs from the resource file */
51 #define START_BUTTON 1
52 #define CHECKBOX_FULLSCREEN 2
53 #define QUIT_BUTTON 3
55 BOOL CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
57 static int checkbox_is_checked;
59 switch (uMsg) {
60 case WM_CLOSE:
61 EndDialog(hwndDlg, IDCANCEL);
62 break;
63 case WM_COMMAND:
64 switch (wParam) {
65 case START_BUTTON:
66 EndDialog(hwndDlg, (checkbox_is_checked)?(IDYES):(IDNO));
67 break;
68 case QUIT_BUTTON:
69 EndDialog(hwndDlg, IDCANCEL);
70 break;
71 case CHECKBOX_FULLSCREEN:
72 checkbox_is_checked ^= 1;
73 break;
75 break;
76 default:
77 return FALSE;
79 return TRUE;
81 #endif
83 #ifdef WIN32
84 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
85 LPSTR lpCmdLine, int nCmdShow) {
86 #else
87 int main( int argc, char** argv) {
88 #endif
89 int i, slide, slide_direction;
90 unsigned int x;
91 Uint32 ticks;
92 int mx, my;
93 unsigned int worldmap_xpos, worldmap_ypos;
94 Uint8 *keys;
95 Uint8 mb;
96 SDL_Event e;
97 int sdl_flags = SDL_SWSURFACE;
98 int btn_hovering = 0, btn_hovering_old = 0;
99 int slide_start;
100 bool mouse_pressed = false;
101 bool quit = false;
102 bool benchmark = false;
103 unsigned int night_start, night_end;
104 GameState *current_game = NULL, *prepared_game = NULL;
105 InputDevice* input_devices;
106 unsigned int input_device_count, input_device_id;
107 Animation *intro;
108 AnimationState *intro_playback;
109 float wiggle;
110 TennixNet* connection = NULL;
111 char* net_host = NULL;
112 bool net_master = false;
113 TennixArchive* tnxar = NULL;
115 MenuButton btn_back = {
116 NULL, /* not needed for image-based button */
117 MENU_OPTIONS_BORDER,
118 HEIGHT-MENU_OPTIONS_BORDER,
119 0, -1, /* width and height will be set by menu_button_init */
120 255, 0, 0,
121 GR_BACK
123 MenuButton btn_start = {
124 NULL, /* not needed for image-based button */
125 WIDTH-MENU_OPTIONS_BORDER,
126 HEIGHT-MENU_OPTIONS_BORDER,
127 -1, -1, /* width and height will be set by menu_button_init */
128 0, 255, 0,
129 GR_FORWARD
131 MenuButton btn_player1 = {
132 NULL,
133 CONTROLLER_SETUP_BORDER,
134 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
135 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
136 50, 50, 255,
137 GR_COUNT
139 MenuButton btn_player2 = {
140 NULL,
141 WIDTH-CONTROLLER_SETUP_SIZE-CONTROLLER_SETUP_BORDER,
142 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
143 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
144 255, 50, 50,
145 GR_COUNT
148 int highlight_location = -1;
149 float highlight_location_distance = 0.0;
150 float new_location_distance = 0.0;
151 bool location_info_visible = false;
152 unsigned int location_info_xpos = 0, location_info_ypos = 0;
154 const SDL_VideoInfo* vi = NULL;
156 bool do_help = false;
158 int state = MENU_STATE_STARTED;
160 #ifdef ENABLE_FPS_LIMIT
161 Uint32 ft, frames; /* frame timer and frames */
162 #endif
164 #if defined(MAEMO) || defined(MACOSX)
165 sdl_flags |= SDL_FULLSCREEN;
166 #endif
168 #ifdef WIN32
169 int mb_result;
170 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
172 switch (mb_result) {
173 case IDYES:
174 sdl_flags |= SDL_FULLSCREEN;
175 break;
176 case IDCANCEL:
177 return 0;
178 break;
179 default:
180 break;
182 #else
183 fprintf(stderr, "Tennix Classic Championship Tour 2011 (v" VERSION ")\n" COPYRIGHT "\n" URL "\n\n");
185 i = 1;
186 while (i < argc) {
187 /* A poor/lazy man's getopt */
188 #define OPTION_SET(longopt,shortopt) \
189 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
190 #define OPTION_VALUE \
191 ((i+1 < argc)?(argv[i+1]):(NULL))
192 #define OPTION_VALUE_PROCESSED \
193 (i++)
194 if (OPTION_SET("--fullscreen", "-f")) {
195 sdl_flags |= SDL_FULLSCREEN;
197 else if (OPTION_SET("--help", "-h")) {
198 do_help = true;
200 else if (OPTION_SET("--benchmark", "-b")) {
201 benchmark = true;
203 else if (OPTION_SET("--slave", "-s")) {
204 net_host = OPTION_VALUE;
205 if (OPTION_VALUE != NULL) {
206 OPTION_VALUE_PROCESSED;
207 net_master = false;
208 } else {
209 fprintf(stderr, "Missing option parameter.\n");
210 do_help = true;
211 break;
214 else if (OPTION_SET("--master", "-m")) {
215 net_host = OPTION_VALUE;
216 if (OPTION_VALUE != NULL) {
217 OPTION_VALUE_PROCESSED;
218 net_master = true;
219 } else {
220 fprintf(stderr, "Missing option parameter.\n");
221 do_help = true;
222 break;
225 else {
226 fprintf(stderr, "Unknown option: %s\n", argv[i]);
227 do_help = true;
229 i++;
232 if (do_help == true) {
233 fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
234 " Where [OPTIONS] are zero or more of the following:\n\n"
235 " [--fullscreen|-f] Fullscreen mode\n"
236 " [--benchmark|-b] Run in benchmark/attract mode\n"
237 " [--master|-m <IP-of-slave>] Network play as master\n"
238 " [--slave|-s <IP-of-master>] Network play as slave\n"
239 " [--help|-h] Show help information\n\n"
240 " See tennix(6) for details.\n", argv[0]);
241 return 0;
243 #endif
245 if (benchmark) {
246 srand(100);
247 } else {
248 srand((unsigned)time(NULL));
251 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
252 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
253 exit( 1);
256 vi = SDL_GetVideoInfo();
257 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
258 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
259 exit( 1);
262 SDL_WM_SetCaption( "Tennix Classic Championship Tour 2011", "Tennix 2011");
263 SDL_ShowCursor( SDL_DISABLE);
264 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
266 tnxar = new TennixArchive(ARCHIVE_FILE, ARCHIVE_FILE_INSTALLED);
267 init_graphics(*tnxar);
268 init_sound(*tnxar);
269 init_input(*tnxar);
270 init_network();
271 delete tnxar;
273 if (net_host != NULL) {
274 connection = network_connect(net_host, net_master);
277 menu_button_init(&btn_back);
278 menu_button_init(&btn_start);
279 menu_button_init(&btn_player1);
280 menu_button_init(&btn_player2);
282 input_devices = find_input_devices(&input_device_count);
284 current_game = gamestate_load(GAMESTATE_FILE);
285 if (current_game != NULL) {
286 /* restore pointer to location */
287 current_game->location = &(locations[current_game->current_location]);
288 /* */
289 for (i=1; i<=MAXPLAYERS; i++) {
290 if (PLAYER(current_game, i).type == PLAYER_TYPE_HUMAN) {
291 input_device_id = PLAYER(current_game, i).input_device_index;
292 if (input_device_id < input_device_count) {
293 /* ok, we still have that device around */
294 PLAYER(current_game, i).input = &(input_devices[input_device_id]);
295 } else {
296 /* the device vanished - set to AI (FIXME: select new device) */
297 PLAYER(current_game, i).type = PLAYER_TYPE_AI;
298 PLAYER(current_game, i).input = NULL;
304 #ifdef ENABLE_FPS_LIMIT
305 frames = 0;
306 ft = SDL_GetTicks();
307 #endif
309 if (benchmark) {
310 GameState* g = gamestate_new();
311 PLAYER(g, 1).type = PLAYER_TYPE_AI;
312 PLAYER(g, 2).type = PLAYER_TYPE_AI;
313 g->location = &(locations[0]);
314 gameloop(g, connection);
315 free(g);
316 exit(0);
319 /*intro = create_intro();
320 intro_playback = animation_state_new(intro);
321 animation_state_run(intro_playback, 1);
322 animation_state_free(intro_playback);
323 animation_free(intro);
324 start_fade();*/
326 worldmap_xpos = (WIDTH-get_image_width(GR_WORLDMAP))/2;
327 worldmap_ypos = (HEIGHT-get_image_height(GR_WORLDMAP))/2;
329 i = 0;
330 /* Sliding initialization */
331 ticks = SDL_GetTicks();
332 slide = slide_start = get_image_width(GR_SIDEBAR);
333 slide_direction = 0;
334 while(!quit) {
335 /* State transitions */
336 switch (state) {
337 case MENU_STATE_STARTED:
338 state = MENU_STATE_SLIDE_TO_MAINMENU;
339 break;
340 case MENU_STATE_SLIDE_TO_MAINMENU:
341 clear_screen();
342 //rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
343 store_screen();
344 slide = slide_start;
345 slide_direction = -1;
346 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
347 break;
348 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
349 if (slide == 0) {
350 slide_direction = 0;
351 state = MENU_STATE_MAINMENU;
353 break;
354 case MENU_STATE_MAINMENU:
355 free(prepared_game);
356 prepared_game = NULL;
357 break;
358 case MENU_STATE_SLIDE_TO_LOCATION:
359 slide = 1;
360 slide_direction = 3;
361 state = MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS;
362 break;
363 case MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS:
364 if (slide == slide_start) {
365 state = MENU_STATE_FADE_TO_LOCATION;
367 break;
368 case MENU_STATE_FADE_TO_LOCATION:
369 start_fade();
370 state = MENU_STATE_LOCATION;
371 clear_screen();
373 //rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
374 /* Draw and store the worldmap with day/night times */
375 show_image(GR_WORLDMAP, WIDTH/2-get_image_width(GR_WORLDMAP)/2, HEIGHT/2-get_image_height(GR_WORLDMAP)/2, 255);
376 day_night(get_image_width(GR_WORLDMAP), &night_start, &night_end);
377 if (night_start > night_end) {
378 rectangle_alpha(worldmap_xpos, worldmap_ypos, night_end, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
379 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, get_image_width(GR_WORLDMAP)-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
380 } else {
381 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, night_end-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
384 /* add misc items to screen */
385 font_draw_string(FONT_XLARGE, "Pick a location", (WIDTH-font_get_string_width(FONT_XLARGE, "Pick a location"))/2, 20);
387 store_screen();
388 break;
389 case MENU_STATE_FADE_TO_OPTIONS:
390 start_fade();
391 clear_screen();
392 //rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
393 rectangle(CONTROLLER_SETUP_BORDER, HEIGHT/2-CONTROLLER_SETUP_SIZE/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
394 rectangle(WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE, (HEIGHT-CONTROLLER_SETUP_SIZE)/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
395 font_draw_string(FONT_XLARGE, "Controller setup", (WIDTH-font_get_string_width(FONT_XLARGE, "Controller setup"))/2, 20);
396 font_draw_string(FONT_MEDIUM, "Player 1", 130 - font_get_string_width(FONT_MEDIUM, "Player 1")/2, (HEIGHT-CONTROLLER_SETUP_SIZE)/2 - 10 - font_get_height(FONT_MEDIUM));
397 font_draw_string(FONT_MEDIUM, "Player 1", 130 - font_get_string_width(FONT_MEDIUM, "Player 1")/2, (HEIGHT-CONTROLLER_SETUP_SIZE)/2 - 10 - font_get_height(FONT_MEDIUM));
398 font_draw_string(FONT_MEDIUM, "Player 2", WIDTH - CONTROLLER_SETUP_SIZE/2 - CONTROLLER_SETUP_BORDER - font_get_string_width(FONT_MEDIUM, "Player 2")/2, (HEIGHT-CONTROLLER_SETUP_SIZE)/2 - 10 - font_get_height(FONT_MEDIUM));
399 store_screen();
400 start_fade();
401 state = MENU_STATE_OPTIONS;
402 break;
403 case MENU_STATE_LOCATION:
404 case MENU_STATE_OPTIONS:
405 /* Prepare a new game */
406 if (prepared_game == NULL) {
407 prepared_game = gamestate_new();
408 prepared_game->location = NULL;
409 /* FIXME - this should not be written here, but taken from the input devices */
410 btn_player1.text = "Keyboard (arrows)";
411 btn_player2.text = "Carl Van Court";
412 #ifdef MAEMO
413 PLAYER(prepared_game, 1).input_device_index = 0;
414 #else
415 PLAYER(prepared_game, 1).input_device_index = 2;
416 #endif
417 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
418 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
419 location_info_visible = false;
420 prepared_game->current_location = -1;
421 highlight_location = -1;
423 break;
424 case MENU_STATE_SLIDE_TO_GAME:
425 /*slide = 1;
426 slide_direction = 2;
427 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
428 break;
429 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
430 if (slide == slide_start) {
431 state = MENU_STATE_GAME;
433 state = MENU_STATE_GAME;
434 break;
435 case MENU_STATE_SLIDE_TO_RESUME:
436 slide = 1;
437 slide_direction = 2;
438 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
439 break;
440 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
441 if (slide == slide_start) {
442 state = MENU_STATE_RESUME;
444 break;
445 case MENU_STATE_GAME:
446 if (prepared_game == NULL) {
447 fprintf(stderr, "Game not yet prepared!\n");
448 exit(EXIT_FAILURE);
451 /* Cancel a possibly started game */
452 free(current_game);
453 current_game = prepared_game;
454 prepared_game = NULL;
455 /* no break - we are continuing with "resume" */
456 case MENU_STATE_RESUME:
457 if (current_game == NULL) {
458 fprintf(stderr, "Cannot resume game!\n");
459 exit(EXIT_FAILURE);
461 start_fade();
462 gameloop(current_game, connection);
463 SDL_Delay(150);
464 while(SDL_PollEvent(&e));
465 #ifdef ENABLE_FPS_LIMIT
466 frames = 0;
467 ft = SDL_GetTicks();
468 #endif
469 start_fade();
470 state = MENU_STATE_SLIDE_TO_MAINMENU;
471 break;
472 case MENU_STATE_SLIDE_TO_QUIT:
473 slide = 1;
474 slide_direction = 3;
475 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
476 break;
477 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
478 if (slide == slide_start) {
479 state = MENU_STATE_QUIT;
481 break;
482 case MENU_STATE_QUIT:
483 quit = true;
484 break;
485 default:
486 fprintf(stderr, "State error: %d\n", state);
487 exit(EXIT_FAILURE);
490 /* Sliding */
491 if (SDL_GetTicks() > ticks + 20) {
492 if (slide >= 1 && slide <= slide_start) {
493 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
494 slide = MAX(0, MIN(slide_start, slide));
495 } else if (slide_direction != 0) {
496 slide_direction = 0;
498 ticks = SDL_GetTicks();
501 /* Graphics */
502 #ifdef DEBUG
503 if (state != MENU_STATE_OPTIONS) {
504 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
506 #endif
507 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
508 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
509 if (state != MENU_STATE_OPTIONS && state != MENU_STATE_LOCATION) {
510 /* Main Menu */
511 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
512 if (current_game != NULL) {
513 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
514 font_draw_string(FONT_SMALL, "match paused", 10, 10);
515 } else {
516 font_draw_string(FONT_MEDIUM, "Tennix Classic Championship Tour 2011", 10, 10);
518 //font_draw_string_color(FONT_MEDIUM, URL, 10-1, HEIGHT-10-1-font_get_height(FONT_MEDIUM), 130, 130, 130);
519 font_draw_string_color(FONT_MEDIUM, URL, 10, HEIGHT-10-font_get_height(FONT_MEDIUM), 100, 100, 100);
520 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
521 } else if (state == MENU_STATE_OPTIONS) {
522 /* Options screen */
523 draw_button_object(&btn_back, mx, my);
524 draw_button_object(&btn_start, mx, my);
525 draw_button_object(&btn_player1, mx, my);
526 draw_button_object(&btn_player2, mx, my);
527 wiggle = 15*sinf((float)ticks/300.);
528 if (PLAYER(prepared_game, 1).input_device_index > -1) {
529 show_image_rotozoom(input_devices[PLAYER(prepared_game, 1).input_device_index].icon, CONTROLLER_SETUP_BORDER + CONTROLLER_SETUP_SIZE/2, HEIGHT/2, wiggle, 1.0);
530 } else {
531 show_image_rotozoom(GR_INPUT_AI, CONTROLLER_SETUP_BORDER + CONTROLLER_SETUP_SIZE/2, HEIGHT/2, wiggle, 1.0);
533 if (PLAYER(prepared_game, 2).input_device_index > -1) {
534 show_image_rotozoom(input_devices[PLAYER(prepared_game, 2).input_device_index].icon, WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE/2, HEIGHT/2, -wiggle, 1.0);
535 } else {
536 show_image_rotozoom(GR_INPUT_AI, WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE/2, HEIGHT/2, -wiggle, 1.0);
538 } else if (state == MENU_STATE_LOCATION) {
539 /* Location selection screen */
540 for (x=0; x<location_count(); x++) {
541 new_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[x].worldmap_x,
542 my-worldmap_ypos-locations[x].worldmap_y);
543 if (highlight_location == -1) {
544 if (new_location_distance < 20*20) {
545 highlight_location = x;
547 } else {
548 highlight_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[highlight_location].worldmap_x,
549 my-worldmap_ypos-locations[highlight_location].worldmap_y);
550 if (highlight_location_distance > 20*20) {
551 highlight_location = -1;
553 if (highlight_location_distance > new_location_distance && new_location_distance < 20*20) {
554 highlight_location = x;
558 if (prepared_game != NULL) {
559 if (!location_info_visible) {
560 for (x=0; x<location_count(); x++) {
561 /* draw rectangle for location at "x"*/
562 if (highlight_location != -1 && (unsigned int)highlight_location == x) {
563 rectangle(worldmap_xpos + locations[x].worldmap_x-3, worldmap_ypos + locations[x].worldmap_y-3, 7, 7, 255*((i/10)%2), 0, 255*((i/10)%2));
566 rectangle(worldmap_xpos + locations[x].worldmap_x-2, worldmap_ypos + locations[x].worldmap_y-2, 5, 5, 255, 255*(prepared_game->current_location != -1 && x==(unsigned int)(prepared_game->current_location)), 0);
568 } else {
569 rectangle_alpha(location_info_xpos-5, location_info_ypos-5, 10+get_image_width(prepared_game->location->photo), get_image_height(prepared_game->location->photo)+100, 30, 30, 30, 200);
570 show_sprite(prepared_game->location->photo, (i/1000)%(prepared_game->location->photo_frames), prepared_game->location->photo_frames, location_info_xpos, location_info_ypos, 255);
571 font_draw_string(FONT_SMALL, prepared_game->location->name, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200);
572 font_draw_string(FONT_SMALL, prepared_game->location->area, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200+font_get_height(FONT_SMALL));
573 font_draw_string(FONT_SMALL, prepared_game->location->city, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200+2*font_get_height(FONT_SMALL));
574 font_draw_string(FONT_SMALL, prepared_game->location->court_type_name, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200+3*font_get_height(FONT_SMALL));
576 if (prepared_game->location != NULL) {
577 draw_button_object(&btn_start, mx, my);
580 draw_button_object(&btn_back, mx, my);
583 SDL_PollEvent( &e);
584 if( e.type == SDL_QUIT) {
585 state = MENU_STATE_SLIDE_TO_QUIT;
586 /*break;*/
589 keys = SDL_GetKeyState( NULL);
590 mb = SDL_GetMouseState( &mx, &my);
592 btn_hovering_old = btn_hovering;
593 if (state == MENU_STATE_MAINMENU) {
594 btn_hovering = M_POS_DECODE(mx, my);
595 if (current_game == NULL) {
596 btn_hovering &= ~MENU_RESUME;
598 } else if (state == MENU_STATE_LOCATION) {
599 if (M_POS_BUTTON(btn_back, mx, my)) {
600 btn_hovering = MENU_QUIT;
601 } else if (M_POS_BUTTON(btn_start, mx, my)) {
602 btn_hovering = MENU_START;
603 } else {
604 btn_hovering = 0;
606 } else if (state == MENU_STATE_OPTIONS) {
607 if (M_POS_BUTTON(btn_back, mx, my)) {
608 btn_hovering = MENU_QUIT;
609 } else if (M_POS_BUTTON(btn_start, mx, my)) {
610 btn_hovering = MENU_START;
611 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
612 btn_hovering = MENU_PLAYER1;
613 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
614 btn_hovering = MENU_PLAYER2;
615 } else {
616 btn_hovering = 0;
618 } else {
619 /* No menu screen - no hovering. */
620 btn_hovering = 0;
622 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
623 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
624 #ifdef HAVE_VOICE_FILES
625 if (btn_hovering == MENU_QUIT) {
626 play_sample(VOICE_QUIT_IT);
627 } else if (btn_hovering == MENU_START) {
628 play_sample(VOICE_NEW_GAME);
629 } else {
630 play_sample(SOUND_MOUSEOVER);
632 #else
633 /*play_sample(SOUND_MOUSEOVER);*/
634 #endif
636 #endif
638 if( keys[SDLK_ESCAPE] || keys['q']) {
639 /* FIXME: do the state thingie! */
640 break;
643 if( keys['f']) {
644 SDL_WM_ToggleFullScreen( screen);
647 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
648 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
649 show_image(GR_CURSOR, mx-2, my-1, 255);
651 #endif
653 /* Draw the "real" mouse coordinates */
654 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
656 /* Store the screen, because we are fading after this screen update */
657 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
659 updatescr();
661 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
662 if (!mouse_pressed) {
663 play_sample(SOUND_MOUSEOVER);
665 mouse_pressed = true;
666 } else if (mouse_pressed == true) {
667 /* Mouse button released */
668 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
669 #ifdef HAVE_VOICE_FILES
670 if (btn_hovering == MENU_START) {
671 play_sample(VOICE_LETS_GO);
672 } else {
673 play_sample(SOUND_MOUSECLICK);
675 #else
676 /*play_sample(SOUND_MOUSEOVER);*/
677 #endif
679 if (state == MENU_STATE_MAINMENU) {
680 switch (btn_hovering) {
681 case MENU_START:
682 state = MENU_STATE_SLIDE_TO_LOCATION;
683 break;
684 case MENU_RESUME:
685 state = MENU_STATE_SLIDE_TO_RESUME;
686 break;
687 case MENU_QUIT:
688 state = MENU_STATE_SLIDE_TO_QUIT;
689 break;
691 } else if (state == MENU_STATE_LOCATION) {
692 switch (btn_hovering) {
693 case MENU_START:
694 if (prepared_game->location != NULL) {
695 state = MENU_STATE_FADE_TO_OPTIONS;
697 break;
698 case MENU_QUIT:
699 state = MENU_STATE_SLIDE_TO_MAINMENU;
700 break;
701 default:
702 if (!location_info_visible && highlight_location != -1) {
703 prepared_game->current_location = highlight_location;
704 /* Set the day/night status */
705 if (night_start < night_end) {
706 locations[prepared_game->current_location].night = (locations[prepared_game->current_location].worldmap_x > night_start && locations[prepared_game->current_location].worldmap_x < night_end);
707 } else {
708 locations[prepared_game->current_location].night = (locations[prepared_game->current_location].worldmap_x < night_end || locations[prepared_game->current_location].worldmap_x > night_start);
710 prepared_game->location = &(locations[prepared_game->current_location]);
711 location_info_xpos = MAX(0, MIN(WIDTH-320-50, mx-320/2));
712 location_info_ypos = MAX(0, MIN(HEIGHT-200-160, my-200/2));
713 location_info_visible = true;
714 } else {
715 location_info_visible = false;
716 highlight_location = -1;
717 prepared_game->current_location = -1;
718 prepared_game->location = NULL;
720 break;
722 } else if (state == MENU_STATE_OPTIONS) {
723 switch (btn_hovering) {
724 case MENU_START:
725 state = MENU_STATE_SLIDE_TO_GAME;
726 break;
727 case MENU_QUIT:
728 state = MENU_STATE_FADE_TO_LOCATION;
729 break;
730 case MENU_PLAYER1:
731 /* advance the input device index */
732 PLAYER(prepared_game, 1).input_device_index++;
733 if (PLAYER(prepared_game, 1).input_device_index == (signed int)input_device_count) {
734 PLAYER(prepared_game, 1).input_device_index = -1;
737 if (input_devices[PLAYER(prepared_game, 1).input_device_index].exclusive_to_player == 2) {
738 PLAYER(prepared_game, 1).input_device_index++;
741 /* determine the selected input device */
742 if (PLAYER(prepared_game, 1).input_device_index == -1) {
743 PLAYER(prepared_game, 1).type = PLAYER_TYPE_AI;
744 PLAYER(prepared_game, 1).input = NULL;
745 btn_player1.text = "Carl Van Court";
746 } else {
747 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
748 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
749 btn_player1.text = input_device_get_name(PLAYER(prepared_game, 1).input);
751 break;
752 case MENU_PLAYER2:
753 /* advance the input device index */
754 PLAYER(prepared_game, 2).input_device_index++;
756 if (input_devices[PLAYER(prepared_game, 2).input_device_index].exclusive_to_player == 1) {
757 PLAYER(prepared_game, 2).input_device_index++;
760 if (PLAYER(prepared_game, 2).input_device_index == (signed int)input_device_count) {
761 PLAYER(prepared_game, 2).input_device_index = -1;
764 /* determine the selected input device */
765 if (PLAYER(prepared_game, 2).input_device_index == -1) {
766 PLAYER(prepared_game, 2).type = PLAYER_TYPE_AI;
767 PLAYER(prepared_game, 2).input = NULL;
768 btn_player2.text = "Carl Van Court";
769 } else {
770 PLAYER(prepared_game, 2).type = PLAYER_TYPE_HUMAN;
771 PLAYER(prepared_game, 2).input = &(input_devices[PLAYER(prepared_game, 2).input_device_index]);
772 btn_player2.text = input_device_get_name(PLAYER(prepared_game, 2).input);
774 break;
777 mouse_pressed = false;
779 i++;
780 #ifdef ENABLE_FPS_LIMIT
781 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
782 SDL_Delay(10);
784 frames++;
785 #endif
788 if (current_game != NULL) {
789 if (gamestate_save(current_game, GAMESTATE_FILE) != 0) {
790 fprintf(stderr, "Warning: cannot save gamestate to %s\n", GAMESTATE_FILE);
794 /* Play the credits */
795 intro = create_credits();
796 intro_playback = animation_state_new(intro);
797 animation_state_run(intro_playback, 1);
798 animation_state_free(intro_playback);
799 animation_free(intro);
801 uninit_graphics();
802 uninit_input();
803 uninit_network();
805 SDL_Quit();
806 return 0;
810 void menu_button_init(MenuButton* b)
812 int w, h;
814 if (b->image_id != GR_COUNT) {
816 * If the button is an image, the "w" and "h" attributes of the
817 * MenuButton struct are simply factors with which the real width
818 * and height of the image (=button) should be multiplied and added
819 * to the "x" and "y" position and the "w" and "h" are replaced with
820 * the real size of the image for the collision detection
822 w = b->w;
823 h = b->h;
825 b->w = get_image_width(b->image_id);
826 b->h = get_image_height(b->image_id);
827 b->x += w*b->w;
828 b->y += h*b->h;