Experimental Python-based AI bot framework
[tennix.git] / tennix.c
blobfa500af1d27254dfbb99fd690ea924ff499d221e
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 "util.h"
40 #include "animation.h"
42 #include "locations.h"
44 SDL_Surface *screen;
46 #ifdef WIN32
48 /* IDs from the resource file */
49 #define START_BUTTON 1
50 #define CHECKBOX_FULLSCREEN 2
51 #define QUIT_BUTTON 3
53 BOOL CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
55 static int checkbox_is_checked;
57 switch (uMsg) {
58 case WM_CLOSE:
59 EndDialog(hwndDlg, IDCANCEL);
60 break;
61 case WM_COMMAND:
62 switch (wParam) {
63 case START_BUTTON:
64 EndDialog(hwndDlg, (checkbox_is_checked)?(IDYES):(IDNO));
65 break;
66 case QUIT_BUTTON:
67 EndDialog(hwndDlg, IDCANCEL);
68 break;
69 case CHECKBOX_FULLSCREEN:
70 checkbox_is_checked ^= 1;
71 break;
73 break;
74 default:
75 return FALSE;
77 return TRUE;
79 #endif
81 #ifdef WIN32
82 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
83 LPSTR lpCmdLine, int nCmdShow) {
84 #else
85 int main( int argc, char** argv) {
86 #endif
87 int i, slide, slide_direction;
88 unsigned int x;
89 Uint32 ticks;
90 int mx, my;
91 unsigned int worldmap_xpos, worldmap_ypos;
92 Uint8 *keys;
93 Uint8 mb;
94 SDL_Event e;
95 int sdl_flags = SDL_SWSURFACE;
96 int btn_hovering = 0, btn_hovering_old = 0;
97 int slide_start;
98 bool mouse_pressed = false;
99 bool quit = false;
100 bool benchmark = false;
101 unsigned int night_start, night_end;
102 GameState *current_game = NULL, *prepared_game = NULL;
103 InputDevice* input_devices;
104 unsigned int input_device_count, input_device_id;
105 Animation *intro;
106 AnimationState *intro_playback;
107 float wiggle;
109 MenuButton btn_back = {
110 "Back to main menu",
111 MENU_OPTIONS_BORDER,
112 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
113 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
114 255, 0, 0
116 MenuButton btn_start = {
117 "Start new game",
118 WIDTH-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_WIDTH),
119 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
120 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
121 0, 255, 0
123 MenuButton btn_player1 = {
124 NULL,
125 CONTROLLER_SETUP_BORDER,
126 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
127 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
128 50, 50, 255
130 MenuButton btn_player2 = {
131 NULL,
132 WIDTH-CONTROLLER_SETUP_SIZE-CONTROLLER_SETUP_BORDER,
133 HEIGHT/2 + CONTROLLER_SETUP_SIZE/2 + 10,
134 CONTROLLER_SETUP_SIZE, MENU_OPTIONS_BUTTON_HEIGHT,
135 255, 50, 50
138 int highlight_location = -1;
139 float highlight_location_distance = 0.0;
140 float new_location_distance = 0.0;
141 bool location_info_visible = false;
142 unsigned int location_info_xpos = 0, location_info_ypos = 0;
144 const SDL_VideoInfo* vi = NULL;
146 bool do_help = false;
148 int state = MENU_STATE_STARTED;
150 #ifdef ENABLE_FPS_LIMIT
151 Uint32 ft, frames; /* frame timer and frames */
152 #endif
154 #ifdef MAEMO
155 sdl_flags |= SDL_FULLSCREEN;
156 #endif
158 #ifdef WIN32
159 int mb_result;
160 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
162 switch (mb_result) {
163 case IDYES:
164 sdl_flags |= SDL_FULLSCREEN;
165 break;
166 case IDCANCEL:
167 return 0;
168 break;
169 default:
170 break;
172 #else
173 fprintf(stderr, "Tennix " VERSION "\n" COPYRIGHT "\n" URL "\n\n");
175 i = 1;
176 while (i < argc) {
177 /* A poor/lazy man's getopt */
178 #define OPTION_SET(longopt,shortopt) \
179 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
180 #define OPTION_VALUE \
181 ((i+1 < argc)?(argv[i+1]):(NULL))
182 #define OPTION_VALUE_PROCESSED \
183 (i++)
184 if (OPTION_SET("--fullscreen", "-f")) {
185 sdl_flags |= SDL_FULLSCREEN;
187 else if (OPTION_SET("--help", "-h")) {
188 do_help = true;
190 else if (OPTION_SET("--benchmark", "-b")) {
191 benchmark = true;
193 else {
194 fprintf(stderr, "Ignoring unknown option: %s\n", argv[i]);
196 i++;
199 if (do_help == true) {
200 fprintf(stderr, "Usage: %s [--fullscreen|-f] [--help|-h]\n", argv[0]);
201 return 0;
203 #endif
205 if (benchmark) {
206 srand(100);
207 } else {
208 srand((unsigned)time(NULL));
211 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
212 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
213 exit( 1);
216 vi = SDL_GetVideoInfo();
217 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
218 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
219 exit( 1);
222 SDL_WM_SetCaption( "Tennix " VERSION, "Tennix");
223 SDL_ShowCursor( SDL_DISABLE);
224 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
226 init_graphics();
227 init_sound();
228 init_input();
230 input_devices = find_input_devices(&input_device_count);
232 current_game = gamestate_load(GAMESTATE_FILE);
233 if (current_game != NULL) {
234 /* restore pointer to location */
235 current_game->location = &(locations[current_game->current_location]);
236 /* */
237 for (i=1; i<=MAXPLAYERS; i++) {
238 if (PLAYER(current_game, i).type == PLAYER_TYPE_HUMAN) {
239 input_device_id = PLAYER(current_game, i).input_device_index;
240 if (input_device_id < input_device_count) {
241 /* ok, we still have that device around */
242 PLAYER(current_game, i).input = &(input_devices[input_device_id]);
243 } else {
244 /* the device vanished - set to AI (FIXME: select new device) */
245 PLAYER(current_game, i).type = PLAYER_TYPE_AI;
246 PLAYER(current_game, i).input = NULL;
252 #ifdef ENABLE_FPS_LIMIT
253 frames = 0;
254 ft = SDL_GetTicks();
255 #endif
257 if (benchmark) {
258 GameState* g = gamestate_new();
259 PLAYER(g, 1).type = PLAYER_TYPE_AI;
260 PLAYER(g, 2).type = PLAYER_TYPE_AI;
261 g->timelimit = BENCHMARK_TIMELIMIT*1000;
262 g->location = &(locations[0]);
263 gameloop(g);
264 free(g);
265 exit(0);
268 intro = create_intro();
269 intro_playback = animation_state_new(intro);
270 animation_state_run(intro_playback, 1);
271 animation_state_free(intro_playback);
272 animation_free(intro);
274 worldmap_xpos = (WIDTH-get_image_width(GR_WORLDMAP))/2;
275 worldmap_ypos = (HEIGHT-get_image_height(GR_WORLDMAP))/2;
277 i = 0;
278 /* Sliding initialization */
279 ticks = SDL_GetTicks();
280 slide = slide_start = get_image_width(GR_SIDEBAR);
281 slide_direction = 0;
282 while(!quit) {
283 /* State transitions */
284 switch (state) {
285 case MENU_STATE_STARTED:
286 state = MENU_STATE_SLIDE_TO_MAINMENU;
287 break;
288 case MENU_STATE_SLIDE_TO_MAINMENU:
289 clear_screen();
290 store_screen();
291 slide = slide_start;
292 slide_direction = -1;
293 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
294 break;
295 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
296 if (slide == 0) {
297 slide_direction = 0;
298 state = MENU_STATE_MAINMENU;
300 break;
301 case MENU_STATE_MAINMENU:
302 free(prepared_game);
303 prepared_game = NULL;
304 break;
305 case MENU_STATE_SLIDE_TO_LOCATION:
306 slide = 1;
307 slide_direction = 3;
308 state = MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS;
309 break;
310 case MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS:
311 if (slide == slide_start) {
312 state = MENU_STATE_FADE_TO_LOCATION;
314 break;
315 case MENU_STATE_FADE_TO_LOCATION:
316 start_fade();
317 state = MENU_STATE_LOCATION;
318 clear_screen();
320 rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
321 /* Draw and store the worldmap with day/night times */
322 show_image(GR_WORLDMAP, WIDTH/2-get_image_width(GR_WORLDMAP)/2, HEIGHT/2-get_image_height(GR_WORLDMAP)/2, 255);
323 day_night(get_image_width(GR_WORLDMAP), &night_start, &night_end);
324 if (night_start > night_end) {
325 rectangle_alpha(worldmap_xpos, worldmap_ypos, night_end, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
326 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, get_image_width(GR_WORLDMAP)-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
327 } else {
328 rectangle_alpha(worldmap_xpos+night_start, worldmap_ypos, night_end-night_start, get_image_height(GR_WORLDMAP), 0, 0, 0, 150);
331 /* add misc items to screen */
332 font_draw_string(FONT_XLARGE, "Pick a location", (WIDTH-font_get_string_width(FONT_XLARGE, "Pick a location"))/2, 20);
334 store_screen();
335 break;
336 case MENU_STATE_FADE_TO_OPTIONS:
337 start_fade();
338 clear_screen();
339 rectangle(0, 0, WIDTH, HEIGHT, 80, 80, 80);
340 rectangle(CONTROLLER_SETUP_BORDER, HEIGHT/2-CONTROLLER_SETUP_SIZE/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
341 rectangle(WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE, (HEIGHT-CONTROLLER_SETUP_SIZE)/2, CONTROLLER_SETUP_SIZE, CONTROLLER_SETUP_SIZE, 150, 150, 150);
342 font_draw_string(FONT_XLARGE, "Controller setup", (WIDTH-font_get_string_width(FONT_XLARGE, "Controller setup"))/2, 20);
343 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));
344 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));
345 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));
346 store_screen();
347 start_fade();
348 state = MENU_STATE_OPTIONS;
349 break;
350 case MENU_STATE_LOCATION:
351 case MENU_STATE_OPTIONS:
352 /* Prepare a new game */
353 if (prepared_game == NULL) {
354 prepared_game = gamestate_new();
355 prepared_game->location = NULL;
356 btn_player1.text = "default";
357 btn_player2.text = "computer (AI)";
358 #ifdef MAEMO
359 PLAYER(prepared_game, 1).input_device_index = 0;
360 #else
361 PLAYER(prepared_game, 1).input_device_index = 2;
362 #endif
363 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
364 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
365 location_info_visible = false;
366 prepared_game->current_location = -1;
367 highlight_location = -1;
369 break;
370 case MENU_STATE_SLIDE_TO_GAME:
371 /*slide = 1;
372 slide_direction = 2;
373 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
374 break;
375 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
376 if (slide == slide_start) {
377 state = MENU_STATE_GAME;
379 state = MENU_STATE_GAME;
380 break;
381 case MENU_STATE_SLIDE_TO_RESUME:
382 slide = 1;
383 slide_direction = 2;
384 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
385 break;
386 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
387 if (slide == slide_start) {
388 state = MENU_STATE_RESUME;
390 break;
391 case MENU_STATE_GAME:
392 if (prepared_game == NULL) {
393 fprintf(stderr, "Game not yet prepared!\n");
394 exit(EXIT_FAILURE);
396 /* Set the day/night status */
397 if (night_start < night_end) {
398 prepared_game->night = (prepared_game->location->worldmap_x > night_start && prepared_game->location->worldmap_x < night_end);
399 } else {
400 prepared_game->night = (prepared_game->location->worldmap_x < night_end || prepared_game->location->worldmap_x > night_start);
403 /* Cancel a possibly started game */
404 free(current_game);
405 current_game = prepared_game;
406 prepared_game = NULL;
407 /* no break - we are continuing with "resume" */
408 case MENU_STATE_RESUME:
409 if (current_game == NULL) {
410 fprintf(stderr, "Cannot resume game!\n");
411 exit(EXIT_FAILURE);
413 start_fade();
414 gameloop(current_game);
415 SDL_Delay(150);
416 while(SDL_PollEvent(&e));
417 #ifdef ENABLE_FPS_LIMIT
418 frames = 0;
419 ft = SDL_GetTicks();
420 #endif
421 start_fade();
422 state = MENU_STATE_SLIDE_TO_MAINMENU;
423 break;
424 case MENU_STATE_SLIDE_TO_QUIT:
425 slide = 1;
426 slide_direction = 3;
427 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
428 break;
429 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
430 if (slide == slide_start) {
431 state = MENU_STATE_QUIT;
433 break;
434 case MENU_STATE_QUIT:
435 quit = true;
436 break;
437 default:
438 fprintf(stderr, "State error: %d\n", state);
439 exit(EXIT_FAILURE);
442 /* Sliding */
443 if (SDL_GetTicks() > ticks + 20) {
444 if (slide >= 1 && slide <= slide_start) {
445 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
446 slide = MAX(0, MIN(slide_start, slide));
447 } else if (slide_direction != 0) {
448 slide_direction = 0;
450 ticks = SDL_GetTicks();
453 /* Graphics */
454 #ifdef DEBUG
455 if (state != MENU_STATE_OPTIONS) {
456 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
458 #endif
459 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
460 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
461 if (state != MENU_STATE_OPTIONS && state != MENU_STATE_LOCATION) {
462 /* Main Menu */
463 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
464 if (current_game != NULL) {
465 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
466 font_draw_string(FONT_SMALL, "current match:", 10, 10);
467 font_draw_string(FONT_SMALL, current_game->sets_score_str, 10, 40);
468 } else {
469 font_draw_string(FONT_MEDIUM, "Tennix " VERSION, 10, 10);
471 font_draw_string_color(FONT_SMALL, URL, 10, HEIGHT-10-font_get_height(FONT_SMALL), 100, 100, 100);
472 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
473 } else if (state == MENU_STATE_OPTIONS) {
474 /* Options screen */
475 draw_button_object(btn_back, mx, my);
476 draw_button_object(btn_start, mx, my);
477 draw_button_object(btn_player1, mx, my);
478 draw_button_object(btn_player2, mx, my);
479 wiggle = 15*sinf((float)ticks/300.);
480 if (PLAYER(prepared_game, 1).input_device_index > -1) {
481 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);
482 } else {
483 show_image_rotozoom(GR_INPUT_AI, CONTROLLER_SETUP_BORDER + CONTROLLER_SETUP_SIZE/2, HEIGHT/2, wiggle, 1.0);
485 if (PLAYER(prepared_game, 2).input_device_index > -1) {
486 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);
487 } else {
488 show_image_rotozoom(GR_INPUT_AI, WIDTH-CONTROLLER_SETUP_BORDER-CONTROLLER_SETUP_SIZE/2, HEIGHT/2, -wiggle, 1.0);
490 } else if (state == MENU_STATE_LOCATION) {
491 /* Location selection screen */
492 for (x=0; x<location_count(); x++) {
493 new_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[x].worldmap_x,
494 my-worldmap_ypos-locations[x].worldmap_y);
495 if (highlight_location == -1) {
496 if (new_location_distance < 20*20) {
497 highlight_location = x;
499 } else {
500 highlight_location_distance = SQUARE_DISTANCE(mx-worldmap_xpos-locations[highlight_location].worldmap_x,
501 my-worldmap_ypos-locations[highlight_location].worldmap_y);
502 if (highlight_location_distance > 20*20) {
503 highlight_location = -1;
505 if (highlight_location_distance > new_location_distance && new_location_distance < 20*20) {
506 highlight_location = x;
510 if (prepared_game != NULL) {
511 if (!location_info_visible) {
512 for (x=0; x<location_count(); x++) {
513 /* draw rectangle for location at "x"*/
514 if (highlight_location != -1 && (unsigned int)highlight_location == x) {
515 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));
518 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);
520 } else {
521 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, 100, 0, 0, 200);
522 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);
523 font_draw_string(FONT_SMALL, prepared_game->location->name, location_info_xpos+MENU_OPTIONS_BORDER, location_info_ypos+MENU_OPTIONS_BORDER+200);
524 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));
525 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));
526 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));
528 if (prepared_game->location != NULL) {
529 draw_button_object(btn_start, mx, my);
532 draw_button_object(btn_back, mx, my);
535 SDL_PollEvent( &e);
536 if( e.type == SDL_QUIT) {
537 state = MENU_STATE_SLIDE_TO_QUIT;
538 /*break;*/
541 keys = SDL_GetKeyState( NULL);
542 mb = SDL_GetMouseState( &mx, &my);
544 btn_hovering_old = btn_hovering;
545 if (state == MENU_STATE_MAINMENU) {
546 btn_hovering = M_POS_DECODE(mx, my);
547 if (current_game == NULL) {
548 btn_hovering &= ~MENU_RESUME;
550 } else if (state == MENU_STATE_LOCATION) {
551 if (M_POS_BUTTON(btn_back, mx, my)) {
552 btn_hovering = MENU_QUIT;
553 } else if (M_POS_BUTTON(btn_start, mx, my)) {
554 btn_hovering = MENU_START;
555 } else {
556 btn_hovering = 0;
558 } else if (state == MENU_STATE_OPTIONS) {
559 if (M_POS_BUTTON(btn_back, mx, my)) {
560 btn_hovering = MENU_QUIT;
561 } else if (M_POS_BUTTON(btn_start, mx, my)) {
562 btn_hovering = MENU_START;
563 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
564 btn_hovering = MENU_PLAYER1;
565 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
566 btn_hovering = MENU_PLAYER2;
567 } else {
568 btn_hovering = 0;
570 } else {
571 /* No menu screen - no hovering. */
572 btn_hovering = 0;
574 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
575 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
576 #ifdef HAVE_VOICE_FILES
577 if (btn_hovering == MENU_QUIT) {
578 play_sample(VOICE_QUIT_IT);
579 } else if (btn_hovering == MENU_START) {
580 play_sample(VOICE_NEW_GAME);
581 } else {
582 play_sample(SOUND_MOUSEOVER);
584 #else
585 /*play_sample(SOUND_MOUSEOVER);*/
586 #endif
588 #endif
590 if( keys[SDLK_ESCAPE] || keys['q']) {
591 /* FIXME: do the state thingie! */
592 break;
595 if( keys['f']) {
596 SDL_WM_ToggleFullScreen( screen);
599 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
600 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
601 show_image(GR_CURSOR, mx-2, my-1, 255);
603 #endif
605 /* Draw the "real" mouse coordinates */
606 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
608 /* Store the screen, because we are fading after this screen update */
609 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
611 updatescr();
613 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
614 if (!mouse_pressed) {
615 play_sample(SOUND_MOUSEOVER);
617 mouse_pressed = true;
618 } else if (mouse_pressed == true) {
619 /* Mouse button released */
620 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS || state == MENU_STATE_LOCATION) {
621 #ifdef HAVE_VOICE_FILES
622 if (btn_hovering == MENU_START) {
623 play_sample(VOICE_LETS_GO);
624 } else {
625 play_sample(SOUND_MOUSECLICK);
627 #else
628 /*play_sample(SOUND_MOUSEOVER);*/
629 #endif
631 if (state == MENU_STATE_MAINMENU) {
632 switch (btn_hovering) {
633 case MENU_START:
634 state = MENU_STATE_SLIDE_TO_LOCATION;
635 break;
636 case MENU_RESUME:
637 state = MENU_STATE_SLIDE_TO_RESUME;
638 break;
639 case MENU_QUIT:
640 state = MENU_STATE_SLIDE_TO_QUIT;
641 break;
643 } else if (state == MENU_STATE_LOCATION) {
644 switch (btn_hovering) {
645 case MENU_START:
646 if (prepared_game->location != NULL) {
647 state = MENU_STATE_FADE_TO_OPTIONS;
649 break;
650 case MENU_QUIT:
651 state = MENU_STATE_SLIDE_TO_MAINMENU;
652 break;
653 default:
654 if (!location_info_visible && highlight_location != -1) {
655 prepared_game->current_location = highlight_location;
656 prepared_game->location = &(locations[prepared_game->current_location]);
657 location_info_xpos = MAX(0, MIN(WIDTH-320-50, mx-320/2));
658 location_info_ypos = MAX(0, MIN(HEIGHT-200-160, my-200/2));
659 location_info_visible = true;
660 } else {
661 location_info_visible = false;
662 highlight_location = -1;
663 prepared_game->current_location = -1;
664 prepared_game->location = NULL;
666 break;
668 } else if (state == MENU_STATE_OPTIONS) {
669 switch (btn_hovering) {
670 case MENU_START:
671 state = MENU_STATE_SLIDE_TO_GAME;
672 break;
673 case MENU_QUIT:
674 state = MENU_STATE_FADE_TO_LOCATION;
675 break;
676 case MENU_PLAYER1:
677 /* advance the input device index */
678 PLAYER(prepared_game, 1).input_device_index++;
679 if (PLAYER(prepared_game, 1).input_device_index == (signed int)input_device_count) {
680 PLAYER(prepared_game, 1).input_device_index = -1;
683 if (input_devices[PLAYER(prepared_game, 1).input_device_index].exclusive_to_player == 2) {
684 PLAYER(prepared_game, 1).input_device_index++;
687 /* determine the selected input device */
688 if (PLAYER(prepared_game, 1).input_device_index == -1) {
689 PLAYER(prepared_game, 1).type = PLAYER_TYPE_AI;
690 PLAYER(prepared_game, 1).input = NULL;
691 btn_player1.text = "Computer (AI)";
692 } else {
693 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
694 PLAYER(prepared_game, 1).input = &(input_devices[PLAYER(prepared_game, 1).input_device_index]);
695 btn_player1.text = input_device_get_name(PLAYER(prepared_game, 1).input);
697 break;
698 case MENU_PLAYER2:
699 /* advance the input device index */
700 PLAYER(prepared_game, 2).input_device_index++;
702 if (input_devices[PLAYER(prepared_game, 2).input_device_index].exclusive_to_player == 1) {
703 PLAYER(prepared_game, 2).input_device_index++;
706 if (PLAYER(prepared_game, 2).input_device_index == (signed int)input_device_count) {
707 PLAYER(prepared_game, 2).input_device_index = -1;
710 /* determine the selected input device */
711 if (PLAYER(prepared_game, 2).input_device_index == -1) {
712 PLAYER(prepared_game, 2).type = PLAYER_TYPE_AI;
713 PLAYER(prepared_game, 2).input = NULL;
714 btn_player2.text = "Computer (AI)";
715 } else {
716 PLAYER(prepared_game, 2).type = PLAYER_TYPE_HUMAN;
717 PLAYER(prepared_game, 2).input = &(input_devices[PLAYER(prepared_game, 2).input_device_index]);
718 btn_player2.text = input_device_get_name(PLAYER(prepared_game, 2).input);
720 break;
723 mouse_pressed = false;
725 i++;
726 #ifdef ENABLE_FPS_LIMIT
727 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
728 SDL_Delay(10);
730 frames++;
731 #endif
734 if (current_game != NULL) {
735 if (gamestate_save(current_game, GAMESTATE_FILE) != 0) {
736 fprintf(stderr, "Warning: cannot save gamestate to %s\n", GAMESTATE_FILE);
740 uninit_graphics();
741 uninit_input();
743 SDL_Quit();
744 return 0;