12 { GAME_X_MID
, GAME_Y_MID
, BALL_DEFAULT_SPEED
, 0.2, 7.5 },
14 { GAME_X_MIN
-4, GAME_Y_MID
, 0, 0, 1 },
15 { GAME_X_MAX
, GAME_Y_MID
, 0, 0, 0 },
21 Uint32 ot
= SDL_GetTicks();
23 Uint32 dt
= GAME_TICKS
;
25 Uint32 accumulator
= 0;
40 while( accumulator
>= dt
) {
47 s
.was_stopped
= false;
56 bool step( GameState
* s
) {
60 if( get_phase( s
) < 1.0) {
61 if( !s
->ground
.jump
) {
63 /* after the ball hit the ground, responsibilities change */
64 s
->player1
.responsible
= !(s
->player2
.responsible
= !s
->player2
.responsible
);
67 s
->ground
.x
= s
->ball
.x
;
68 s
->ground
.y
= s
->ball
.y
;
70 if( s
->ground
.jump
&& !(s
->time
%5)) s
->ground
.jump
--;
73 if( IS_OUT_X(s
->ball
.x
) || IS_OUT_Y(s
->ball
.y
)) {
74 if( (IS_OUT_Y(s
->ball
.y
) && s
->player1
.responsible
) || (!(s
->player1
.state
&& IS_NEAR_Y(s
->player1
.y
, s
->ball
.y
+get_phase( s
))) && s
->ball
.x
< GAME_X_MIN
)) {
77 s
->player2
.responsible
= !(s
->player1
.responsible
= 1);
78 s
->ball
.x
= GAME_X_MID
;
79 s
->ball
.y
= GAME_Y_MID
;
80 s
->ball
.move_x
= fabsf( s
->ball
.move_x
);
81 limit_value( &s
->ball
.move_x
, s
->ball
.move_x
, BALL_DEFAULT_SPEED
);
82 s
->ball
.move_y
= -0.1;
84 s
->was_stopped
= true;
85 sound_applause_stop();
88 if( (IS_OUT_Y(s
->ball
.y
) && s
->player2
.responsible
) || (!(s
->player2
.state
&& IS_NEAR_Y(s
->player2
.y
, s
->ball
.y
+get_phase( s
))) && s
->ball
.x
> GAME_X_MAX
)) {
91 s
->player1
.responsible
= !(s
->player2
.responsible
= 1);
92 s
->ball
.x
= GAME_X_MID
;
93 s
->ball
.y
= GAME_Y_MID
;
94 s
->ball
.move_x
= -fabsf( s
->ball
.move_x
);
95 limit_value( &s
->ball
.move_x
, -BALL_DEFAULT_SPEED
, s
->ball
.move_x
);
98 s
->was_stopped
= true;
99 sound_applause_stop();
102 if( IS_OUT_X(s
->ball
.x
)) {
103 if( s
->ball
.move_x
< 0) {
104 s
->ball
.x
= GAME_X_MIN
;
105 if( s
->player1
.state
== PLAYER_STATE_MAX
) {
106 s
->ball
.move_x
= PLAYER_POWERSHOT
;
108 s
->ball
.move_x
= 2.5 + 2.0*s
->player1
.state
/PLAYER_STATE_MAX
;
110 s
->ball
.move_y
= get_move_y( s
, 1);
111 s
->ball
.jump
+= 1.0-2.0*(s
->player1
.state
<5);
112 sound_racket( s
->player1
.state
== PLAYER_STATE_MAX
);
114 s
->ball
.x
= GAME_X_MAX
;
115 if( s
->player2
.state
== PLAYER_STATE_MAX
) {
116 s
->ball
.move_x
= -PLAYER_POWERSHOT
;
118 s
->ball
.move_x
= -(2.5 + 2.0*s
->player2
.state
/PLAYER_STATE_MAX
);
120 s
->ball
.move_y
= get_move_y( s
, 2);
121 s
->ball
.jump
+= 1.0-2.0*(s
->player2
.state
<5);
122 sound_racket( s
->player2
.state
== PLAYER_STATE_MAX
);
127 s
->ball
.x
+= s
->ball
.move_x
;
128 s
->ball
.y
+= s
->ball
.move_y
;
130 if( s
->player1
.state
) s
->player1
.state
--;
131 if( s
->player2
.state
) s
->player2
.state
--;
134 keys
= SDL_GetKeyState( NULL
);
136 if( keys
['2']) s
->player1
.y
-=8;
137 if( keys
['w']) s
->player1
.y
-=6;
138 if( keys
['s']) s
->player1
.y
+=6;
139 if( keys
['x']) s
->player1
.y
+=8;
140 if( keys
['d'] && !s
->player1
.state
) s
->player1
.state
= PLAYER_STATE_MAX
;
142 if( keys
['9']) s
->player2
.y
-=8;
143 if( keys
['o']) s
->player2
.y
-=6;
144 if( keys
['l']) s
->player2
.y
+=6;
145 if( keys
['.']) s
->player2
.y
+=8;
146 if( keys
['k'] && !s
->player2
.state
) s
->player2
.state
= PLAYER_STATE_MAX
;
148 if( keys
['f']) SDL_WM_ToggleFullScreen( screen
);
149 if( keys
['y']) SDL_SaveBMP( screen
, "screenshot.bmp");
151 if( keys
[SDLK_ESCAPE
] || keys
['q']) return true;
153 limit_value( &s
->player1
.y
, PLAYER_Y_MIN
, PLAYER_Y_MAX
);
154 limit_value( &s
->player2
.y
, PLAYER_Y_MIN
, PLAYER_Y_MAX
);
155 limit_value( &s
->ball
.jump
, BALL_JUMP_MIN
, BALL_JUMP_MAX
);
160 void render( GameState
* s
) {
162 show_image( GR_COURT
, 0, 0, 255);
163 show_image( GR_SHADOW
, s
->ball
.x
, s
->ball
.y
, 255);
165 show_sprite( GR_RACKET
, (!s
->player1
.state
), 4, s
->player1
.x
, s
->player1
.y
, 255);
166 show_sprite( GR_RACKET
, (!s
->player2
.state
)+2, 4, s
->player2
.x
, s
->player2
.y
, 255);
168 if( s
->ground
.jump
) {
169 show_sprite( GR_GROUND
, s
->ground
.jump
-1, 3, s
->ground
.x
, s
->ground
.y
, 128);
172 if( s
->ball
.move_x
> 0) {
173 show_sprite( GR_BALL
, (s
->time
/1000)%4, 4, s
->ball
.x
, s
->ball
.y
- get_phase( s
), 255);
175 show_sprite( GR_BALL
, 3-(s
->time
/1000)%4, 4, s
->ball
.x
, s
->ball
.y
- get_phase( s
), 255);
178 show_digit( s
->player1
.score
/10%10, 140*2, 14, 100);
179 show_digit( s
->player1
.score
%10, 148*2, 14, 100);
180 show_digit( 10, 156*2, 14, 100);
181 show_digit( s
->player2
.score
/10%10, 164*2, 14, 100);
182 show_digit( s
->player2
.score
%10, 172*2, 14, 100);
187 void limit_value( float* value
, float min
, float max
) {
190 } else if( *value
> max
) {
195 float get_phase( GameState
* s
) {
197 float x
, min
, max
, direction
;
202 direction
= s
->ball
.move_x
;
204 pos
= (direction
>0)?(1-GROUND_PHASE
):(GROUND_PHASE
);
206 fract
= (x
-min
)/(max
-min
);
210 return fabsf( cosf(PI
*fract
/2))*PHASE_AMP
*s
->ball
.jump
;
212 fract
= (pos
-fract
)/(1-pos
);
213 return fabsf( sinf(PI
*fract
/2))*PHASE_AMP
*s
->ball
.jump
;
217 float get_move_y( GameState
* s
, unsigned char player
) {
218 float pct
, dest
, x_len
, y_len
;
219 float py
, by
, pa
, move_x
, phase
;
226 by
= s
->ball
.y
- get_phase( s
);
228 move_x
= s
->ball
.move_x
;
229 phase
= get_phase( s
);
231 /* 0.0 .. 1.0 for racket hit position */
232 pct
= ((py
+pa
)-by
)/(2*pa
);
233 limit_value( &pct
, 0.0, 1.0);
235 /* Y destination for ball */
236 dest
= GAME_Y_MIN
+ pct
*(GAME_Y_MAX
-GAME_Y_MIN
);
238 /* lengths for the ball's journey */
239 x_len
= GAME_X_MAX
- GAME_X_MIN
;
240 y_len
= dest
- py
+ phase
;
242 /* return the should-be value for move_y */
243 return (y_len
*move_x
)/(x_len
);