Don't send input events when nothing has changed
[tennix.git] / tennix.c
blob9145b4e2a0bf1de938236e605784594dbf1e7ac2
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 "game.h"
36 #include "graphics.h"
37 #include "sound.h"
38 #include "input.h"
39 #include "network.h"
40 #include "util.h"
41 #include "animation.h"
43 #include "locations.h"
45 SDL_Surface *screen;
47 #ifdef WIN32
49 /* IDs from the resource file */
50 #define START_BUTTON 1
51 #define CHECKBOX_FULLSCREEN 2
52 #define QUIT_BUTTON 3
54 BOOL CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
56 static int checkbox_is_checked;
58 switch (uMsg) {
59 case WM_CLOSE:
60 EndDialog(hwndDlg, IDCANCEL);
61 break;
62 case WM_COMMAND:
63 switch (wParam) {
64 case START_BUTTON:
65 EndDialog(hwndDlg, (checkbox_is_checked)?(IDYES):(IDNO));
66 break;
67 case QUIT_BUTTON:
68 EndDialog(hwndDlg, IDCANCEL);
69 break;
70 case CHECKBOX_FULLSCREEN:
71 checkbox_is_checked ^= 1;
72 break;
74 break;
75 default:
76 return FALSE;
78 return TRUE;
80 #endif
82 #ifdef WIN32
83 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
84 LPSTR lpCmdLine, int nCmdShow) {
85 #else
86 int main( int argc, char** argv) {
87 #endif
88 int i, slide, slide_direction;
89 unsigned int x;
90 Uint32 ticks;
91 int mx, my;
92 unsigned int worldmap_xpos, worldmap_ypos;
93 Uint8 *keys;
94 Uint8 mb;
95 SDL_Event e;
96 int sdl_flags = SDL_SWSURFACE;
97 int btn_hovering = 0, btn_hovering_old = 0;
98 int slide_start;
99 bool mouse_pressed = false;
100 bool quit = false;
101 bool benchmark = false;
102 unsigned int night_start, night_end;
103 GameState *current_game = NULL, *prepared_game = NULL;
104 InputDevice* input_devices;
105 unsigned int input_device_count, input_device_id;
106 Animation *intro;
107 AnimationState *intro_playback;
108 float wiggle;
109 TennixNet* connection = NULL;
110 char* net_host = NULL;
111 bool net_master = false;
113 MenuButton btn_back = {
114 NULL, /* not needed for image-based button */
115 MENU_OPTIONS_BORDER,
116 HEIGHT-MENU_OPTIONS_BORDER,
117 0, -1, /* width and height will be set by menu_button_init */
118 255, 0, 0,
119 GR_BACK
121 MenuButton btn_start = {
122 NULL, /* not needed for image-based button */
123 WIDTH-MENU_OPTIONS_BORDER,
124 HEIGHT-MENU_OPTIONS_BORDER,
125 -1, -1, /* width and height will be set by menu_button_init */
126 0, 255, 0,
127 GR_FORWARD
129 MenuButton btn_player1 = {
130 NULL,
131 CONTROLLER_SETUP_BORDER,
132 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
133 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
134 50, 50, 255,
135 GR_COUNT
137 MenuButton btn_player2 = {
138 NULL,
139 WIDTH-CONTROLLER_SETUP_SIZE-CONTROLLER_SETUP_BORDER,
140 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
141 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
142 255, 50, 50,
143 GR_COUNT
146 int highlight_location = -1;
147 float highlight_location_distance = 0.0;
148 float new_location_distance = 0.0;
149 bool location_info_visible = false;
150 unsigned int location_info_xpos = 0, location_info_ypos = 0;
152 const SDL_VideoInfo* vi = NULL;
154 bool do_help = false;
156 int state = MENU_STATE_STARTED;
158 #ifdef ENABLE_FPS_LIMIT
159 Uint32 ft, frames; /* frame timer and frames */
160 #endif
162 #if defined(MAEMO) || defined(MACOSX)
163 sdl_flags |= SDL_FULLSCREEN;
164 #endif
166 #ifdef WIN32
167 int mb_result;
168 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
170 switch (mb_result) {
171 case IDYES:
172 sdl_flags |= SDL_FULLSCREEN;
173 break;
174 case IDCANCEL:
175 return 0;
176 break;
177 default:
178 break;
180 #else
181 fprintf(stderr, "Tennix 2009 World Tennis Championship Tour (v" VERSION ")\n" COPYRIGHT "\n" URL "\n\n");
183 i = 1;
184 while (i < argc) {
185 /* A poor/lazy man's getopt */
186 #define OPTION_SET(longopt,shortopt) \
187 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
188 #define OPTION_VALUE \
189 ((i+1 < argc)?(argv[i+1]):(NULL))
190 #define OPTION_VALUE_PROCESSED \
191 (i++)
192 if (OPTION_SET("--fullscreen", "-f")) {
193 sdl_flags |= SDL_FULLSCREEN;
195 else if (OPTION_SET("--help", "-h")) {
196 do_help = true;
198 else if (OPTION_SET("--benchmark", "-b")) {
199 benchmark = true;
201 else if (OPTION_SET("--slave", "-s")) {
202 net_host = OPTION_VALUE;
203 assert(OPTION_VALUE != NULL);
204 OPTION_VALUE_PROCESSED;
205 net_master = false;
207 else if (OPTION_SET("--master", "-m")) {
208 net_host = OPTION_VALUE;
209 assert(OPTION_VALUE != NULL);
210 OPTION_VALUE_PROCESSED;
211 net_master = true;
213 else {
214 fprintf(stderr, "Ignoring unknown option: %s\n", argv[i]);
216 i++;
219 if (do_help == true) {
220 fprintf(stderr, "Usage: %s [--fullscreen|-f] [--help|-h]\n", argv[0]);
221 return 0;
223 #endif
225 if (benchmark) {
226 srand(100);
227 } else {
228 srand((unsigned)time(NULL));
231 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
232 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
233 exit( 1);
236 vi = SDL_GetVideoInfo();
237 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
238 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
239 exit( 1);
242 SDL_WM_SetCaption( "Tennix 2009 World Tennis Championship Tour", "Tennix 2009");
243 SDL_ShowCursor( SDL_DISABLE);
244 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
246 init_graphics();
247 init_sound();
248 init_input();
249 init_network();
251 if (net_host != NULL) {
252 connection = network_connect(net_host, net_master);
255 menu_button_init(&btn_back);
256 menu_button_init(&btn_start);
257 menu_button_init(&btn_player1);
258 menu_button_init(&btn_player2);
260 input_devices = find_input_devices(&input_device_count);
262 current_game = gamestate_load(GAMESTATE_FILE);
263 if (current_game != NULL) {
264 /* restore pointer to location */
265 current_game->location = &(locations[current_game->current_location]);
266 /* */
267 for (i=1; i<=MAXPLAYERS; i++) {
268 if (PLAYER(current_game, i).type == PLAYER_TYPE_HUMAN) {
269 input_device_id = PLAYER(current_game, i).input_device_index;
270 if (input_device_id < input_device_count) {
271 /* ok, we still have that device around */
272 PLAYER(current_game, i).input = &(input_devices[input_device_id]);
273 } else {
274 /* the device vanished - set to AI (FIXME: select new device) */
275 PLAYER(current_game, i).type = PLAYER_TYPE_AI;
276 PLAYER(current_game, i).input = NULL;
282 #ifdef ENABLE_FPS_LIMIT
283 frames = 0;
284 ft = SDL_GetTicks();
285 #endif
287 if (benchmark) {
288 GameState* g = gamestate_new();
289 PLAYER(g, 1).type = PLAYER_TYPE_AI;
290 PLAYER(g, 2).type = PLAYER_TYPE_AI;
291 g->location = &(locations[0]);
292 gameloop(g, connection);
293 free(g);
294 exit(0);
297 /*intro = create_intro();
298 intro_playback = animation_state_new(intro);
299 animation_state_run(intro_playback, 1);
300 animation_state_free(intro_playback);
301 animation_free(intro);
302 start_fade();*/
304 worldmap_xpos = (WIDTH-get_image_width(GR_WORLDMAP))/2;
305 worldmap_ypos = (HEIGHT-get_image_height(GR_WORLDMAP))/2;
307 i = 0;
308 /* Sliding initialization */
309 ticks = SDL_GetTicks();
310 slide = slide_start = get_image_width(GR_SIDEBAR);
311 slide_direction = 0;
312 while(!quit) {
313 /* State transitions */
314 switch (state) {
315 case MENU_STATE_STARTED:
316 state = MENU_STATE_SLIDE_TO_MAINMENU;
317 break;
318 case MENU_STATE_SLIDE_TO_MAINMENU:
319 clear_screen();
320 rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
321 store_screen();
322 slide = slide_start;
323 slide_direction = -1;
324 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
325 break;
326 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
327 if (slide == 0) {
328 slide_direction = 0;
329 state = MENU_STATE_MAINMENU;
331 break;
332 case MENU_STATE_MAINMENU:
333 free(prepared_game);
334 prepared_game = NULL;
335 break;
336 case MENU_STATE_SLIDE_TO_LOCATION:
337 slide = 1;
338 slide_direction = 3;
339 state = MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS;
340 break;
341 case MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS:
342 if (slide == slide_start) {
343 state = MENU_STATE_FADE_TO_LOCATION;
345 break;
346 case MENU_STATE_FADE_TO_LOCATION:
347 start_fade();
348 state = MENU_STATE_LOCATION;
349 clear_screen();
351 rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
352 /* Draw and store the worldmap with day/night times */
353 show_image(GR_WORLDMAP, WIDTH/2-get_image_width(GR_WORLDMAP)/2, HEIGHT/2-get_image_height(GR_WORLDMAP)/2, 255);
354 day_night(get_image_width(GR_WORLDMAP), &night_start, &night_end);
355 if (night_start > night_end) {
356 rectangle_alpha(worldmap_xpos, worldmap_ypos, night_end, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
357 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, get_image_width(GR_WORLDMAP)-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
358 } else {
359 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, night_end-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
362 /* add misc items to screen */
363 font_draw_string(FONT_XLARGE, "Pick a location", (WIDTH-font_get_string_width(FONT_XLARGE, "Pick a location"))/2, 20);
365 store_screen();
366 break;
367 case MENU_STATE_FADE_TO_OPTIONS:
368 start_fade();
369 clear_screen();
370 rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
371 rectangle(CONTROLLER_SETUP_BORDER, HEIGHT/2-CONTROLLER_SETUP_SIZE/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
372 rectangle(WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE, (HEIGHT-CONTROLLER_SETUP_SIZE)/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
373 font_draw_string(FONT_XLARGE, "Controller setup", (WIDTH-font_get_string_width(FONT_XLARGE, "Controller setup"))/2, 20);
374 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));
375 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));
376 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));
377 store_screen();
378 start_fade();
379 state = MENU_STATE_OPTIONS;
380 break;
381 case MENU_STATE_LOCATION:
382 case MENU_STATE_OPTIONS:
383 /* Prepare a new game */
384 if (prepared_game == NULL) {
385 prepared_game = gamestate_new();
386 prepared_game->location = NULL;
387 /* FIXME - this should not be written here, but taken from the input devices */
388 btn_player1.text = "Keyboard (arrows)";
389 btn_player2.text = "Carl Van Court";
390 #ifdef MAEMO
391 PLAYER(prepared_game, 1).input_device_index = 0;
392 #else
393 PLAYER(prepared_game, 1).input_device_index = 2;
394 #endif
395 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
396 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
397 location_info_visible = false;
398 prepared_game->current_location = -1;
399 highlight_location = -1;
401 break;
402 case MENU_STATE_SLIDE_TO_GAME:
403 /*slide = 1;
404 slide_direction = 2;
405 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
406 break;
407 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
408 if (slide == slide_start) {
409 state = MENU_STATE_GAME;
411 state = MENU_STATE_GAME;
412 break;
413 case MENU_STATE_SLIDE_TO_RESUME:
414 slide = 1;
415 slide_direction = 2;
416 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
417 break;
418 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
419 if (slide == slide_start) {
420 state = MENU_STATE_RESUME;
422 break;
423 case MENU_STATE_GAME:
424 if (prepared_game == NULL) {
425 fprintf(stderr, "Game not yet prepared!\n");
426 exit(EXIT_FAILURE);
429 /* Cancel a possibly started game */
430 free(current_game);
431 current_game = prepared_game;
432 prepared_game = NULL;
433 /* no break - we are continuing with "resume" */
434 case MENU_STATE_RESUME:
435 if (current_game == NULL) {
436 fprintf(stderr, "Cannot resume game!\n");
437 exit(EXIT_FAILURE);
439 start_fade();
440 gameloop(current_game, connection);
441 SDL_Delay(150);
442 while(SDL_PollEvent(&e));
443 #ifdef ENABLE_FPS_LIMIT
444 frames = 0;
445 ft = SDL_GetTicks();
446 #endif
447 start_fade();
448 state = MENU_STATE_SLIDE_TO_MAINMENU;
449 break;
450 case MENU_STATE_SLIDE_TO_QUIT:
451 slide = 1;
452 slide_direction = 3;
453 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
454 break;
455 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
456 if (slide == slide_start) {
457 state = MENU_STATE_QUIT;
459 break;
460 case MENU_STATE_QUIT:
461 quit = true;
462 break;
463 default:
464 fprintf(stderr, "State error: %d\n", state);
465 exit(EXIT_FAILURE);
468 /* Sliding */
469 if (SDL_GetTicks() > ticks + 20) {
470 if (slide >= 1 && slide <= slide_start) {
471 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
472 slide = MAX(0, MIN(slide_start, slide));
473 } else if (slide_direction != 0) {
474 slide_direction = 0;
476 ticks = SDL_GetTicks();
479 /* Graphics */
480 #ifdef DEBUG
481 if (state != MENU_STATE_OPTIONS) {
482 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
484 #endif
485 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
486 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
487 if (state != MENU_STATE_OPTIONS && state != MENU_STATE_LOCATION) {
488 /* Main Menu */
489 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
490 if (current_game != NULL) {
491 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
492 font_draw_string(FONT_SMALL, "match paused", 10, 10);
493 } else {
494 font_draw_string(FONT_MEDIUM, "Tennix 2009 World Tennis Championship Tour", 10, 10);
496 font_draw_string_color(FONT_MEDIUM, URL, 10-1, HEIGHT-10-1-font_get_height(FONT_MEDIUM), 130, 130, 130);
497 font_draw_string_color(FONT_MEDIUM, URL, 10, HEIGHT-10-font_get_height(FONT_MEDIUM), 30, 30, 30);
498 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
499 } else if (state == MENU_STATE_OPTIONS) {
500 /* Options screen */
501 draw_button_object(&btn_back, mx, my);
502 draw_button_object(&btn_start, mx, my);
503 draw_button_object(&btn_player1, mx, my);
504 draw_button_object(&btn_player2, mx, my);
505 wiggle = 15*sinf((float)ticks/300.);
506 if (PLAYER(prepared_game, 1).input_device_index > -1) {
507 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);
508 } else {
509 show_image_rotozoom(GR_INPUT_AI, CONTROLLER_SETUP_BORDER + CONTROLLER_SETUP_SIZE/2, HEIGHT/2, wiggle, 1.0);
511 if (PLAYER(prepared_game, 2).input_device_index > -1) {
512 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);
513 } else {
514 show_image_rotozoom(GR_INPUT_AI, WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE/2, HEIGHT/2, -wiggle, 1.0);
516 } else if (state == MENU_STATE_LOCATION) {
517 /* Location selection screen */
518 for (x=0; x<location_count(); x++) {
519 new_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[x].worldmap_x,
520 my-worldmap_ypos-locations[x].worldmap_y);
521 if (highlight_location == -1) {
522 if (new_location_distance < 20*20) {
523 highlight_location = x;
525 } else {
526 highlight_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[highlight_location].worldmap_x,
527 my-worldmap_ypos-locations[highlight_location].worldmap_y);
528 if (highlight_location_distance > 20*20) {
529 highlight_location = -1;
531 if (highlight_location_distance > new_location_distance && new_location_distance < 20*20) {
532 highlight_location = x;
536 if (prepared_game != NULL) {
537 if (!location_info_visible) {
538 for (x=0; x<location_count(); x++) {
539 /* draw rectangle for location at "x"*/
540 if (highlight_location != -1 && (unsigned int)highlight_location == x) {
541 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));
544 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);
546 } else {
547 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);
548 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);
549 font_draw_string(FONT_SMALL, prepared_game->location->name, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200);
550 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));
551 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));
552 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));
554 if (prepared_game->location != NULL) {
555 draw_button_object(&btn_start, mx, my);
558 draw_button_object(&btn_back, mx, my);
561 SDL_PollEvent( &e);
562 if( e.type == SDL_QUIT) {
563 state = MENU_STATE_SLIDE_TO_QUIT;
564 /*break;*/
567 keys = SDL_GetKeyState( NULL);
568 mb = SDL_GetMouseState( &mx, &my);
570 btn_hovering_old = btn_hovering;
571 if (state == MENU_STATE_MAINMENU) {
572 btn_hovering = M_POS_DECODE(mx, my);
573 if (current_game == NULL) {
574 btn_hovering &= ~MENU_RESUME;
576 } else if (state == MENU_STATE_LOCATION) {
577 if (M_POS_BUTTON(btn_back, mx, my)) {
578 btn_hovering = MENU_QUIT;
579 } else if (M_POS_BUTTON(btn_start, mx, my)) {
580 btn_hovering = MENU_START;
581 } else {
582 btn_hovering = 0;
584 } else if (state == MENU_STATE_OPTIONS) {
585 if (M_POS_BUTTON(btn_back, mx, my)) {
586 btn_hovering = MENU_QUIT;
587 } else if (M_POS_BUTTON(btn_start, mx, my)) {
588 btn_hovering = MENU_START;
589 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
590 btn_hovering = MENU_PLAYER1;
591 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
592 btn_hovering = MENU_PLAYER2;
593 } else {
594 btn_hovering = 0;
596 } else {
597 /* No menu screen - no hovering. */
598 btn_hovering = 0;
600 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
601 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
602 #ifdef HAVE_VOICE_FILES
603 if (btn_hovering == MENU_QUIT) {
604 play_sample(VOICE_QUIT_IT);
605 } else if (btn_hovering == MENU_START) {
606 play_sample(VOICE_NEW_GAME);
607 } else {
608 play_sample(SOUND_MOUSEOVER);
610 #else
611 /*play_sample(SOUND_MOUSEOVER);*/
612 #endif
614 #endif
616 if( keys[SDLK_ESCAPE] || keys['q']) {
617 /* FIXME: do the state thingie! */
618 break;
621 if( keys['f']) {
622 SDL_WM_ToggleFullScreen( screen);
625 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
626 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
627 show_image(GR_CURSOR, mx-2, my-1, 255);
629 #endif
631 /* Draw the "real" mouse coordinates */
632 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
634 /* Store the screen, because we are fading after this screen update */
635 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
637 updatescr();
639 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
640 if (!mouse_pressed) {
641 play_sample(SOUND_MOUSEOVER);
643 mouse_pressed = true;
644 } else if (mouse_pressed == true) {
645 /* Mouse button released */
646 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
647 #ifdef HAVE_VOICE_FILES
648 if (btn_hovering == MENU_START) {
649 play_sample(VOICE_LETS_GO);
650 } else {
651 play_sample(SOUND_MOUSECLICK);
653 #else
654 /*play_sample(SOUND_MOUSEOVER);*/
655 #endif
657 if (state == MENU_STATE_MAINMENU) {
658 switch (btn_hovering) {
659 case MENU_START:
660 state = MENU_STATE_SLIDE_TO_LOCATION;
661 break;
662 case MENU_RESUME:
663 state = MENU_STATE_SLIDE_TO_RESUME;
664 break;
665 case MENU_QUIT:
666 state = MENU_STATE_SLIDE_TO_QUIT;
667 break;
669 } else if (state == MENU_STATE_LOCATION) {
670 switch (btn_hovering) {
671 case MENU_START:
672 if (prepared_game->location != NULL) {
673 state = MENU_STATE_FADE_TO_OPTIONS;
675 break;
676 case MENU_QUIT:
677 state = MENU_STATE_SLIDE_TO_MAINMENU;
678 break;
679 default:
680 if (!location_info_visible && highlight_location != -1) {
681 prepared_game->current_location = highlight_location;
682 /* Set the day/night status */
683 if (night_start < night_end) {
684 locations[prepared_game->current_location].night = (locations[prepared_game->current_location].worldmap_x > night_start && locations[prepared_game->current_location].worldmap_x < night_end);
685 } else {
686 locations[prepared_game->current_location].night = (locations[prepared_game->current_location].worldmap_x < night_end || locations[prepared_game->current_location].worldmap_x > night_start);
688 prepared_game->location = &(locations[prepared_game->current_location]);
689 location_info_xpos = MAX(0, MIN(WIDTH-320-50, mx-320/2));
690 location_info_ypos = MAX(0, MIN(HEIGHT-200-160, my-200/2));
691 location_info_visible = true;
692 } else {
693 location_info_visible = false;
694 highlight_location = -1;
695 prepared_game->current_location = -1;
696 prepared_game->location = NULL;
698 break;
700 } else if (state == MENU_STATE_OPTIONS) {
701 switch (btn_hovering) {
702 case MENU_START:
703 state = MENU_STATE_SLIDE_TO_GAME;
704 break;
705 case MENU_QUIT:
706 state = MENU_STATE_FADE_TO_LOCATION;
707 break;
708 case MENU_PLAYER1:
709 /* advance the input device index */
710 PLAYER(prepared_game, 1).input_device_index++;
711 if (PLAYER(prepared_game, 1).input_device_index == (signed int)input_device_count) {
712 PLAYER(prepared_game, 1).input_device_index = -1;
715 if (input_devices[PLAYER(prepared_game, 1).input_device_index].exclusive_to_player == 2) {
716 PLAYER(prepared_game, 1).input_device_index++;
719 /* determine the selected input device */
720 if (PLAYER(prepared_game, 1).input_device_index == -1) {
721 PLAYER(prepared_game, 1).type = PLAYER_TYPE_AI;
722 PLAYER(prepared_game, 1).input = NULL;
723 btn_player1.text = "Carl Van Court";
724 } else {
725 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
726 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
727 btn_player1.text = input_device_get_name(PLAYER(prepared_game, 1).input);
729 break;
730 case MENU_PLAYER2:
731 /* advance the input device index */
732 PLAYER(prepared_game, 2).input_device_index++;
734 if (input_devices[PLAYER(prepared_game, 2).input_device_index].exclusive_to_player == 1) {
735 PLAYER(prepared_game, 2).input_device_index++;
738 if (PLAYER(prepared_game, 2).input_device_index == (signed int)input_device_count) {
739 PLAYER(prepared_game, 2).input_device_index = -1;
742 /* determine the selected input device */
743 if (PLAYER(prepared_game, 2).input_device_index == -1) {
744 PLAYER(prepared_game, 2).type = PLAYER_TYPE_AI;
745 PLAYER(prepared_game, 2).input = NULL;
746 btn_player2.text = "Carl Van Court";
747 } else {
748 PLAYER(prepared_game, 2).type = PLAYER_TYPE_HUMAN;
749 PLAYER(prepared_game, 2).input = &(input_devices[PLAYER(prepared_game, 2).input_device_index]);
750 btn_player2.text = input_device_get_name(PLAYER(prepared_game, 2).input);
752 break;
755 mouse_pressed = false;
757 i++;
758 #ifdef ENABLE_FPS_LIMIT
759 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
760 SDL_Delay(10);
762 frames++;
763 #endif
766 if (current_game != NULL) {
767 if (gamestate_save(current_game, GAMESTATE_FILE) != 0) {
768 fprintf(stderr, "Warning: cannot save gamestate to %s\n", GAMESTATE_FILE);
772 /* Play the credits */
773 /*intro = create_credits();
774 intro_playback = animation_state_new(intro);
775 animation_state_run(intro_playback, 1);
776 animation_state_free(intro_playback);
777 animation_free(intro);*/
779 uninit_graphics();
780 uninit_input();
781 uninit_network();
783 SDL_Quit();
784 return 0;
788 void menu_button_init(MenuButton* b)
790 int w, h;
792 if (b->image_id != GR_COUNT) {
794 * If the button is an image, the "w" and "h" attributes of the
795 * MenuButton struct are simply factors with which the real width
796 * and height of the image (=button) should be multiplied and added
797 * to the "x" and "y" position and the "w" and "h" are replaced with
798 * the real size of the image for the collision detection
800 w = b->w;
801 h = b->h;
803 b->w = get_image_width(b->image_id);
804 b->h = get_image_height(b->image_id);
805 b->x += w*b->w;
806 b->y += h*b->h;