Loading progress bar, add missing header files, Maemo
[tennix.git] / tennix.c
blobb6acee41f9e7807dfc3a741de4c58daffd574f93
2 /**
4 * Tennix! SDL Port
5 * Copyright (C) 2003, 2007, 2008 Thomas Perl <thp@perli.net>
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"
40 SDL_Surface *screen;
42 #ifdef WIN32
44 /* IDs from the resource file */
45 #define START_BUTTON 1
46 #define CHECKBOX_FULLSCREEN 2
47 #define QUIT_BUTTON 3
49 BOOL CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
51 static int checkbox_is_checked;
53 switch (uMsg) {
54 case WM_CLOSE:
55 EndDialog(hwndDlg, IDCANCEL);
56 break;
57 case WM_COMMAND:
58 switch (wParam) {
59 case START_BUTTON:
60 EndDialog(hwndDlg, (checkbox_is_checked)?(IDYES):(IDNO));
61 break;
62 case QUIT_BUTTON:
63 EndDialog(hwndDlg, IDCANCEL);
64 break;
65 case CHECKBOX_FULLSCREEN:
66 checkbox_is_checked ^= 1;
67 break;
69 break;
70 default:
71 return FALSE;
73 return TRUE;
75 #endif
77 #ifdef WIN32
78 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
79 LPSTR lpCmdLine, int nCmdShow) {
80 #else
81 int main( int argc, char** argv) {
82 #endif
83 int i, slide, slide_direction;
84 int ticks;
85 int mx, my;
86 Uint8 *keys;
87 Uint8 mb;
88 SDL_Event e;
89 int sdl_flags = SDL_SWSURFACE;
90 int btn_hovering = 0, btn_hovering_old = 0;
91 int slide_start;
92 bool mouse_pressed = false;
93 bool quit = false;
94 GameState *current_game = NULL, *prepared_game = NULL;
96 MenuButton btn_back = {
97 "Back to main menu",
98 MENU_OPTIONS_BORDER,
99 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
100 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
101 255, 0, 0
103 MenuButton btn_start = {
104 "Start new game",
105 WIDTH-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_WIDTH),
106 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
107 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
108 0, 255, 0
110 MenuButton btn_court_change = {
111 "change",
112 240,
114 50, MENU_OPTIONS_BUTTON_HEIGHT,
115 100, 100, 100
117 MenuButton btn_player1 = {
118 NULL,
119 380,
120 180,
121 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
122 50, 50, 255
124 MenuButton btn_player2 = {
125 NULL,
126 380,
127 180+MENU_OPTIONS_BORDER*2+MENU_OPTIONS_BUTTON_HEIGHT,
128 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
129 255, 50, 50
132 int state = MENU_STATE_STARTED;
134 #ifdef ENABLE_FPS_LIMIT
135 Uint32 ft, frames; /* frame timer and frames */
136 #endif
138 #ifdef MAEMO
139 sdl_flags |= SDL_FULLSCREEN;
140 #endif
142 #ifdef WIN32
143 int mb_result;
144 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
146 switch (mb_result) {
147 case IDYES:
148 sdl_flags |= SDL_FULLSCREEN;
149 break;
150 case IDCANCEL:
151 return 0;
152 break;
153 default:
154 break;
156 #else
157 fprintf( stderr, "Tennix %s\n%s\n%s\n\n", VERSION, COPYRIGHT, URL);
159 bool do_help = false;
160 i = 1;
161 while (i < argc) {
162 /* A poor/lazy man's getopt */
163 #define OPTION_SET(longopt,shortopt) \
164 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
165 #define OPTION_VALUE \
166 ((i+1 < argc)?(argv[i+1]):(NULL))
167 #define OPTION_VALUE_PROCESSED \
168 (i++)
169 if (OPTION_SET("--fullscreen", "-f")) {
170 sdl_flags |= SDL_FULLSCREEN;
172 else if (OPTION_SET("--help", "-h")) {
173 do_help = true;
175 else if (OPTION_SET("--list-joysticks", "-J")) {
176 SDL_Init(SDL_INIT_JOYSTICK);
177 joystick_list();
178 return 0;
180 else if (OPTION_SET("--joystick", "-j")) {
181 SDL_Init(SDL_INIT_JOYSTICK);
182 if (OPTION_VALUE == NULL) {
183 fprintf(stderr, "Error: You need to specify the name of the joystick as parameter.\n");
184 do_help = true;
185 break;
187 if (joystick_open(OPTION_VALUE)==0) {
188 fprintf(stderr, "Warning: Cannot find joystick \"%s\" - Ignored.\n", OPTION_VALUE);
189 break;
191 OPTION_VALUE_PROCESSED;
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] [--list-joysticks|-J] [--joystick|-j] [joystick name]\n", argv[0]);
201 return 0;
203 #endif
205 srand( (unsigned)time( NULL));
207 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
208 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
209 exit( 1);
212 SDL_VideoInfo* vi = (SDL_VideoInfo*)SDL_GetVideoInfo();
213 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
214 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
215 exit( 1);
218 SDL_WM_SetCaption( "Tennix " VERSION, "Tennix");
219 SDL_ShowCursor( SDL_DISABLE);
220 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
222 init_graphics();
223 init_sound();
224 init_joystick();
226 #ifdef ENABLE_FPS_LIMIT
227 frames = 0;
228 ft = SDL_GetTicks();
229 #endif
231 i = 0;
232 /* Sliding initialization */
233 ticks = SDL_GetTicks();
234 slide = slide_start = get_image_width(GR_SIDEBAR);
235 slide_direction = 0;
236 while(!quit) {
237 /* State transitions */
238 switch (state) {
239 case MENU_STATE_STARTED:
240 state = MENU_STATE_SLIDE_TO_MAINMENU;
241 break;
242 case MENU_STATE_SLIDE_TO_MAINMENU:
243 slide = slide_start;
244 slide_direction = -1;
245 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
246 break;
247 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
248 if (slide == 0) {
249 slide_direction = 0;
250 state = MENU_STATE_MAINMENU;
252 break;
253 case MENU_STATE_MAINMENU:
254 free(prepared_game);
255 prepared_game = NULL;
256 break;
257 case MENU_STATE_SLIDE_TO_OPTIONS:
258 slide = 1;
259 slide_direction = 3;
260 state = MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS;
261 break;
262 case MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS:
263 if (slide == slide_start) {
264 start_fade();
265 state = MENU_STATE_OPTIONS;
267 break;
268 case MENU_STATE_OPTIONS:
269 /* Prepare a new game */
270 if (prepared_game == NULL) {
271 prepared_game = gamestate_new();
272 btn_player1.text = "Keyboard (W-S-D)";
273 btn_player2.text = "Computer (AI)";
275 break;
276 case MENU_STATE_SLIDE_TO_GAME:
277 /*slide = 1;
278 slide_direction = 2;
279 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
280 break;
281 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
282 if (slide == slide_start) {
283 state = MENU_STATE_GAME;
285 state = MENU_STATE_GAME;
286 break;
287 case MENU_STATE_SLIDE_TO_RESUME:
288 slide = 1;
289 slide_direction = 2;
290 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
291 break;
292 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
293 if (slide == slide_start) {
294 state = MENU_STATE_RESUME;
296 break;
297 case MENU_STATE_GAME:
298 if (prepared_game == NULL) {
299 fprintf(stderr, "Game not yet prepared!\n");
300 exit(EXIT_FAILURE);
302 /* Cancel a possibly started game */
303 free(current_game);
304 current_game = prepared_game;
305 prepared_game = NULL;
306 /* no break - we are continuing with "resume" */
307 case MENU_STATE_RESUME:
308 if (current_game == NULL) {
309 fprintf(stderr, "Cannot resume game!\n");
310 exit(EXIT_FAILURE);
312 start_fade();
313 gameloop(current_game);
314 SDL_Delay(150);
315 while(SDL_PollEvent(&e));
316 #ifdef ENABLE_FPS_LIMIT
317 frames = 0;
318 ft = SDL_GetTicks();
319 #endif
320 start_fade();
321 state = MENU_STATE_SLIDE_TO_MAINMENU;
322 break;
323 case MENU_STATE_SLIDE_TO_QUIT:
324 slide = 1;
325 slide_direction = 3;
326 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
327 break;
328 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
329 if (slide == slide_start) {
330 state = MENU_STATE_QUIT;
332 break;
333 case MENU_STATE_QUIT:
334 quit = true;
335 break;
336 default:
337 fprintf(stderr, "State error: %d\n", state);
338 exit(EXIT_FAILURE);
341 /* Sliding */
342 if (SDL_GetTicks() > ticks + 20) {
343 if (slide >= 1 && slide <= slide_start) {
344 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
345 slide = MAX(0, MIN(slide_start, slide));
346 } else if (slide_direction != 0) {
347 slide_direction = 0;
349 ticks = SDL_GetTicks();
352 /* Graphics */
353 #ifdef DEBUG
354 if (state != MENU_STATE_OPTIONS) {
355 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
357 #endif
358 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
359 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
360 if (state != MENU_STATE_OPTIONS) {
361 /* Main Menu */
362 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
363 if (current_game != NULL) {
364 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
365 font_draw_string(GR_DKC2_FONT, "current match:", 10, 10, 0, 0);
366 font_draw_string(GR_DKC2_FONT, current_game->sets_score_str, 10, 40, 0, 0);
368 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
369 } else {
370 /* Options screen */
371 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20, 255);
372 draw_button_object(btn_back, mx, my);
373 draw_button_object(btn_start, mx, my);
374 if (prepared_game != NULL) {
375 fill_image(prepared_game->court_type, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, get_image_width(GR_STADIUM), get_image_height(GR_STADIUM));
376 show_image(GR_STADIUM, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, 255);
377 draw_button_object(btn_court_change, mx, my);
378 font_draw_string(GR_DKC2_FONT, "Location", MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER, 0, 0);
379 draw_button_object(btn_player1, mx, my);
380 draw_button_object(btn_player2, mx, my);
381 font_draw_string(GR_DKC2_FONT, "Player 1", btn_player1.x, btn_player1.y-MENU_OPTIONS_BORDER, 0, 0);
382 font_draw_string(GR_DKC2_FONT, "Player 2", btn_player2.x, btn_player2.y-MENU_OPTIONS_BORDER, 0, 0);
386 SDL_PollEvent( &e);
387 if( e.type == SDL_QUIT) {
388 state = MENU_STATE_SLIDE_TO_QUIT;
389 /*break;*/
392 keys = SDL_GetKeyState( NULL);
393 mb = SDL_GetMouseState( &mx, &my);
395 btn_hovering_old = btn_hovering;
396 if (state == MENU_STATE_MAINMENU) {
397 btn_hovering = M_POS_DECODE(mx, my);
398 if (current_game == NULL) {
399 btn_hovering &= ~MENU_RESUME;
401 } else if (state == MENU_STATE_OPTIONS) {
402 if (M_POS_BUTTON(btn_back, mx, my)) {
403 btn_hovering = MENU_QUIT;
404 } else if (M_POS_BUTTON(btn_start, mx, my)) {
405 btn_hovering = MENU_START;
406 } else if (M_POS_BUTTON(btn_court_change, mx, my)) {
407 btn_hovering = MENU_COURT_CHANGE;
408 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
409 btn_hovering = MENU_PLAYER1;
410 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
411 btn_hovering = MENU_PLAYER2;
412 } else {
413 btn_hovering = 0;
415 } else {
416 /* No menu screen - no hovering. */
417 btn_hovering = 0;
419 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
420 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
421 #ifdef HAVE_VOICE_FILES
422 if (btn_hovering == MENU_QUIT) {
423 play_sample(VOICE_QUIT_IT);
424 } else if (btn_hovering == MENU_START) {
425 play_sample(VOICE_NEW_GAME);
426 } else {
427 play_sample(SOUND_MOUSEOVER);
429 #else
430 play_sample(SOUND_MOUSEOVER);
431 #endif
433 #endif
435 if( keys[SDLK_ESCAPE] || keys['q']) {
436 /* FIXME: do the state thingie! */
437 break;
440 if( keys['f']) {
441 SDL_WM_ToggleFullScreen( screen);
444 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
445 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
446 show_sprite( GR_RACKET, ((mb&SDL_BUTTON( SDL_BUTTON_LEFT))>0)+(((mb&SDL_BUTTON( SDL_BUTTON_RIGHT))>0)*2), 4, mx, my, 255);
448 #endif
450 /* Draw the "real" mouse coordinates */
451 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
453 /* Store the screen, because we are fading after this screen update */
454 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
456 updatescr();
458 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
459 mouse_pressed = true;
460 } else if (mouse_pressed == true) {
461 /* Mouse button released */
462 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
463 #ifdef HAVE_VOICE_FILES
464 if (btn_hovering == MENU_START) {
465 play_sample(VOICE_LETS_GO);
466 } else {
467 play_sample(SOUND_MOUSECLICK);
469 #else
470 play_sample(SOUND_MOUSECLICK);
471 #endif
473 if (state == MENU_STATE_MAINMENU) {
474 switch (btn_hovering) {
475 case MENU_START:
476 state = MENU_STATE_SLIDE_TO_OPTIONS;
477 break;
478 case MENU_RESUME:
479 state = MENU_STATE_SLIDE_TO_RESUME;
480 break;
481 case MENU_QUIT:
482 state = MENU_STATE_SLIDE_TO_QUIT;
483 break;
485 } else if (state == MENU_STATE_OPTIONS) {
486 switch (btn_hovering) {
487 case MENU_START:
488 state = MENU_STATE_SLIDE_TO_GAME;
489 break;
490 case MENU_QUIT:
491 state = MENU_STATE_SLIDE_TO_MAINMENU;
492 break;
493 case MENU_COURT_CHANGE:
494 prepared_game->court_type++;
495 if (prepared_game->court_type > GR_CTT_LAST) {
496 prepared_game->court_type = GR_CTT_FIRST;
498 break;
499 case MENU_PLAYER1:
500 switch (prepared_game->player1.type) {
501 case PLAYER_TYPE_HUMAN:
502 prepared_game->player1.type = PLAYER_TYPE_AI;
503 btn_player1.text = "Computer (AI)";
504 break;
505 case PLAYER_TYPE_AI:
506 prepared_game->player1.type = PLAYER_TYPE_HUMAN;
507 btn_player1.text = "Keyboard (W-S-D)";
508 break;
510 break;
511 case MENU_PLAYER2:
512 switch (prepared_game->player2.type) {
513 case PLAYER_TYPE_HUMAN:
514 prepared_game->player2.type = PLAYER_TYPE_AI;
515 btn_player2.text = "Computer (AI)";
516 break;
517 case PLAYER_TYPE_AI:
518 prepared_game->player2.type = PLAYER_TYPE_HUMAN;
519 btn_player2.text = "Keyboard (O-L-K)";
520 break;
522 break;
525 mouse_pressed = false;
527 i++;
528 #ifdef ENABLE_FPS_LIMIT
529 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
530 SDL_Delay(10);
532 frames++;
533 #endif
536 uninit_graphics();
537 uninit_joystick();
539 SDL_Quit();
540 return 0;