Voice support, experimental wind, extended referee
[tennix.git] / tennix.c
blobc84e412b99f500eef992974529ae2ae7813fca23
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>
28 #ifdef WIN32
29 #include <windows.h>
30 #endif
32 #include "tennix.h"
33 #include "game.h"
34 #include "graphics.h"
35 #include "sound.h"
36 #include "input.h"
38 SDL_Surface *screen;
40 #ifdef WIN32
42 /* IDs from the resource file */
43 #define START_BUTTON 1
44 #define CHECKBOX_FULLSCREEN 2
45 #define QUIT_BUTTON 3
47 BOOL CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
49 static int checkbox_is_checked;
51 switch (uMsg) {
52 case WM_CLOSE:
53 EndDialog(hwndDlg, IDCANCEL);
54 break;
55 case WM_COMMAND:
56 switch (wParam) {
57 case START_BUTTON:
58 EndDialog(hwndDlg, (checkbox_is_checked)?(IDYES):(IDNO));
59 break;
60 case QUIT_BUTTON:
61 EndDialog(hwndDlg, IDCANCEL);
62 break;
63 case CHECKBOX_FULLSCREEN:
64 checkbox_is_checked ^= 1;
65 break;
67 break;
68 default:
69 return FALSE;
71 return TRUE;
73 #endif
75 #ifdef WIN32
76 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
77 LPSTR lpCmdLine, int nCmdShow) {
78 #else
79 int main( int argc, char** argv) {
80 #endif
81 int i, slide, slide_direction;
82 int ticks;
83 int mx, my;
84 Uint8 *keys;
85 Uint8 mb;
86 SDL_Event e;
87 int sdl_flags = SDL_SWSURFACE;
88 int btn_hovering = 0, btn_hovering_old = 0;
89 int slide_start;
90 bool mouse_pressed = false;
91 bool quit = false;
92 GameState *current_game = NULL, *prepared_game = NULL;
94 MenuButton btn_back = {
95 "Back to main menu",
96 MENU_OPTIONS_BORDER,
97 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
98 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
99 255, 0, 0
101 MenuButton btn_start = {
102 "Start new game",
103 WIDTH-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_WIDTH),
104 HEIGHT-(MENU_OPTIONS_BORDER+MENU_OPTIONS_BUTTON_HEIGHT),
105 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
106 0, 255, 0
108 MenuButton btn_court_change = {
109 "change",
110 240,
112 50, MENU_OPTIONS_BUTTON_HEIGHT,
113 100, 100, 100
115 MenuButton btn_player1 = {
116 NULL,
117 380,
118 180,
119 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
120 50, 50, 255
122 MenuButton btn_player2 = {
123 NULL,
124 380,
125 180+MENU_OPTIONS_BORDER*2+MENU_OPTIONS_BUTTON_HEIGHT,
126 MENU_OPTIONS_BUTTON_WIDTH, MENU_OPTIONS_BUTTON_HEIGHT,
127 255, 50, 50
130 int state = MENU_STATE_STARTED;
132 #ifdef ENABLE_FPS_LIMIT
133 Uint32 ft, frames; /* frame timer and frames */
134 #endif
136 #ifdef MAEMO
137 sdl_flags |= SDL_FULLSCREEN;
138 #endif
140 #ifdef WIN32
141 int mb_result;
142 mb_result = DialogBox(hInstance, "CONFIG", 0, (DLGPROC)ConfigDialogProc);
144 switch (mb_result) {
145 case IDYES:
146 sdl_flags |= SDL_FULLSCREEN;
147 break;
148 case IDCANCEL:
149 return 0;
150 break;
151 default:
152 break;
154 #else
155 fprintf( stderr, "Tennix %s\n%s\n%s\n\n", VERSION, COPYRIGHT, URL);
157 bool do_help = false;
158 i = 1;
159 while (i < argc) {
160 /* A poor/lazy man's getopt */
161 #define OPTION_SET(longopt,shortopt) \
162 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
163 #define OPTION_VALUE \
164 ((i+1 < argc)?(argv[i+1]):(NULL))
165 #define OPTION_VALUE_PROCESSED \
166 (i++)
167 if (OPTION_SET("--fullscreen", "-f")) {
168 sdl_flags |= SDL_FULLSCREEN;
170 else if (OPTION_SET("--help", "-h")) {
171 do_help = true;
173 else if (OPTION_SET("--list-joysticks", "-J")) {
174 SDL_Init(SDL_INIT_JOYSTICK);
175 joystick_list();
176 return 0;
178 else if (OPTION_SET("--joystick", "-j")) {
179 SDL_Init(SDL_INIT_JOYSTICK);
180 if (OPTION_VALUE == NULL) {
181 fprintf(stderr, "Error: You need to specify the name of the joystick as parameter.\n");
182 do_help = true;
183 break;
185 if (joystick_open(OPTION_VALUE)==0) {
186 fprintf(stderr, "Warning: Cannot find joystick \"%s\" - Ignored.\n", OPTION_VALUE);
187 break;
189 OPTION_VALUE_PROCESSED;
191 else {
192 fprintf(stderr, "Ignoring unknown option: %s\n", argv[i]);
194 i++;
197 if (do_help == true) {
198 fprintf(stderr, "Usage: %s [--fullscreen|-f] [--help|-h] [--list-joysticks|-J] [--joystick|-j] [joystick name]\n", argv[0]);
199 return 0;
201 #endif
203 srand( (unsigned)time( NULL));
205 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) {
206 fprintf( stderr, "Can't init SDL: %s\n", SDL_GetError());
207 exit( 1);
210 SDL_VideoInfo* vi = (SDL_VideoInfo*)SDL_GetVideoInfo();
211 if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) {
212 fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError());
213 exit( 1);
216 SDL_WM_SetCaption( "Tennix " VERSION, "Tennix");
217 SDL_ShowCursor( SDL_DISABLE);
218 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1);
220 init_sound();
221 init_graphics();
222 init_joystick();
224 #ifdef ENABLE_FPS_LIMIT
225 frames = 0;
226 ft = SDL_GetTicks();
227 #endif
229 i = 0;
230 /* Sliding initialization */
231 ticks = SDL_GetTicks();
232 slide = slide_start = get_image_width(GR_SIDEBAR);
233 slide_direction = 0;
234 while(!quit) {
235 /* State transitions */
236 switch (state) {
237 case MENU_STATE_STARTED:
238 state = MENU_STATE_SLIDE_TO_MAINMENU;
239 break;
240 case MENU_STATE_SLIDE_TO_MAINMENU:
241 slide = slide_start;
242 slide_direction = -1;
243 state = MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS;
244 break;
245 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS:
246 if (slide == 0) {
247 slide_direction = 0;
248 state = MENU_STATE_MAINMENU;
250 break;
251 case MENU_STATE_MAINMENU:
252 free(prepared_game);
253 prepared_game = NULL;
254 break;
255 case MENU_STATE_SLIDE_TO_OPTIONS:
256 slide = 1;
257 slide_direction = 3;
258 state = MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS;
259 break;
260 case MENU_STATE_SLIDE_TO_OPTIONS_IN_PROGRESS:
261 if (slide == slide_start) {
262 start_fade();
263 state = MENU_STATE_OPTIONS;
265 break;
266 case MENU_STATE_OPTIONS:
267 /* Prepare a new game */
268 if (prepared_game == NULL) {
269 prepared_game = gamestate_new();
270 btn_player1.text = "Keyboard (W-S-D)";
271 btn_player2.text = "Computer (AI)";
273 break;
274 case MENU_STATE_SLIDE_TO_GAME:
275 /*slide = 1;
276 slide_direction = 2;
277 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
278 break;
279 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
280 if (slide == slide_start) {
281 state = MENU_STATE_GAME;
283 state = MENU_STATE_GAME;
284 break;
285 case MENU_STATE_SLIDE_TO_RESUME:
286 slide = 1;
287 slide_direction = 2;
288 state = MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS;
289 break;
290 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS:
291 if (slide == slide_start) {
292 state = MENU_STATE_RESUME;
294 break;
295 case MENU_STATE_GAME:
296 if (prepared_game == NULL) {
297 fprintf(stderr, "Game not yet prepared!\n");
298 exit(EXIT_FAILURE);
300 /* Cancel a possibly started game */
301 free(current_game);
302 current_game = prepared_game;
303 prepared_game = NULL;
304 /* no break - we are continuing with "resume" */
305 case MENU_STATE_RESUME:
306 if (current_game == NULL) {
307 fprintf(stderr, "Cannot resume game!\n");
308 exit(EXIT_FAILURE);
310 start_fade();
311 gameloop(current_game);
312 SDL_Delay(150);
313 while(SDL_PollEvent(&e));
314 #ifdef ENABLE_FPS_LIMIT
315 frames = 0;
316 ft = SDL_GetTicks();
317 #endif
318 start_fade();
319 state = MENU_STATE_SLIDE_TO_MAINMENU;
320 break;
321 case MENU_STATE_SLIDE_TO_QUIT:
322 slide = 1;
323 slide_direction = 3;
324 state = MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS;
325 break;
326 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS:
327 if (slide == slide_start) {
328 state = MENU_STATE_QUIT;
330 break;
331 case MENU_STATE_QUIT:
332 quit = true;
333 break;
334 default:
335 fprintf(stderr, "State error: %d\n", state);
336 exit(EXIT_FAILURE);
339 /* Sliding */
340 if (SDL_GetTicks() > ticks + 20) {
341 if (slide >= 1 && slide <= slide_start) {
342 slide += slide_direction+(slide_direction*slide/(sqrt(2*slide)));
343 slide = MAX(0, MIN(slide_start, slide));
344 } else if (slide_direction != 0) {
345 slide_direction = 0;
347 ticks = SDL_GetTicks();
350 /* Graphics */
351 #ifdef DEBUG
352 if (state != MENU_STATE_OPTIONS) {
353 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -i, 0);
355 #endif
356 show_image(GR_SIDEBAR, WIDTH-get_image_width(GR_SIDEBAR)+slide, 0, 255);
357 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20-slide, 255);
358 if (state != MENU_STATE_OPTIONS) {
359 /* Main Menu */
360 show_image(GR_BTN_PLAY, WIDTH-get_image_width(GR_BTN_PLAY)+slide+(slide/7)+3-(3*(btn_hovering==MENU_START)), 150, 255);
361 if (current_game != NULL) {
362 show_image(GR_BTN_RESUME, WIDTH-get_image_width(GR_BTN_RESUME)+slide+(slide/7)+3-(3*(btn_hovering==MENU_RESUME)), 230, 255);
363 font_draw_string(GR_DKC2_FONT, "current match:", 10, 10, 0, 0);
364 font_draw_string(GR_DKC2_FONT, current_game->sets_score_str, 10, 40, 0, 0);
366 show_image(GR_BTN_QUIT, WIDTH-get_image_width(GR_BTN_QUIT)+slide+(slide/7)+3-(3*(btn_hovering==MENU_QUIT)), 350, 255);
367 } else {
368 /* Options screen */
369 show_image(GR_TENNIXLOGO, WIDTH-get_image_width(GR_SIDEBAR)-10, 20, 255);
370 draw_button_object(btn_back, mx, my);
371 draw_button_object(btn_start, mx, my);
372 if (prepared_game != NULL) {
373 fill_image(prepared_game->court_type, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, get_image_width(GR_STADIUM), get_image_height(GR_STADIUM));
374 show_image(GR_STADIUM, MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER*2, 255);
375 draw_button_object(btn_court_change, mx, my);
376 font_draw_string(GR_DKC2_FONT, "Location", MENU_OPTIONS_BORDER, MENU_OPTIONS_BORDER, 0, 0);
377 draw_button_object(btn_player1, mx, my);
378 draw_button_object(btn_player2, mx, my);
379 font_draw_string(GR_DKC2_FONT, "Player 1", btn_player1.x, btn_player1.y-MENU_OPTIONS_BORDER, 0, 0);
380 font_draw_string(GR_DKC2_FONT, "Player 2", btn_player2.x, btn_player2.y-MENU_OPTIONS_BORDER, 0, 0);
384 SDL_PollEvent( &e);
385 if( e.type == SDL_QUIT) {
386 state = MENU_STATE_SLIDE_TO_QUIT;
387 /*break;*/
390 keys = SDL_GetKeyState( NULL);
391 mb = SDL_GetMouseState( &mx, &my);
393 btn_hovering_old = btn_hovering;
394 if (state == MENU_STATE_MAINMENU) {
395 btn_hovering = M_POS_DECODE(mx, my);
396 if (current_game == NULL) {
397 btn_hovering &= ~MENU_RESUME;
399 } else if (state == MENU_STATE_OPTIONS) {
400 if (M_POS_BUTTON(btn_back, mx, my)) {
401 btn_hovering = MENU_QUIT;
402 } else if (M_POS_BUTTON(btn_start, mx, my)) {
403 btn_hovering = MENU_START;
404 } else if (M_POS_BUTTON(btn_court_change, mx, my)) {
405 btn_hovering = MENU_COURT_CHANGE;
406 } else if (M_POS_BUTTON(btn_player1, mx, my)) {
407 btn_hovering = MENU_PLAYER1;
408 } else if (M_POS_BUTTON(btn_player2, mx, my)) {
409 btn_hovering = MENU_PLAYER2;
410 } else {
411 btn_hovering = 0;
413 } else {
414 /* No menu screen - no hovering. */
415 btn_hovering = 0;
417 if (btn_hovering_old != btn_hovering && btn_hovering != 0) {
418 #ifdef HAVE_VOICE_FILES
419 if (btn_hovering == MENU_QUIT) {
420 play_sample(VOICE_QUIT_IT);
421 } else if (btn_hovering == MENU_START) {
422 play_sample(VOICE_NEW_GAME);
423 } else {
424 play_sample(SOUND_MOUSEOVER);
426 #else
427 play_sample(SOUND_MOUSEOVER);
428 #endif
431 if( keys[SDLK_ESCAPE] || keys['q']) {
432 /* FIXME: do the state thingie! */
433 break;
436 if( keys['f']) {
437 SDL_WM_ToggleFullScreen( screen);
440 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
441 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
442 show_sprite( GR_RACKET, ((mb&SDL_BUTTON( SDL_BUTTON_LEFT))>0)+(((mb&SDL_BUTTON( SDL_BUTTON_RIGHT))>0)*2), 4, mx, my, 255);
444 #endif
446 /* Draw the "real" mouse coordinates */
447 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
449 /* Store the screen, because we are fading after this screen update */
450 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
452 updatescr();
454 if( mb & SDL_BUTTON(SDL_BUTTON_LEFT)) {
455 mouse_pressed = true;
456 } else if (mouse_pressed == true) {
457 /* Mouse button released */
458 if (state == MENU_STATE_MAINMENU || state == MENU_STATE_OPTIONS) {
459 #ifdef HAVE_VOICE_FILES
460 if (btn_hovering == MENU_START) {
461 play_sample(VOICE_LETS_GO);
462 } else {
463 play_sample(SOUND_MOUSECLICK);
465 #else
466 play_sample(SOUND_MOUSECLICK);
467 #endif
469 if (state == MENU_STATE_MAINMENU) {
470 switch (btn_hovering) {
471 case MENU_START:
472 state = MENU_STATE_SLIDE_TO_OPTIONS;
473 break;
474 case MENU_RESUME:
475 state = MENU_STATE_SLIDE_TO_RESUME;
476 break;
477 case MENU_QUIT:
478 state = MENU_STATE_SLIDE_TO_QUIT;
479 break;
481 } else if (state == MENU_STATE_OPTIONS) {
482 switch (btn_hovering) {
483 case MENU_START:
484 state = MENU_STATE_SLIDE_TO_GAME;
485 break;
486 case MENU_QUIT:
487 state = MENU_STATE_SLIDE_TO_MAINMENU;
488 break;
489 case MENU_COURT_CHANGE:
490 prepared_game->court_type++;
491 if (prepared_game->court_type > GR_CTT_LAST) {
492 prepared_game->court_type = GR_CTT_FIRST;
494 break;
495 case MENU_PLAYER1:
496 switch (prepared_game->player1.type) {
497 case PLAYER_TYPE_HUMAN:
498 prepared_game->player1.type = PLAYER_TYPE_AI;
499 btn_player1.text = "Computer (AI)";
500 break;
501 case PLAYER_TYPE_AI:
502 prepared_game->player1.type = PLAYER_TYPE_HUMAN;
503 btn_player1.text = "Keyboard (W-S-D)";
504 break;
506 break;
507 case MENU_PLAYER2:
508 switch (prepared_game->player2.type) {
509 case PLAYER_TYPE_HUMAN:
510 prepared_game->player2.type = PLAYER_TYPE_AI;
511 btn_player2.text = "Computer (AI)";
512 break;
513 case PLAYER_TYPE_AI:
514 prepared_game->player2.type = PLAYER_TYPE_HUMAN;
515 btn_player2.text = "Keyboard (O-L-K)";
516 break;
518 break;
521 mouse_pressed = false;
523 i++;
524 #ifdef ENABLE_FPS_LIMIT
525 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
526 SDL_Delay(10);
528 frames++;
529 #endif
532 uninit_graphics();
533 uninit_joystick();
535 SDL_Quit();
536 return 0;