Add some missing #include lines, use C99 as standard
[tennix.git] / tennix.c
blobb8ffcef7ae4a2141614077c8ace0edf38333c3dd
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"
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 Uint32 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 bool benchmark = false;
95 GameState *current_game = NULL, *prepared_game = NULL;
97 MenuButton btn_back = {
98 "Back to main menu",
99 MENU_OPTIONS_BORDER,
100 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
101 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
102 255, 0, 0
104 MenuButton btn_start = {
105 "Start new game",
106 WIDTH-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_WIDTH),
107 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
108 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
109 0, 255, 0
111 MenuButton btn_court_change = {
112 "change",
113 240,
115 50, MENU_OPTIONS_BUTTON_HEIGHT,
116 100, 100, 100
118 MenuButton btn_player1 = {
119 NULL,
120 380,
121 180,
122 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
123 50, 50, 255
125 MenuButton btn_player2 = {
126 NULL,
127 380,
128 180+MENU_OPTIONS_BORDER*2+MENU_OPTIONS_BUTTON_HEIGHT,
129 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
130 255, 50, 50
133 const SDL_VideoInfo* vi = NULL;
135 bool do_help = false;
137 int state = MENU_STATE_STARTED;
139 #ifdef ENABLE_FPS_LIMIT
140 Uint32 ft, frames; /* frame timer and frames */
141 #endif
143 #ifdef MAEMO
144 sdl_flags |= SDL_FULLSCREEN;
145 #endif
147 #ifdef WIN32
148 int mb_result;
149 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
151 switch (mb_result) {
152 case IDYES:
153 sdl_flags |= SDL_FULLSCREEN;
154 break;
155 case IDCANCEL:
156 return 0;
157 break;
158 default:
159 break;
161 #else
162 fprintf(stderr, "Tennix " VERSION "\n" COPYRIGHT "\n" URL "\n\n");
164 i = 1;
165 while (i < argc) {
166 /* A poor/lazy man's getopt */
167 #define OPTION_SET(longopt,shortopt) \
168 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
169 #define OPTION_VALUE \
170 ((i+1 < argc)?(argv[i+1]):(NULL))
171 #define OPTION_VALUE_PROCESSED \
172 (i++)
173 if (OPTION_SET("--fullscreen", "-f")) {
174 sdl_flags |= SDL_FULLSCREEN;
176 else if (OPTION_SET("--help", "-h")) {
177 do_help = true;
179 else if (OPTION_SET("--list-joysticks", "-J")) {
180 SDL_Init(SDL_INIT_JOYSTICK);
181 joystick_list();
182 return 0;
184 else if (OPTION_SET("--benchmark", "-b")) {
185 benchmark = true;
187 else if (OPTION_SET("--joystick", "-j")) {
188 SDL_Init(SDL_INIT_JOYSTICK);
189 if (OPTION_VALUE == NULL) {
190 fprintf(stderr, "Error: You need to specify the name of the joystick as parameter.\n");
191 do_help = true;
192 break;
194 if (joystick_open(OPTION_VALUE)==0) {
195 fprintf(stderr, "Warning: Cannot find joystick \"%s\" - Ignored.\n", OPTION_VALUE);
196 break;
198 OPTION_VALUE_PROCESSED;
200 else {
201 fprintf(stderr, "Ignoring unknown option: %s\n", argv[i]);
203 i++;
206 if (do_help == true) {
207 fprintf(stderr, "Usage: %s [--fullscreen|-f] [--help|-h] [--list-joysticks|-J] [--joystick|-j] [joystick name]\n", argv[0]);
208 return 0;
210 #endif
212 if (benchmark) {
213 srand(100);
214 } else {
215 srand((unsigned)time(NULL));
218 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
219 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
220 exit( 1);
223 vi = SDL_GetVideoInfo();
224 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
225 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
226 exit( 1);
229 SDL_WM_SetCaption( "Tennix " VERSION, "Tennix");
230 SDL_ShowCursor( SDL_DISABLE);
231 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
233 init_graphics();
234 init_sound();
235 init_joystick();
237 #ifdef ENABLE_FPS_LIMIT
238 frames = 0;
239 ft = SDL_GetTicks();
240 #endif
242 if (benchmark) {
243 GameState* g = gamestate_new();
244 PLAYER(g, 1).type = PLAYER_TYPE_AI;
245 PLAYER(g, 2).type = PLAYER_TYPE_AI;
246 g->timelimit = BENCHMARK_TIMELIMIT*1000;
247 gameloop(g);
248 free(g);
249 exit(0);
252 i = 0;
253 /* Sliding initialization */
254 ticks = SDL_GetTicks();
255 slide = slide_start = get_image_width(GR_SIDEBAR);
256 slide_direction = 0;
257 while(!quit) {
258 /* State transitions */
259 switch (state) {
260 case MENU_STATE_STARTED:
261 state = MENU_STATE_SLIDE_TO_MAINMENU;
262 break;
263 case MENU_STATE_SLIDE_TO_MAINMENU:
264 slide = slide_start;
265 slide_direction = -1;
266 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
267 break;
268 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
269 if (slide == 0) {
270 slide_direction = 0;
271 state = MENU_STATE_MAINMENU;
273 break;
274 case MENU_STATE_MAINMENU:
275 free(prepared_game);
276 prepared_game = NULL;
277 break;
278 case MENU_STATE_SLIDE_TO_OPTIONS:
279 slide = 1;
280 slide_direction = 3;
281 state = MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS;
282 break;
283 case MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS:
284 if (slide == slide_start) {
285 start_fade();
286 state = MENU_STATE_OPTIONS;
288 break;
289 case MENU_STATE_OPTIONS:
290 /* Prepare a new game */
291 if (prepared_game == NULL) {
292 prepared_game = gamestate_new();
293 btn_player1.text = "Keyboard (W-S-D)";
294 btn_player2.text = "Computer (AI)";
296 break;
297 case MENU_STATE_SLIDE_TO_GAME:
298 /*slide = 1;
299 slide_direction = 2;
300 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
301 break;
302 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
303 if (slide == slide_start) {
304 state = MENU_STATE_GAME;
306 state = MENU_STATE_GAME;
307 break;
308 case MENU_STATE_SLIDE_TO_RESUME:
309 slide = 1;
310 slide_direction = 2;
311 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
312 break;
313 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
314 if (slide == slide_start) {
315 state = MENU_STATE_RESUME;
317 break;
318 case MENU_STATE_GAME:
319 if (prepared_game == NULL) {
320 fprintf(stderr, "Game not yet prepared!\n");
321 exit(EXIT_FAILURE);
323 /* Cancel a possibly started game */
324 free(current_game);
325 current_game = prepared_game;
326 prepared_game = NULL;
327 /* no break - we are continuing with "resume" */
328 case MENU_STATE_RESUME:
329 if (current_game == NULL) {
330 fprintf(stderr, "Cannot resume game!\n");
331 exit(EXIT_FAILURE);
333 start_fade();
334 gameloop(current_game);
335 SDL_Delay(150);
336 while(SDL_PollEvent(&e));
337 #ifdef ENABLE_FPS_LIMIT
338 frames = 0;
339 ft = SDL_GetTicks();
340 #endif
341 start_fade();
342 state = MENU_STATE_SLIDE_TO_MAINMENU;
343 break;
344 case MENU_STATE_SLIDE_TO_QUIT:
345 slide = 1;
346 slide_direction = 3;
347 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
348 break;
349 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
350 if (slide == slide_start) {
351 state = MENU_STATE_QUIT;
353 break;
354 case MENU_STATE_QUIT:
355 quit = true;
356 break;
357 default:
358 fprintf(stderr, "State error: %d\n", state);
359 exit(EXIT_FAILURE);
362 /* Sliding */
363 if (SDL_GetTicks() > ticks + 20) {
364 if (slide >= 1 && slide <= slide_start) {
365 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
366 slide = MAX(0, MIN(slide_start, slide));
367 } else if (slide_direction != 0) {
368 slide_direction = 0;
370 ticks = SDL_GetTicks();
373 /* Graphics */
374 #ifdef DEBUG
375 if (state != MENU_STATE_OPTIONS) {
376 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
378 #endif
379 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
380 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
381 if (state != MENU_STATE_OPTIONS) {
382 /* Main Menu */
383 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
384 if (current_game != NULL) {
385 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
386 font_draw_string(GR_DKC2_FONT, "current match:", 10, 10, 0, 0);
387 font_draw_string(GR_DKC2_FONT, current_game->sets_score_str, 10, 40, 0, 0);
388 } else {
389 font_draw_string(GR_DKC2_FONT, "Tennix " VERSION, 10, 10, 0, 0);
391 font_draw_string(GR_DKC2_FONT, URL, 10, HEIGHT-10-get_image_height(GR_DKC2_FONT), 0, 0);
392 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
393 } else {
394 /* Options screen */
395 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20, 255);
396 draw_button_object(btn_back, mx, my);
397 draw_button_object(btn_start, mx, my);
398 if (prepared_game != NULL) {
399 fill_image(prepared_game->court_type, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, get_image_width(GR_STADIUM), get_image_height(GR_STADIUM));
400 show_image(GR_STADIUM, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, 255);
401 draw_button_object(btn_court_change, mx, my);
402 font_draw_string(GR_DKC2_FONT, "Location", MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER, 0, 0);
403 draw_button_object(btn_player1, mx, my);
404 draw_button_object(btn_player2, mx, my);
405 font_draw_string(GR_DKC2_FONT, "Player 1", btn_player1.x, btn_player1.y-MENU_OPTIONS_BORDER, 0, 0);
406 font_draw_string(GR_DKC2_FONT, "Player 2", btn_player2.x, btn_player2.y-MENU_OPTIONS_BORDER, 0, 0);
410 SDL_PollEvent( &e);
411 if( e.type == SDL_QUIT) {
412 state = MENU_STATE_SLIDE_TO_QUIT;
413 /*break;*/
416 keys = SDL_GetKeyState( NULL);
417 mb = SDL_GetMouseState( &mx, &my);
419 btn_hovering_old = btn_hovering;
420 if (state == MENU_STATE_MAINMENU) {
421 btn_hovering = M_POS_DECODE(mx, my);
422 if (current_game == NULL) {
423 btn_hovering &= ~MENU_RESUME;
425 } else if (state == MENU_STATE_OPTIONS) {
426 if (M_POS_BUTTON(btn_back, mx, my)) {
427 btn_hovering = MENU_QUIT;
428 } else if (M_POS_BUTTON(btn_start, mx, my)) {
429 btn_hovering = MENU_START;
430 } else if (M_POS_BUTTON(btn_court_change, mx, my)) {
431 btn_hovering = MENU_COURT_CHANGE;
432 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
433 btn_hovering = MENU_PLAYER1;
434 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
435 btn_hovering = MENU_PLAYER2;
436 } else {
437 btn_hovering = 0;
439 } else {
440 /* No menu screen - no hovering. */
441 btn_hovering = 0;
443 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
444 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
445 #ifdef HAVE_VOICE_FILES
446 if (btn_hovering == MENU_QUIT) {
447 play_sample(VOICE_QUIT_IT);
448 } else if (btn_hovering == MENU_START) {
449 play_sample(VOICE_NEW_GAME);
450 } else {
451 play_sample(SOUND_MOUSEOVER);
453 #else
454 play_sample(SOUND_MOUSEOVER);
455 #endif
457 #endif
459 if( keys[SDLK_ESCAPE] || keys['q']) {
460 /* FIXME: do the state thingie! */
461 break;
464 if( keys['f']) {
465 SDL_WM_ToggleFullScreen( screen);
468 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
469 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
470 show_sprite( GR_RACKET, ((mb&SDL_BUTTON( SDL_BUTTON_LEFT))>0)+(((mb&SDL_BUTTON( SDL_BUTTON_RIGHT))>0)*2), 4, mx, my, 255);
472 #endif
474 /* Draw the "real" mouse coordinates */
475 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
477 /* Store the screen, because we are fading after this screen update */
478 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
480 updatescr();
482 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
483 mouse_pressed = true;
484 } else if (mouse_pressed == true) {
485 /* Mouse button released */
486 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
487 #ifdef HAVE_VOICE_FILES
488 if (btn_hovering == MENU_START) {
489 play_sample(VOICE_LETS_GO);
490 } else {
491 play_sample(SOUND_MOUSECLICK);
493 #else
494 play_sample(SOUND_MOUSECLICK);
495 #endif
497 if (state == MENU_STATE_MAINMENU) {
498 switch (btn_hovering) {
499 case MENU_START:
500 state = MENU_STATE_SLIDE_TO_OPTIONS;
501 break;
502 case MENU_RESUME:
503 state = MENU_STATE_SLIDE_TO_RESUME;
504 break;
505 case MENU_QUIT:
506 state = MENU_STATE_SLIDE_TO_QUIT;
507 break;
509 } else if (state == MENU_STATE_OPTIONS) {
510 switch (btn_hovering) {
511 case MENU_START:
512 state = MENU_STATE_SLIDE_TO_GAME;
513 break;
514 case MENU_QUIT:
515 state = MENU_STATE_SLIDE_TO_MAINMENU;
516 break;
517 case MENU_COURT_CHANGE:
518 prepared_game->court_type++;
519 if (prepared_game->court_type > GR_CTT_LAST) {
520 prepared_game->court_type = GR_CTT_FIRST;
522 break;
523 case MENU_PLAYER1:
524 switch (PLAYER(prepared_game, 1).type) {
525 case PLAYER_TYPE_HUMAN:
526 PLAYER(prepared_game, 1).type = PLAYER_TYPE_AI;
527 btn_player1.text = "Computer (AI)";
528 break;
529 case PLAYER_TYPE_AI:
530 PLAYER(prepared_game, 1).type = PLAYER_TYPE_HUMAN;
531 btn_player1.text = "Keyboard (W-S-D)";
532 break;
534 break;
535 case MENU_PLAYER2:
536 switch (PLAYER(prepared_game, 2).type) {
537 case PLAYER_TYPE_HUMAN:
538 PLAYER(prepared_game, 2).type = PLAYER_TYPE_AI;
539 btn_player2.text = "Computer (AI)";
540 break;
541 case PLAYER_TYPE_AI:
542 PLAYER(prepared_game, 2).type = PLAYER_TYPE_HUMAN;
543 btn_player2.text = "Keyboard (O-L-K)";
544 break;
546 break;
549 mouse_pressed = false;
551 i++;
552 #ifdef ENABLE_FPS_LIMIT
553 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
554 SDL_Delay(10);
556 frames++;
557 #endif
560 uninit_graphics();
561 uninit_joystick();
563 SDL_Quit();
564 return 0;