5 * Copyright (C) 2003, 2007, 2008, 2009 Thomas Perl <thp@thpinfo.com>
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,
41 #include "locations.h"
47 /* IDs from the resource file */
48 #define START_BUTTON 1
49 #define CHECKBOX_FULLSCREEN 2
52 BOOL CALLBACK
ConfigDialogProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
54 static int checkbox_is_checked
;
58 EndDialog(hwndDlg
, IDCANCEL
);
63 EndDialog(hwndDlg
, (checkbox_is_checked
)?(IDYES
):(IDNO
));
66 EndDialog(hwndDlg
, IDCANCEL
);
68 case CHECKBOX_FULLSCREEN
:
69 checkbox_is_checked
^= 1;
81 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
82 LPSTR lpCmdLine
, int nCmdShow
) {
84 int main( int argc
, char** argv
) {
86 int i
, slide
, slide_direction
;
90 unsigned int worldmap_xpos
, worldmap_ypos
;
94 int sdl_flags
= SDL_SWSURFACE
;
95 int btn_hovering
= 0, btn_hovering_old
= 0;
97 bool mouse_pressed
= false;
99 bool benchmark
= false;
100 unsigned int night_start
, night_end
;
101 GameState
*current_game
= NULL
, *prepared_game
= NULL
;
102 InputDevice
* input_devices
;
103 unsigned int input_device_count
;
105 MenuButton btn_back
= {
108 HEIGHT
-(MENU_OPTIONS_BORDER
+MENU_OPTIONS_BUTTON_HEIGHT
),
109 MENU_OPTIONS_BUTTON_WIDTH
, MENU_OPTIONS_BUTTON_HEIGHT
,
112 MenuButton btn_start
= {
114 WIDTH
-(MENU_OPTIONS_BORDER
+MENU_OPTIONS_BUTTON_WIDTH
),
115 HEIGHT
-(MENU_OPTIONS_BORDER
+MENU_OPTIONS_BUTTON_HEIGHT
),
116 MENU_OPTIONS_BUTTON_WIDTH
, MENU_OPTIONS_BUTTON_HEIGHT
,
119 MenuButton btn_player1
= {
123 300, MENU_OPTIONS_BUTTON_HEIGHT
,
126 MenuButton btn_player2
= {
129 180+MENU_OPTIONS_BORDER
*2+MENU_OPTIONS_BUTTON_HEIGHT
,
130 300, MENU_OPTIONS_BUTTON_HEIGHT
,
134 int current_location
= -1;
135 int highlight_location
= -1;
136 float highlight_location_distance
= 0.0;
137 float new_location_distance
= 0.0;
138 bool location_info_visible
= false;
139 unsigned int location_info_xpos
= 0, location_info_ypos
= 0;
141 const SDL_VideoInfo
* vi
= NULL
;
143 bool do_help
= false;
145 int state
= MENU_STATE_STARTED
;
147 #ifdef ENABLE_FPS_LIMIT
148 Uint32 ft
, frames
; /* frame timer and frames */
152 sdl_flags
|= SDL_FULLSCREEN
;
157 mb_result
= DialogBox(hInstance
, "CONFIG", 0, (DLGPROC
)ConfigDialogProc
);
161 sdl_flags
|= SDL_FULLSCREEN
;
170 fprintf(stderr
, "Tennix " VERSION
"\n" COPYRIGHT
"\n" URL
"\n\n");
174 /* A poor/lazy man's getopt */
175 #define OPTION_SET(longopt,shortopt) \
176 (strcmp(argv[i], longopt)==0 || strcmp(argv[i], shortopt)==0)
177 #define OPTION_VALUE \
178 ((i+1 < argc)?(argv[i+1]):(NULL))
179 #define OPTION_VALUE_PROCESSED \
181 if (OPTION_SET("--fullscreen", "-f")) {
182 sdl_flags
|= SDL_FULLSCREEN
;
184 else if (OPTION_SET("--help", "-h")) {
187 else if (OPTION_SET("--benchmark", "-b")) {
191 fprintf(stderr
, "Ignoring unknown option: %s\n", argv
[i
]);
196 if (do_help
== true) {
197 fprintf(stderr
, "Usage: %s [--fullscreen|-f] [--help|-h]\n", argv
[0]);
205 srand((unsigned)time(NULL
));
208 if (SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_JOYSTICK
) == -1) {
209 fprintf( stderr
, "Can't init SDL: %s\n", SDL_GetError());
213 vi
= SDL_GetVideoInfo();
214 if( (screen
= SDL_SetVideoMode( WIDTH
, HEIGHT
, vi
->vfmt
->BitsPerPixel
, sdl_flags
)) == NULL
) {
215 fprintf( stderr
, "Can't set video mode: %s\n", SDL_GetError());
219 SDL_WM_SetCaption( "Tennix " VERSION
, "Tennix");
220 SDL_ShowCursor( SDL_DISABLE
);
221 SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY
, 1);
227 input_devices
= find_input_devices(&input_device_count
);
229 #ifdef ENABLE_FPS_LIMIT
235 GameState
* g
= gamestate_new();
236 PLAYER(g
, 1).type
= PLAYER_TYPE_AI
;
237 PLAYER(g
, 2).type
= PLAYER_TYPE_AI
;
238 g
->timelimit
= BENCHMARK_TIMELIMIT
*1000;
239 g
->location
= &(locations
[0]);
245 worldmap_xpos
= (WIDTH
-get_image_width(GR_WORLDMAP
))/2;
246 worldmap_ypos
= (HEIGHT
-get_image_height(GR_WORLDMAP
))/2;
249 /* Sliding initialization */
250 ticks
= SDL_GetTicks();
251 slide
= slide_start
= get_image_width(GR_SIDEBAR
);
254 /* State transitions */
256 case MENU_STATE_STARTED
:
257 state
= MENU_STATE_SLIDE_TO_MAINMENU
;
259 case MENU_STATE_SLIDE_TO_MAINMENU
:
263 slide_direction
= -1;
264 state
= MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS
;
266 case MENU_STATE_SLIDE_TO_MAINMENU_IN_PROGRESS
:
269 state
= MENU_STATE_MAINMENU
;
272 case MENU_STATE_MAINMENU
:
274 prepared_game
= NULL
;
276 case MENU_STATE_SLIDE_TO_LOCATION
:
279 state
= MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS
;
281 case MENU_STATE_SLIDE_TO_LOCATION_IN_PROGRESS
:
282 if (slide
== slide_start
) {
283 state
= MENU_STATE_FADE_TO_LOCATION
;
286 case MENU_STATE_FADE_TO_LOCATION
:
288 state
= MENU_STATE_LOCATION
;
291 /* Draw and store the worldmap with day/night times */
292 show_image(GR_WORLDMAP
, WIDTH
/2-get_image_width(GR_WORLDMAP
)/2, HEIGHT
/2-get_image_height(GR_WORLDMAP
)/2, 255);
293 day_night(get_image_width(GR_WORLDMAP
), &night_start
, &night_end
);
294 if (night_start
> night_end
) {
295 rectangle_alpha(worldmap_xpos
, worldmap_ypos
, night_end
, get_image_height(GR_WORLDMAP
), 0, 0, 0, 150);
296 rectangle_alpha(worldmap_xpos
+night_start
, worldmap_ypos
, get_image_width(GR_WORLDMAP
)-night_start
, 10, 0, 0, 0, 150);
298 rectangle_alpha(worldmap_xpos
+night_start
, worldmap_ypos
, night_end
-night_start
, get_image_height(GR_WORLDMAP
), 0, 0, 0, 150);
301 /* add misc items to screen */
302 show_image(GR_TENNIXLOGO
, WIDTH
-get_image_width(GR_SIDEBAR
)-10, 20, 255);
303 font_draw_string(GR_DKC2_FONT
, "Where do you want to play?", MENU_OPTIONS_BORDER
, MENU_OPTIONS_BORDER
, 0, 0);
307 case MENU_STATE_FADE_TO_OPTIONS
:
311 state
= MENU_STATE_OPTIONS
;
313 case MENU_STATE_LOCATION
:
314 case MENU_STATE_OPTIONS
:
315 /* Prepare a new game */
316 if (prepared_game
== NULL
) {
317 prepared_game
= gamestate_new();
318 prepared_game
->location
= NULL
;
319 btn_player1
.text
= "computer (AI)";
320 btn_player2
.text
= "computer (AI)";
321 location_info_visible
= false;
322 current_location
= -1;
323 highlight_location
= -1;
326 case MENU_STATE_SLIDE_TO_GAME
:
329 state = MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS;
331 case MENU_STATE_SLIDE_TO_GAME_IN_PROGRESS:
332 if (slide == slide_start) {
333 state = MENU_STATE_GAME;
335 state
= MENU_STATE_GAME
;
337 case MENU_STATE_SLIDE_TO_RESUME
:
340 state
= MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS
;
342 case MENU_STATE_SLIDE_TO_RESUME_IN_PROGRESS
:
343 if (slide
== slide_start
) {
344 state
= MENU_STATE_RESUME
;
347 case MENU_STATE_GAME
:
348 if (prepared_game
== NULL
) {
349 fprintf(stderr
, "Game not yet prepared!\n");
352 /* Set the day/night status */
353 if (night_start
< night_end
) {
354 prepared_game
->night
= (prepared_game
->location
->worldmap_x
> night_start
&& prepared_game
->location
->worldmap_x
< night_end
);
356 prepared_game
->night
= (prepared_game
->location
->worldmap_x
< night_end
|| prepared_game
->location
->worldmap_x
> night_start
);
359 /* Cancel a possibly started game */
361 current_game
= prepared_game
;
362 prepared_game
= NULL
;
363 /* no break - we are continuing with "resume" */
364 case MENU_STATE_RESUME
:
365 if (current_game
== NULL
) {
366 fprintf(stderr
, "Cannot resume game!\n");
370 gameloop(current_game
);
372 while(SDL_PollEvent(&e
));
373 #ifdef ENABLE_FPS_LIMIT
378 state
= MENU_STATE_SLIDE_TO_MAINMENU
;
380 case MENU_STATE_SLIDE_TO_QUIT
:
383 state
= MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS
;
385 case MENU_STATE_SLIDE_TO_QUIT_IN_PROGRESS
:
386 if (slide
== slide_start
) {
387 state
= MENU_STATE_QUIT
;
390 case MENU_STATE_QUIT
:
394 fprintf(stderr
, "State error: %d\n", state
);
399 if (SDL_GetTicks() > ticks
+ 20) {
400 if (slide
>= 1 && slide
<= slide_start
) {
401 slide
+= slide_direction
+(slide_direction
*slide
/(sqrt(2*slide
)));
402 slide
= MAX(0, MIN(slide_start
, slide
));
403 } else if (slide_direction
!= 0) {
406 ticks
= SDL_GetTicks();
411 if (state
!= MENU_STATE_OPTIONS
) {
412 fill_image_offset(GR_FOG
, 0, 0, WIDTH
, HEIGHT
, -i
, 0);
415 show_image(GR_SIDEBAR
, WIDTH
-get_image_width(GR_SIDEBAR
)+slide
, 0, 255);
416 show_image(GR_TENNIXLOGO
, WIDTH
-get_image_width(GR_SIDEBAR
)-10, 20-slide
, 255);
417 if (state
!= MENU_STATE_OPTIONS
&& state
!= MENU_STATE_LOCATION
) {
419 show_image(GR_BTN_PLAY
, WIDTH
-get_image_width(GR_BTN_PLAY
)+slide
+(slide
/7)+3-(3*(btn_hovering
==MENU_START
)), 150, 255);
420 if (current_game
!= NULL
) {
421 show_image(GR_BTN_RESUME
, WIDTH
-get_image_width(GR_BTN_RESUME
)+slide
+(slide
/7)+3-(3*(btn_hovering
==MENU_RESUME
)), 230, 255);
422 font_draw_string(GR_DKC2_FONT
, "current match:", 10, 10, 0, 0);
423 font_draw_string(GR_DKC2_FONT
, current_game
->sets_score_str
, 10, 40, 0, 0);
425 font_draw_string(GR_DKC2_FONT
, "Tennix " VERSION
, 10, 10, 0, 0);
427 font_draw_string(GR_DKC2_FONT
, URL
, 10, HEIGHT
-10-get_image_height(GR_DKC2_FONT
), 0, 0);
428 show_image(GR_BTN_QUIT
, WIDTH
-get_image_width(GR_BTN_QUIT
)+slide
+(slide
/7)+3-(3*(btn_hovering
==MENU_QUIT
)), 350, 255);
429 } else if (state
== MENU_STATE_OPTIONS
) {
431 show_image(GR_TENNIXLOGO
, WIDTH
-get_image_width(GR_SIDEBAR
)-10, 20, 255);
432 draw_button_object(btn_back
, mx
, my
);
433 draw_button_object(btn_start
, mx
, my
);
434 draw_button_object(btn_player1
, mx
, my
);
435 draw_button_object(btn_player2
, mx
, my
);
436 font_draw_string(GR_DKC2_FONT
, "Player 1", btn_player1
.x
, btn_player1
.y
-MENU_OPTIONS_BORDER
, 0, 0);
437 font_draw_string(GR_DKC2_FONT
, "Player 2", btn_player2
.x
, btn_player2
.y
-MENU_OPTIONS_BORDER
, 0, 0);
438 } else if (state
== MENU_STATE_LOCATION
) {
439 /* Location selection screen */
440 for (x
=0; x
<location_count(); x
++) {
441 new_location_distance
= SQUARE_DISTANCE(mx
-worldmap_xpos
-locations
[x
].worldmap_x
,
442 my
-worldmap_ypos
-locations
[x
].worldmap_y
);
443 if (highlight_location
== -1) {
444 if (new_location_distance
< 20*20) {
445 highlight_location
= x
;
448 highlight_location_distance
= SQUARE_DISTANCE(mx
-worldmap_xpos
-locations
[highlight_location
].worldmap_x
,
449 my
-worldmap_ypos
-locations
[highlight_location
].worldmap_y
);
450 if (highlight_location_distance
> 20*20) {
451 highlight_location
= -1;
453 if (highlight_location_distance
> new_location_distance
&& new_location_distance
< 20*20) {
454 highlight_location
= x
;
458 if (!location_info_visible
) {
459 for (x
=0; x
<location_count(); x
++) {
460 /* draw rectangle for location at "x"*/
461 if (highlight_location
!= -1 && (unsigned int)highlight_location
== x
) {
462 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));
465 rectangle(worldmap_xpos
+ locations
[x
].worldmap_x
-2, worldmap_ypos
+ locations
[x
].worldmap_y
-2, 5, 5, 255, 255*(current_location
!= -1 && x
==(unsigned int)current_location
), 0);
468 if (prepared_game
!= NULL
) {
469 if (location_info_visible
) {
470 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);
471 font_draw_string(GR_DKC2_FONT
, prepared_game
->location
->name
, location_info_xpos
+MENU_OPTIONS_BORDER
, location_info_ypos
+MENU_OPTIONS_BORDER
+200, 0, 0);
472 font_draw_string(GR_DKC2_FONT
, prepared_game
->location
->area
, location_info_xpos
+MENU_OPTIONS_BORDER
, location_info_ypos
+MENU_OPTIONS_BORDER
+200+get_image_height(GR_DKC2_FONT
), 0, 0);
473 font_draw_string(GR_DKC2_FONT
, prepared_game
->location
->city
, location_info_xpos
+MENU_OPTIONS_BORDER
, location_info_ypos
+MENU_OPTIONS_BORDER
+200+2*get_image_height(GR_DKC2_FONT
), 0, 0);
474 font_draw_string(GR_DKC2_FONT
, prepared_game
->location
->court_type_name
, location_info_xpos
+MENU_OPTIONS_BORDER
, location_info_ypos
+MENU_OPTIONS_BORDER
+200+4*get_image_height(GR_DKC2_FONT
), 0, 0);
476 if (prepared_game
->location
!= NULL
) {
477 draw_button_object(btn_start
, mx
, my
);
480 draw_button_object(btn_back
, mx
, my
);
484 if( e
.type
== SDL_QUIT
) {
485 state
= MENU_STATE_SLIDE_TO_QUIT
;
489 keys
= SDL_GetKeyState( NULL
);
490 mb
= SDL_GetMouseState( &mx
, &my
);
492 btn_hovering_old
= btn_hovering
;
493 if (state
== MENU_STATE_MAINMENU
) {
494 btn_hovering
= M_POS_DECODE(mx
, my
);
495 if (current_game
== NULL
) {
496 btn_hovering
&= ~MENU_RESUME
;
498 } else if (state
== MENU_STATE_LOCATION
) {
499 if (M_POS_BUTTON(btn_back
, mx
, my
)) {
500 btn_hovering
= MENU_QUIT
;
501 } else if (M_POS_BUTTON(btn_start
, mx
, my
)) {
502 btn_hovering
= MENU_START
;
506 } else if (state
== MENU_STATE_OPTIONS
) {
507 if (M_POS_BUTTON(btn_back
, mx
, my
)) {
508 btn_hovering
= MENU_QUIT
;
509 } else if (M_POS_BUTTON(btn_start
, mx
, my
)) {
510 btn_hovering
= MENU_START
;
511 } else if (M_POS_BUTTON(btn_player1
, mx
, my
)) {
512 btn_hovering
= MENU_PLAYER1
;
513 } else if (M_POS_BUTTON(btn_player2
, mx
, my
)) {
514 btn_hovering
= MENU_PLAYER2
;
519 /* No menu screen - no hovering. */
522 #ifndef MAEMO /* On Maemo, we cannot really "hover" (touchscreen!) */
523 if (btn_hovering_old
!= btn_hovering
&& btn_hovering
!= 0) {
524 #ifdef HAVE_VOICE_FILES
525 if (btn_hovering
== MENU_QUIT
) {
526 play_sample(VOICE_QUIT_IT
);
527 } else if (btn_hovering
== MENU_START
) {
528 play_sample(VOICE_NEW_GAME
);
530 play_sample(SOUND_MOUSEOVER
);
533 play_sample(SOUND_MOUSEOVER
);
538 if( keys
[SDLK_ESCAPE
] || keys
['q']) {
539 /* FIXME: do the state thingie! */
544 SDL_WM_ToggleFullScreen( screen
);
547 #ifndef MAEMO /* No mouse cursor on Maemo (we have a touchscreen) */
548 if (state
== MENU_STATE_MAINMENU
|| state
== MENU_STATE_OPTIONS
|| state
== MENU_STATE_LOCATION
) {
549 show_image(GR_CURSOR
, mx
-2, my
-1, 255);
553 /* Draw the "real" mouse coordinates */
554 /*rectangle(mx-1, my-1, 2, 2, 255, 255, 255);*/
556 /* Store the screen, because we are fading after this screen update */
557 /*if (!(mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && btn_hovering != MENU_NONE && mouse_pressed == true) store_screen();*/
561 if( mb
& SDL_BUTTON(SDL_BUTTON_LEFT
)) {
562 mouse_pressed
= true;
563 } else if (mouse_pressed
== true) {
564 /* Mouse button released */
565 if (state
== MENU_STATE_MAINMENU
|| state
== MENU_STATE_OPTIONS
|| state
== MENU_STATE_LOCATION
) {
566 #ifdef HAVE_VOICE_FILES
567 if (btn_hovering
== MENU_START
) {
568 play_sample(VOICE_LETS_GO
);
570 play_sample(SOUND_MOUSECLICK
);
573 play_sample(SOUND_MOUSECLICK
);
576 if (state
== MENU_STATE_MAINMENU
) {
577 switch (btn_hovering
) {
579 state
= MENU_STATE_SLIDE_TO_LOCATION
;
582 state
= MENU_STATE_SLIDE_TO_RESUME
;
585 state
= MENU_STATE_SLIDE_TO_QUIT
;
588 } else if (state
== MENU_STATE_LOCATION
) {
589 switch (btn_hovering
) {
591 if (prepared_game
->location
!= NULL
) {
592 state
= MENU_STATE_FADE_TO_OPTIONS
;
596 state
= MENU_STATE_SLIDE_TO_MAINMENU
;
599 if (!location_info_visible
&& highlight_location
!= -1) {
600 current_location
= highlight_location
;
601 prepared_game
->location
= &(locations
[current_location
]);
602 location_info_xpos
= MAX(0, MIN(WIDTH
-320-50, mx
-320/2));
603 location_info_ypos
= MAX(0, MIN(HEIGHT
-200-160, my
-200/2));
604 location_info_visible
= true;
606 location_info_visible
= false;
607 current_location
= -1;
608 highlight_location
= -1;
609 prepared_game
->location
= NULL
;
613 } else if (state
== MENU_STATE_OPTIONS
) {
614 switch (btn_hovering
) {
616 state
= MENU_STATE_SLIDE_TO_GAME
;
619 state
= MENU_STATE_FADE_TO_LOCATION
;
622 /* advance the input device index */
623 PLAYER(prepared_game
, 1).input_device_index
++;
624 if (PLAYER(prepared_game
, 1).input_device_index
== (signed int)input_device_count
) {
625 PLAYER(prepared_game
, 1).input_device_index
= -1;
628 /* determine the selected input device */
629 if (PLAYER(prepared_game
, 1).input_device_index
== -1) {
630 PLAYER(prepared_game
, 1).type
= PLAYER_TYPE_AI
;
631 PLAYER(prepared_game
, 1).input
= NULL
;
632 btn_player1
.text
= "Computer (AI)";
634 PLAYER(prepared_game
, 1).type
= PLAYER_TYPE_HUMAN
;
635 PLAYER(prepared_game
, 1).input
= &(input_devices
[PLAYER(prepared_game
, 1).input_device_index
]);
636 btn_player1
.text
= input_device_get_name(PLAYER(prepared_game
, 1).input
);
640 /* advance the input device index */
641 PLAYER(prepared_game
, 2).input_device_index
++;
642 if (PLAYER(prepared_game
, 2).input_device_index
== (signed int)input_device_count
) {
643 PLAYER(prepared_game
, 2).input_device_index
= -1;
646 /* determine the selected input device */
647 if (PLAYER(prepared_game
, 2).input_device_index
== -1) {
648 PLAYER(prepared_game
, 2).type
= PLAYER_TYPE_AI
;
649 PLAYER(prepared_game
, 2).input
= NULL
;
650 btn_player2
.text
= "Computer (AI)";
652 PLAYER(prepared_game
, 2).type
= PLAYER_TYPE_HUMAN
;
653 PLAYER(prepared_game
, 2).input
= &(input_devices
[PLAYER(prepared_game
, 2).input_device_index
]);
654 btn_player2
.text
= input_device_get_name(PLAYER(prepared_game
, 2).input
);
659 mouse_pressed
= false;
662 #ifdef ENABLE_FPS_LIMIT
663 while (frames
*1000.0/((float)(SDL_GetTicks()-ft
+1))>(float)(DEFAULT_FPS
)) {