Add compiler flags for Maemo
[tennix.git] / game.c
blob50abd4138f5352e0c3b18152bd54b6e7cb89262b
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 <math.h>
27 #include "tennix.h"
28 #include "game.h"
29 #include "graphics.h"
30 #include "input.h"
31 #include "sound.h"
34 GameState *gamestate_new() {
35 int x, y, z;
36 GameState *s;
38 GameState template = {
39 { 0, 0, 0.0, 0.0, 0.0 },
40 { 0, 0, 0, 0, 0 },
41 { GAME_X_MIN-RACKET_X_MID*2, GAME_Y_MID, 0, 0, 1, DESIRE_NORMAL, PLAYER_TYPE_HUMAN, GAME_Y_MID, false, 0, {0}, 0, 0, PLAYER_ACCEL_DEFAULT, true },
42 { GAME_X_MAX+RACKET_X_MID*2, GAME_Y_MID, 0, 0, 0, DESIRE_NORMAL, PLAYER_TYPE_AI, GAME_Y_MID, false, 0, {0}, 0, 0, PLAYER_ACCEL_DEFAULT, true },
46 true,
47 "welcome to tennix " VERSION,
48 { 0 },
49 { 0 },
50 REFEREE_NORMAL,
52 WINNER_NONE,
53 false,
54 GR_CTT_GRASS,
55 -1,
56 { 0 },
58 false,
59 { { { 0 } } },
60 0.0,
61 SOUND_MAX,
62 0.0,
63 0.0,
67 false,
71 false,
72 REFEREE_COUNT
75 s = (GameState*)malloc(sizeof(GameState));
76 if (s == NULL) abort();
78 memcpy(s, &template, sizeof(GameState));
80 game_setup_serve(s);
82 /* smoothen n-gram */
83 for( x = 0; x<NGRAM_STEPS; x++) {
84 for( y = 0; y<NGRAM_STEPS; y++) {
85 for( z = 0; z<NGRAM_STEPS; z++) {
86 s->ngram[x][y][z] = 1;
91 return s;
95 void gameloop(GameState *s) {
96 strcpy(s->game_score_str, format_game(s));
97 strcpy(s->sets_score_str, format_sets(s));
98 s->text_changed = true;
100 Uint32 ot = SDL_GetTicks();
101 Uint32 nt;
102 Uint32 dt = GAME_TICKS;
103 Uint32 diff;
104 Uint32 accumulator = 0;
105 bool quit = false;
107 #ifdef ENABLE_FPS_LIMIT
108 Uint32 ft, frames; /* frame timer and frames */
109 #endif
111 if (s->rain > 0) {
112 play_sample_background(SOUND_RAIN);
114 play_sample_loop(SOUND_AUDIENCE);
115 /* Reset the court type, so it is redrawn on first display */
116 s->old_court_type = -1;
118 #ifdef ENABLE_FPS_LIMIT
119 frames = 0;
120 ft = SDL_GetTicks();
121 #endif
122 while( !quit) {
123 nt = SDL_GetTicks();
124 diff = nt-ot;
125 if( diff > 2000) {
126 diff = 0;
129 accumulator += diff;
130 ot = nt;
132 while( accumulator >= dt) {
133 quit = step(s);
134 s->time += dt;
135 s->windtime += s->wind*dt;
136 accumulator -= dt;
138 if( s->was_stopped) {
139 ot = SDL_GetTicks();
140 s->was_stopped = false;
143 if (s->timelimit != 0 && s->time >= s->timelimit) {
144 quit = 1;
147 #ifdef ENABLE_FPS_LIMIT
148 while (frames*1000.0/((float)(SDL_GetTicks()-ft+1))>(float)(DEFAULT_FPS)) {
149 SDL_Delay(10);
151 frames++;
152 #endif
154 render(s);
157 clear_screen();
158 store_screen();
160 stop_sample(SOUND_AUDIENCE);
161 stop_sample(SOUND_RAIN);
164 bool step( GameState* s) {
165 Uint8 *keys;
166 SDL_Event e;
168 if( get_phase( s) < 1.0) {
169 if( !s->ground.jump) {
170 s->play_sound = SOUND_GROUND;
172 if( IS_OUT_Y( s->ball.y)) {
173 /* out - responsibilities stay the same */
174 s->status = "out!";
175 s->play_sound = SOUND_OUT;
176 s->referee = REFEREE_OUT;
177 } else {
178 /* not out - responsibilities change */
179 s->player1.responsible = !(s->player2.responsible = !s->player2.responsible);
180 s->status = format_status( s);
181 s->referee = REFEREE_NORMAL;
184 s->ground.jump = 3;
185 s->ground.x = s->ball.x;
186 s->ground.y = s->ball.y;
187 } else {
188 if( s->ground.jump && !(s->time%5)) s->ground.jump--;
191 if( IS_OUT_X(s->ball.x) || IS_OFFSCREEN_Y(s->ball.y)) {
192 if( IS_OFFSCREEN( s->ball.x, s->ball.y)) {
193 s->player1_serves = s->player1.responsible;
195 score_game( s, s->player2.responsible);
196 strcpy( s->game_score_str, format_game( s));
197 strcpy( s->sets_score_str, format_sets( s));
198 s->text_changed = true;
200 if( s->player1.responsible) {
201 if( s->player1.type == PLAYER_TYPE_HUMAN && s->player2.type == PLAYER_TYPE_AI) {
202 s->status = "computer scores";
203 } else {
204 s->status = "player 2 scores";
206 s->referee = REFEREE_PLAYER2;
207 } else {
208 if( s->player1.type == PLAYER_TYPE_HUMAN && s->player2.type == PLAYER_TYPE_AI) {
209 s->status = "player scores";
210 } else {
211 s->status = "player 1 scores";
213 s->referee = REFEREE_PLAYER1;
216 game_setup_serve( s);
217 s->play_sound = SOUND_APPLAUSE;
218 SDL_Delay( 500);
219 s->was_stopped = true;
220 s->history_size = 0;
221 s->history_is_locked = 0;
222 s->ngram_prediction = 0.0;
223 #ifdef DEBUG
224 printf( "-- game reset --\n");
225 #endif
228 if( IS_OUT_X(s->ball.x)) {
229 if( !s->history_is_locked && s->referee != REFEREE_OUT) {
230 s->history[s->history_size] = (int)(NGRAM_STEPS*s->ball.y/HEIGHT);
231 s->history_size++;
232 if( s->history_size == 3) {
233 s->ngram[s->history[0]][s->history[1]][s->history[2]] += 10;
234 #ifdef DEBUG
235 printf( "history: %d, %d, %d\n", s->history[0], s->history[1], s->history[2]);
236 #endif
237 s->ngram_prediction = ngram_predictor( s);
238 s->history[0] = s->history[1];
239 s->history[1] = s->history[2];
240 s->history_size--;
242 s->history_is_locked = true;
244 if( s->ball.move_x <= 0 && IS_NEAR_X( s->player1.x, s->ball.x) && IS_NEAR_Y( s->player1.y, s->ball.y) && s->player1.state && s->referee != REFEREE_OUT) {
245 s->ball.x = GAME_X_MIN;
246 if( s->player1.state == PLAYER_STATE_MAX) {
247 s->ball.move_x = PLAYER_POWERSHOT;
248 } else {
249 s->ball.move_x = 2.5 + 2.0*s->player1.state/PLAYER_STATE_MAX;
251 s->ball.move_y = get_move_y( s, 1);
252 s->player2.responsible = !(s->player1.responsible = 1);
253 s->ball.jump += 1.0-2.0*(s->player1.state<5);
254 s->play_sound = SOUND_RACKET;
255 pan_sample(SOUND_RACKET, 0.4);
256 } else if( s->ball.move_x >= 0 && IS_NEAR_X( s->player2.x, s->ball.x) && IS_NEAR_Y( s->player2.y, s->ball.y) && s->player2.state && s->referee != REFEREE_OUT) {
257 s->ball.x = GAME_X_MAX;
258 if( s->player2.state == PLAYER_STATE_MAX) {
259 s->ball.move_x = -PLAYER_POWERSHOT;
260 } else {
261 s->ball.move_x = -(2.5 + 2.0*s->player2.state/PLAYER_STATE_MAX);
263 s->ball.move_y = get_move_y( s, 2);
264 s->player1.responsible = !(s->player2.responsible = 1);
265 s->ball.jump += 1.0-2.0*(s->player2.state<5);
266 s->play_sound = SOUND_RACKET;
267 pan_sample(SOUND_RACKET, 0.6);
270 } else {
271 s->history_is_locked = false;
274 SDL_PollEvent( &e);
275 keys = SDL_GetKeyState( NULL);
276 switch(e.type) {
277 case SDL_JOYAXISMOTION:
278 if (e.jaxis.axis == JOYSTICK_Y_AXIS) {
279 s->joystick_y = JOYSTICK_PERCENTIZE(e.jaxis.value);
280 } else if (e.jaxis.axis == JOYSTICK_X_AXIS) {
281 s->joystick_x = JOYSTICK_PERCENTIZE(e.jaxis.value);
283 break;
284 case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN:
285 if (e.jbutton.button == JOYSTICK_BUTTON_A) {
286 s->joystick_a = (e.jbutton.state == SDL_PRESSED);
288 break;
291 if( s->time%50==0) {
293 * Maemo keys:
294 * F7 = "Decrease" key
295 * F8 = "Increase" key
297 if (keys['c'] || keys[SDLK_F8]) {
298 s->court_type++;
299 } else if (keys[SDLK_F7]) {
300 s->court_type--;
302 if (keys['r']) {
303 if (s->rain == 0) {
304 play_sample_background(SOUND_RAIN);
306 s->rain += 10;
308 if (keys['t']) {
309 s->fog++;
311 if (keys['1']) {
312 s->wind++;
314 if (keys['2']) {
315 s->wind--;
317 if (keys['n']) {
318 s->night = 1 - s->night;
320 if( s->court_type > GR_CTT_LAST) {
321 s->court_type = GR_CTT_FIRST;
322 } else if (s->court_type < GR_CTT_FIRST) {
323 s->court_type = GR_CTT_LAST;
327 if(!(SDL_GetTicks() < fading_start+FADE_DURATION) && !s->is_over) {
328 if( s->player1.type == PLAYER_TYPE_HUMAN) {
329 input_human( &s->player1,
330 keys['w'] || keys[SDLK_UP] || s->joystick_y < -JOYSTICK_TRESHOLD,
331 keys['s'] || keys[SDLK_DOWN] || s->joystick_y > JOYSTICK_TRESHOLD,
332 keys['d'] || keys[SDLK_SPACE] || keys[SDLK_LCTRL] || keys[SDLK_RETURN] || s->joystick_a,
333 #ifdef ENABLE_MOUSE
334 true,
335 #else
336 false,
337 #endif
339 } else {
340 input_ai( &s->player1, &s->ball, &s->player2, s);
343 if( s->player2.type == PLAYER_TYPE_HUMAN) {
344 input_human( &s->player2, keys['o'], keys['l'], keys['k'], false, s);
345 } else {
346 input_ai( &s->player2, &s->ball, &s->player1, s);
350 /* Maemo: The "F6" button is the "Fullscreen" button */
351 if( keys['f'] || keys[SDLK_F6]) SDL_WM_ToggleFullScreen( screen);
352 if( keys['y']) SDL_SaveBMP( screen, "screenshot.bmp");
354 /* Maemo: The "F4" button is the "Open menu" button */
355 if( keys['p'] || keys[SDLK_F4]) {
356 while( keys['p'] || keys[SDLK_F4]) {
357 SDL_PollEvent( &e);
358 keys = SDL_GetKeyState( NULL);
359 SDL_Delay( 10);
361 while( (keys['p'] || keys[SDLK_F4]) == 0) {
362 SDL_PollEvent( &e);
363 keys = SDL_GetKeyState( NULL);
364 SDL_Delay( 10);
366 while( keys['p'] || keys[SDLK_F4]) {
367 SDL_PollEvent( &e);
368 keys = SDL_GetKeyState( NULL);
369 SDL_Delay( 10);
371 s->was_stopped = true;
374 if( keys[SDLK_ESCAPE] || keys['q']) return true;
376 limit_value( &s->player1.y, PLAYER_Y_MIN, PLAYER_Y_MAX);
377 limit_value( &s->player2.y, PLAYER_Y_MIN, PLAYER_Y_MAX);
378 limit_value( &s->ball.jump, BALL_JUMP_MIN, BALL_JUMP_MAX);
380 /* Update ball_dest for debugging purposes */
381 get_move_y(s, 1);
382 get_move_y(s, 2);
384 if (s->ball.move_x > 0 && s->wind > 0) {
385 s->ball.x += fabsf(s->wind)/2;
386 } else if (s->ball.move_x < 0 && s->wind < 0) {
387 s->ball.x -= fabsf(s->wind)/2;
390 s->ball.x += s->ball.move_x;
391 s->ball.y += s->ball.move_y;
393 if(s->player1.state) s->player1.state--;
394 if(s->player2.state) s->player2.state--;
396 return false;
399 void render( GameState* s) {
400 int i, x, y;
401 #ifdef EXTENDED_REFEREE
402 int t=1000;
403 #endif
404 if (s->play_sound != SOUND_MAX) {
405 play_sample(s->play_sound);
406 s->play_sound = SOUND_MAX;
408 if( s->winner != WINNER_NONE) {
409 if( !s->is_over) {
410 start_fade();
411 s->is_over = true;
413 clear_screen();
414 store_screen();
415 show_sprite( GR_RACKET, 2*(s->winner-1), 4, WIDTH/2 - get_image_width( GR_RACKET)/8, HEIGHT/2 - get_image_height( GR_RACKET), 255);
416 sprintf( s->game_score_str, "player %d wins the match with %s", s->winner, format_sets( s));
417 font_draw_string( GR_DKC2_FONT, s->game_score_str, (WIDTH-font_get_string_width( GR_DKC2_FONT, s->game_score_str))/2, HEIGHT/2 + 30, s->time/20, ANIMATION_WAVE | ANIMATION_BUNGEE);
418 updatescr();
419 return;
421 if (s->old_court_type != s->court_type || s->text_changed || s->old_referee != s->referee) {
422 clear_screen();
423 fill_image(s->court_type, 120, 120, 400, 250);
424 show_image(GR_COURT, 0, 0, 255);
425 font_draw_string( GR_DKC2_FONT, s->game_score_str, 14, 14, 0, ANIMATION_NONE);
426 font_draw_string( GR_DKC2_FONT, s->sets_score_str, (WIDTH-font_get_string_width( GR_DKC2_FONT, s->sets_score_str))-14, 14, 0, ANIMATION_NONE);
427 #ifdef EXTENDED_REFEREE
428 switch (s->referee) {
429 case REFEREE_NORMAL:
430 t = 1000;
431 break;
432 case REFEREE_OUT:
433 t = 200;
434 break;
435 case REFEREE_PLAYER1:
436 case REFEREE_PLAYER2:
437 t = 400;
438 break;
440 t = (s->time/t)%4;
441 switch (t) {
442 case 0:
443 t=0;
444 break;
445 case 1:
446 t=1;
447 break;
448 case 2:
449 t=0;
450 break;
451 case 3:
452 t=2;
453 break;
455 show_sprite( GR_REFEREE, s->referee*3+t, 12, 250, 10, 255);
456 if (voice_finished_flag == 0) {
457 show_sprite(GR_TALK, (s->time/150)%2, 2, 280, 45, 255);
459 #else
460 show_sprite( GR_REFEREE, s->referee, 4, 250, 10, 255);
461 #endif
462 s->old_court_type = s->court_type;
463 s->text_changed = false;
464 s->old_referee = s->referee;
465 store_screen();
467 show_image( GR_SHADOW, s->ball.x-BALL_X_MID, s->ball.y + get_phase( s) - BALL_Y_MID, 255);
469 show_sprite( GR_RACKET, (!s->player1.state), 4, s->player1.x-RACKET_X_MID, s->player1.y-RACKET_Y_MID, 255);
470 show_sprite( GR_RACKET, (!s->player2.state)+2, 4, s->player2.x-RACKET_X_MID, s->player2.y-RACKET_Y_MID, 255);
472 if( s->ball.move_x > 0) {
473 show_sprite( GR_BALL, (s->time/100)%BALL_STATES, BALL_STATES, s->ball.x-BALL_X_MID, s->ball.y-BALL_Y_MID, 255);
474 } else if( s->ball.move_x < 0) {
475 show_sprite( GR_BALL, BALL_STATES-1-(s->time/100)%BALL_STATES, BALL_STATES, s->ball.x-BALL_X_MID, s->ball.y-BALL_Y_MID, 255);
476 } else {
477 show_sprite( GR_BALL, 0, BALL_STATES, s->ball.x-BALL_X_MID, s->ball.y-BALL_Y_MID, 255);
480 /* Player 1's mouse rectangle */
481 if (!(s->player1.mouse_locked)) {
482 rectangle(s->player1.x-2+5, s->player1.mouse_y-2, 4, 4, 255, 255, 255);
483 rectangle(s->player1.x-1+5, s->player1.mouse_y-1, 2, 2, 0, 0, 0);
487 font_draw_string( GR_DKC2_FONT, s->status, (WIDTH-font_get_string_width( GR_DKC2_FONT, s->status))/2, HEIGHT-50, s->time/30, ANIMATION_WAVE);
489 for (i=0; i<s->rain; i++) {
490 x = rand()%WIDTH;
491 y = rand()%HEIGHT;
492 draw_line_faded(x, y, x+10+s->wind*5, y+30, 0, 0, 255, 100, 200, 255);
494 if (s->rain) {
496 * Cheap-ish update of the whole screen. This can
497 * probably be optimized.
499 update_rect(0, 0, WIDTH, HEIGHT);
502 #ifdef DEBUG
503 line_horiz( s->player1.y, 255, 0, 0);
504 line_horiz( s->player2.y, 0, 0, 255);
505 line_horiz( s->ball.y, 0, 255, 0);
507 line_vert( s->player1.x, 255, 0, 0);
508 line_vert( s->player2.x, 0, 0, 255);
509 line_vert( s->ball.x, 0, 255, 0);
511 line_horiz( s->player1.ball_dest, 255, 0, 255);
512 line_horiz( s->player2.ball_dest, 0, 255, 255);
514 line_horiz( GAME_Y_MIN, 100, 100, 100);
515 line_horiz( GAME_Y_MAX, 100, 100, 100);
516 #endif
517 switch (s->fog) {
518 default:
519 case 4:
520 fill_image_offset(GR_FOG2, 0, 0, WIDTH, HEIGHT, -s->time/150-s->windtime/200, 0);
521 case 3:
522 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, -s->time/100-s->windtime/150, 20);
523 case 2:
524 fill_image_offset(GR_FOG2, 0, 0, WIDTH, HEIGHT, -s->time/180-s->windtime/180, 80);
525 case 1:
526 fill_image_offset(GR_FOG, 0, 0, WIDTH, HEIGHT, s->time/200-s->windtime/100, 0);
527 case 0:
528 break;
530 if (s->night) {
531 show_image(GR_NIGHT, 0, 0, 255);
534 updatescr();
537 void limit_value( float* value, float min, float max) {
538 if( *value < min) {
539 *value = min;
540 } else if( *value > max) {
541 *value = max;
545 float get_phase( GameState* s) {
546 float pos, fract;
547 float x, min, max, direction;
549 x = s->ball.x;
550 min = GAME_X_MIN;
551 max = GAME_X_MAX;
552 direction = s->ball.move_x;
554 pos = (direction>0)?(1-GROUND_PHASE):(GROUND_PHASE);
556 fract = (x-min)/(max-min);
558 if( fract < pos) {
559 fract = fract/pos;
560 return fabsf( cosf(PI*fract/2))*PHASE_AMP*s->ball.jump;
561 } else {
562 fract = (pos-fract)/(1-pos);
563 return fabsf( sinf(PI*fract/2))*PHASE_AMP*s->ball.jump;
567 float get_move_y( GameState* s, unsigned char player) {
568 float pct, dest, x_len, y_len;
569 float py, by, pa, move_x;
571 py = (player==1)?(s->player1.y):(s->player2.y);
572 by = s->ball.y;
573 pa = RACKET_Y_MID*2;
574 move_x = s->ball.move_x;
576 /* -1.0 .. 1.0 for racket hit position */
577 pct = (by-py)/(pa/2);
578 limit_value( &pct, -1.0, 1.0);
580 /* Y destination for ball */
581 dest = GAME_Y_MID + pct*(GAME_Y_MAX-GAME_Y_MIN);
582 if( player == 1) {
583 s->player1.ball_dest = dest;
584 } else {
585 s->player2.ball_dest = dest;
588 /* lengths for the ball's journey */
589 if( player == 1) {
590 x_len = fabsf(GAME_X_MAX - s->ball.x);
591 y_len = dest - by + MOVE_Y_SEED-rand()%MOVE_Y_SEED*2;
592 } else {
593 x_len = s->ball.x - GAME_X_MIN;
594 y_len = by - dest + MOVE_Y_SEED-rand()%MOVE_Y_SEED*2;
597 /* return the should-be value for move_y */
598 return (y_len*move_x)/(x_len);
601 void input_human( Player* player, bool up, bool down, bool hit, bool use_mouse, GameState* s) {
602 int diff = PLAYER_MOVE_Y;
603 int mb;
606 * Only use mouse control if the user isn't pressing any buttons
607 * this way, keyboard control still works when mouse control is
608 * enabled.
610 if (use_mouse && (down || up || hit)) {
612 * this is here so if the user decides to play
613 * with keyboard controls, we will lock the
614 * mouse and disable displaying the mouse cursor
616 player->mouse_locked = true;
618 if (use_mouse && !down && !up && !hit) {
619 mb = SDL_GetMouseState(&(player->mouse_x), &(player->mouse_y));
620 if (mb&SDL_BUTTON(SDL_BUTTON_LEFT)) {
621 if (player->mouse_y < player->y) {
622 down = false;
623 up = true;
624 diff = (player->y-player->mouse_y<diff)?(player->y-player->mouse_y):(diff);
625 } else if (player->mouse_y > player->y) {
626 up = false;
627 down = true;
628 diff = (player->mouse_y-player->y<diff)?(player->mouse_y-player->y):(diff);
630 player->mouse_locked = false;
631 } else if (!player->mouse_locked) {
632 hit = true;
636 if (fabsf(s->joystick_y) > JOYSTICK_TRESHOLD) {
637 diff = PLAYER_MOVE_Y*fabsf(s->joystick_y)/40.0;
638 if (diff > PLAYER_MOVE_Y) {
639 diff = PLAYER_MOVE_Y;
643 if (up) {
644 player->y -= fminf(diff, diff*player->accelerate);
645 player->accelerate *= PLAYER_ACCEL_INCREASE;
646 } else if (down) {
647 player->y += fminf(diff, diff*player->accelerate);
648 player->accelerate *= PLAYER_ACCEL_INCREASE;
649 } else {
650 player->accelerate = PLAYER_ACCEL_DEFAULT;
653 if( hit) {
654 if( !player->state && !player->state_locked) {
655 player->state = PLAYER_STATE_MAX;
656 player->state_locked = true;
658 } else {
659 player->state_locked = false;
663 void input_ai( Player* player, Ball* ball, Player* opponent, GameState* s) {
664 float fact = 1.7;
665 float target;
667 if( fabsf( player->y - ball->y) > RACKET_Y_MID*5) {
668 fact = 3.5;
671 target = GAME_Y_MID + (opponent->ball_dest - GAME_Y_MID)/5;
673 if( player->responsible) {
674 if( player->desire == DESIRE_NORMAL && !IS_NEAR_Y_AI( player->y, ball->y)) {
675 if( player->y < ball->y) {
676 player->y += fmin( 2*fact, ball->y - player->y);
677 } else if( player->y > ball->y) {
678 player->y -= fmin( 2*fact, player->y - ball->y);
682 if( (ball->move_x != 0 || IS_NEAR_Y_AI( player->y, ball->y)) && IS_NEAR_X_AI( player->x, ball->x) && !player->state && rand()%4==0) {
683 player->state = PLAYER_STATE_MAX;
685 } else if( ball->move_x == 0) {
686 if( player->desire == DESIRE_NORMAL && !IS_NEAR_Y_AI( player->y, target)) {
687 if( player->y < target) {
688 player->y += fmin( fact, (target-player->y)/40.0);
689 } else if( player->y > target) {
690 player->y -= fmin( fact, (player->y-target)/40.0);
693 } else if( s->ngram_prediction > 0.0) {
694 target = s->ngram_prediction*((float)HEIGHT)/((float)(NGRAM_STEPS));
695 target = GAME_Y_MID + (target-GAME_Y_MID)*1.5;
697 if( player->desire == DESIRE_NORMAL && !IS_NEAR_Y_AI( player->y, target)) {
698 if( player->y < target) {
699 player->y += fmin( fact, (target-player->y)/40.0);
700 } else if( player->y > target) {
701 player->y -= fmin( fact, (player->y-target)/40.0);
704 } else {/*
705 if( player->desire == DESIRE_NORMAL) {
706 if( !IS_NEAR_Y_AI( player->y, target)) {
707 player->y += (target - player->y)/40.0;
713 void game_setup_serve( GameState* s) {
714 s->ball.jump = 7.5;
715 s->ball.y = GAME_Y_MID;
716 s->ball.move_x = 0.0;
717 s->ball.move_y = 0.0;
719 if( s->player1_serves) {
720 s->player1.responsible = true;
721 s->player1.ball_dest = 0.0;
722 s->ball.x = GAME_X_MIN-RACKET_X_MID*1.5;
723 } else {
724 s->player1.responsible = false;
725 s->player2.ball_dest = 0.0;
726 s->ball.x = GAME_X_MAX+RACKET_X_MID*1.5;
729 s->player2.responsible = !(s->player1.responsible);
732 float ngram_predictor( GameState* s) {
733 unsigned int count = 0;
734 unsigned long sum = 0;
735 int x, y, z;
736 float result;
738 if( s->history_size < 3) {
739 return 0.0;
742 x = s->history[1];
743 y = s->history[2];
745 for( z = 0; z<NGRAM_STEPS; z++) {
746 count += s->ngram[x][y][z];
747 sum += z * s->ngram[x][y][z];
750 result = ((float)(sum))/((float)(count));
751 #ifdef DEBUG
752 printf( "predicting next = %.2f\n", result);
753 #endif
755 return result;
758 void score_game( GameState* s, bool player1_scored) {
759 Player* winner = (player1_scored)?(&(s->player1)):(&(s->player2));
760 Player* loser = (player1_scored)?(&(s->player2)):(&(s->player1));
762 if( s->current_set >= SETS_TO_WIN*2-1) {
763 return;
766 winner->game++;
767 if( loser->game < winner->game-1) {
768 if( winner->game >= 4) {
769 winner->game = loser->game = 0;
770 winner->sets[s->current_set]++;
772 #ifdef HAVE_VOICE_FILES
773 /* speak the current score */
774 voice_say_list(4, VOICE_ZERO_IN + (s->player1.sets[s->current_set])*2, VOICE_TO, VOICE_ZERO_OUT + (s->player2.sets[s->current_set])*2, VOICE_IN_THE_FIRST_SET+s->current_set);
775 #endif
777 /* scoring the set.. */
778 if( (winner->sets[s->current_set] == 6 && loser->sets[s->current_set] < 5) ||
779 winner->sets[s->current_set] == 7) {
780 s->current_set++;
781 s->winner = game_get_winner( s);
787 char* format_sets( GameState* s) {
788 static char sets[100];
789 static char tmp[100];
790 int i, max = s->current_set;
792 sets[0] = '\0';
794 if( s->winner != WINNER_NONE) {
795 max--;
797 for( i=0; i<=max; i++) {
798 sprintf( tmp, "%d:%d, ", s->player1.sets[i], s->player2.sets[i]);
799 strcat( sets, tmp);
802 sets[strlen(sets)-2] = '\0';
804 return sets;
807 char* format_game( GameState* s) {
808 static char game[100];
809 static const int game_scoring[] = { 0, 15, 30, 40 };
811 if( s->player1.game < 4 && s->player2.game < 4) {
812 #ifdef HAVE_VOICE_FILES
813 if (s->player1.game > 0 || s->player2.game > 0) {
814 if (s->player1.game == s->player2.game) {
815 voice_say_list(2, VOICE_LOVE_IN + 2*(s->player1.game), VOICE_ALL);
816 } else {
817 voice_say_list(2, VOICE_LOVE_IN + 2*(s->player1.game), VOICE_LOVE_OUT + 2*(s->player2.game));
820 #endif
821 sprintf( game, "%d - %d", game_scoring[s->player1.game], game_scoring[s->player2.game]);
822 } else if( s->player1.game > s->player2.game) {
823 #ifdef HAVE_VOICE_FILES
824 voice_say_list(1, VOICE_ADVANTAGE_PLAYER_ONE);
825 #endif
826 strcpy( game, "advantage player 1");
827 } else if( s->player1.game < s->player2.game) {
828 #ifdef HAVE_VOICE_FILES
829 voice_say_list(1, VOICE_ADVANTAGE_PLAYER_TWO);
830 #endif
831 strcpy( game, "advantage player 2");
832 } else {
833 #ifdef HAVE_VOICE_FILES
834 voice_say_list(1, VOICE_DEUCE);
835 #endif
836 strcpy( game, "deuce");
839 return game;
842 char* format_status( GameState* s) {
843 static char status[100];
844 static const char* set_names[] = { "first", "second", "third", "fourth", "fifth" };
846 sprintf( status, "%d:%d in %s set", s->player1.sets[s->current_set], s->player2.sets[s->current_set], set_names[s->current_set]);
848 return status;
851 int game_get_winner( GameState* s) {
852 int i;
853 int sets[2] = {0};
855 for( i=0; i<s->current_set; i++) {
856 if( s->player1.sets[i] > s->player2.sets[i]) {
857 sets[0]++;
858 } else {
859 sets[1]++;
863 if( sets[0] == SETS_TO_WIN) return WINNER_PLAYER1;
864 if( sets[1] == SETS_TO_WIN) return WINNER_PLAYER2;
866 return WINNER_NONE;