Make Tennix buildable on the MacOSX platform (Tennix.app)
[tennix.git] / game.c
blob22f22109a296570f9f267a2592eb2e4c5d06c66e
1 #include <stdio.h>
2 #include <math.h>
4 #include "tennix.h"
5 #include "game.h"
6 #include "graphics.h"
7 #include "input.h"
8 #include "sound.h"
10 void game() {
11 GameState s = {
12 { GAME_X_MID, GAME_Y_MID, BALL_DEFAULT_SPEED, 0.2, 7.5 },
13 { 0, 0, 0, 0, 0 },
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();
22 Uint32 nt;
23 Uint32 dt = GAME_TICKS;
24 Uint32 diff;
25 Uint32 accumulator = 0;
26 bool quit = false;
28 sound_audience();
30 while( !quit) {
31 nt = SDL_GetTicks();
32 diff = nt-ot;
33 if( diff > 2000) {
34 diff = 0;
37 accumulator += diff;
38 ot = nt;
40 while( accumulator >= dt) {
41 quit = step( &s);
42 s.time += dt;
43 accumulator -= dt;
45 if( s.was_stopped) {
46 ot = SDL_GetTicks();
47 s.was_stopped = false;
51 render( &s);
56 bool step( GameState* s) {
57 Uint8 *keys;
58 SDL_Event e;
60 if( get_phase( s) < 1.0) {
61 if( !s->ground.jump) {
62 sound_ground();
63 /* after the ball hit the ground, responsibilities change */
64 s->player1.responsible = !(s->player2.responsible = !s->player2.responsible);
66 s->ground.jump = 3;
67 s->ground.x = s->ball.x;
68 s->ground.y = s->ball.y;
69 } else {
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)) {
75 s->player2.score++;
76 sound_applause();
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;
83 wait_keypress();
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)) {
89 s->player1.score++;
90 sound_applause();
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);
96 s->ball.move_y = 0.1;
97 wait_keypress();
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;
107 } else {
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);
113 } else {
114 s->ball.x = GAME_X_MAX;
115 if( s->player2.state == PLAYER_STATE_MAX) {
116 s->ball.move_x = -PLAYER_POWERSHOT;
117 } else {
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--;
133 SDL_PollEvent( &e);
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);
157 return false;
160 void render( GameState* s) {
161 clearscr();
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);
174 } else {
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);
184 updatescr();
187 void limit_value( float* value, float min, float max) {
188 if( *value < min) {
189 *value = min;
190 } else if( *value > max) {
191 *value = max;
195 float get_phase( GameState* s) {
196 float pos, fract;
197 float x, min, max, direction;
199 x = s->ball.x;
200 min = GAME_X_MIN;
201 max = GAME_X_MAX;
202 direction = s->ball.move_x;
204 pos = (direction>0)?(1-GROUND_PHASE):(GROUND_PHASE);
206 fract = (x-min)/(max-min);
208 if( fract < pos) {
209 fract = fract/pos;
210 return fabsf( cosf(PI*fract/2))*PHASE_AMP*s->ball.jump;
211 } else {
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;
221 if( player == 1) {
222 py = s->player1.y;
223 } else {
224 py = s->player2.y;
226 by = s->ball.y - get_phase( s);
227 pa = PLAYER_AREA;
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);