use pngs instead of bmps
[tennix.git] / game.c
blob967644227effeffd3cb265dc5d884b86b7f072c5
2 /**
4 * Tennix! SDL Port
5 * Copyright (C) 2003, 2007 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"
33 void game() {
34 GameState s = {
35 { GAME_X_MID, GAME_Y_MID, BALL_DEFAULT_SPEED, 0.2, 7.5 },
36 { 0, 0, 0, 0, 0 },
37 { GAME_X_MIN-4, GAME_Y_MID, 0, 0, 1 },
38 { GAME_X_MAX, GAME_Y_MID, 0, 0, 0 },
44 Uint32 ot = SDL_GetTicks();
45 Uint32 nt;
46 Uint32 dt = GAME_TICKS;
47 Uint32 diff;
48 Uint32 accumulator = 0;
49 bool quit = false;
51 sound_audience();
53 while( !quit) {
54 nt = SDL_GetTicks();
55 diff = nt-ot;
56 if( diff > 2000) {
57 diff = 0;
60 accumulator += diff;
61 ot = nt;
63 while( accumulator >= dt) {
64 quit = step( &s);
65 s.time += dt;
66 accumulator -= dt;
68 if( s.was_stopped) {
69 ot = SDL_GetTicks();
70 s.was_stopped = false;
74 render( &s);
79 bool step( GameState* s) {
80 Uint8 *keys;
81 SDL_Event e;
83 if( get_phase( s) < 1.0) {
84 if( !s->ground.jump) {
85 sound_ground();
86 /* after the ball hit the ground, responsibilities change */
87 s->player1.responsible = !(s->player2.responsible = !s->player2.responsible);
89 s->ground.jump = 3;
90 s->ground.x = s->ball.x;
91 s->ground.y = s->ball.y;
92 } else {
93 if( s->ground.jump && !(s->time%5)) s->ground.jump--;
96 if( IS_OUT_X(s->ball.x) || IS_OUT_Y(s->ball.y)) {
97 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)) {
98 s->player2.score++;
99 sound_applause();
100 s->player2.responsible = !(s->player1.responsible = 1);
101 s->ball.x = GAME_X_MID;
102 s->ball.y = GAME_Y_MID;
103 s->ball.move_x = fabsf( s->ball.move_x);
104 limit_value( &s->ball.move_x, s->ball.move_x, BALL_DEFAULT_SPEED);
105 s->ball.move_y = -0.1;
106 wait_keypress();
107 s->was_stopped = true;
108 sound_applause_stop();
111 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)) {
112 s->player1.score++;
113 sound_applause();
114 s->player1.responsible = !(s->player2.responsible = 1);
115 s->ball.x = GAME_X_MID;
116 s->ball.y = GAME_Y_MID;
117 s->ball.move_x = -fabsf( s->ball.move_x);
118 limit_value( &s->ball.move_x, -BALL_DEFAULT_SPEED, s->ball.move_x);
119 s->ball.move_y = 0.1;
120 wait_keypress();
121 s->was_stopped = true;
122 sound_applause_stop();
125 if( IS_OUT_X(s->ball.x)) {
126 if( s->ball.move_x < 0) {
127 s->ball.x = GAME_X_MIN;
128 if( s->player1.state == PLAYER_STATE_MAX) {
129 s->ball.move_x = PLAYER_POWERSHOT;
130 } else {
131 s->ball.move_x = 2.5 + 2.0*s->player1.state/PLAYER_STATE_MAX;
133 s->ball.move_y = get_move_y( s, 1);
134 s->ball.jump += 1.0-2.0*(s->player1.state<5);
135 sound_racket( s->player1.state == PLAYER_STATE_MAX);
136 } else {
137 s->ball.x = GAME_X_MAX;
138 if( s->player2.state == PLAYER_STATE_MAX) {
139 s->ball.move_x = -PLAYER_POWERSHOT;
140 } else {
141 s->ball.move_x = -(2.5 + 2.0*s->player2.state/PLAYER_STATE_MAX);
143 s->ball.move_y = get_move_y( s, 2);
144 s->ball.jump += 1.0-2.0*(s->player2.state<5);
145 sound_racket( s->player2.state == PLAYER_STATE_MAX);
150 s->ball.x += s->ball.move_x;
151 s->ball.y += s->ball.move_y;
153 if( s->player1.state) s->player1.state--;
154 if( s->player2.state) s->player2.state--;
156 SDL_PollEvent( &e);
157 keys = SDL_GetKeyState( NULL);
159 if( keys['2']) s->player1.y-=8;
160 if( keys['w']) s->player1.y-=6;
161 if( keys['s']) s->player1.y+=6;
162 if( keys['x']) s->player1.y+=8;
163 if( keys['d'] && !s->player1.state) s->player1.state = PLAYER_STATE_MAX;
165 if( keys['9']) s->player2.y-=8;
166 if( keys['o']) s->player2.y-=6;
167 if( keys['l']) s->player2.y+=6;
168 if( keys['.']) s->player2.y+=8;
169 if( keys['k'] && !s->player2.state) s->player2.state = PLAYER_STATE_MAX;
171 if( keys['f']) SDL_WM_ToggleFullScreen( screen);
172 if( keys['y']) SDL_SaveBMP( screen, "screenshot.bmp");
174 if( keys[SDLK_ESCAPE] || keys['q']) return true;
176 limit_value( &s->player1.y, PLAYER_Y_MIN, PLAYER_Y_MAX);
177 limit_value( &s->player2.y, PLAYER_Y_MIN, PLAYER_Y_MAX);
178 limit_value( &s->ball.jump, BALL_JUMP_MIN, BALL_JUMP_MAX);
180 return false;
183 void render( GameState* s) {
184 clearscr();
185 show_image( GR_COURT, 0, 0, 255);
186 show_image( GR_SHADOW, s->ball.x, s->ball.y, 255);
188 show_sprite( GR_RACKET, (!s->player1.state), 4, s->player1.x, s->player1.y, 255);
189 show_sprite( GR_RACKET, (!s->player2.state)+2, 4, s->player2.x, s->player2.y, 255);
191 if( s->ground.jump) {
192 show_sprite( GR_GROUND, s->ground.jump-1, 3, s->ground.x, s->ground.y, 128);
195 if( s->ball.move_x > 0) {
196 show_sprite( GR_BALL, (s->time/1000)%4, 4, s->ball.x, s->ball.y - get_phase( s), 255);
197 } else {
198 show_sprite( GR_BALL, 3-(s->time/1000)%4, 4, s->ball.x, s->ball.y - get_phase( s), 255);
201 show_digit( s->player1.score/10%10, 140*2, 14, 100);
202 show_digit( s->player1.score%10, 148*2, 14, 100);
203 show_digit( 10, 156*2, 14, 100);
204 show_digit( s->player2.score/10%10, 164*2, 14, 100);
205 show_digit( s->player2.score%10, 172*2, 14, 100);
207 updatescr();
210 void limit_value( float* value, float min, float max) {
211 if( *value < min) {
212 *value = min;
213 } else if( *value > max) {
214 *value = max;
218 float get_phase( GameState* s) {
219 float pos, fract;
220 float x, min, max, direction;
222 x = s->ball.x;
223 min = GAME_X_MIN;
224 max = GAME_X_MAX;
225 direction = s->ball.move_x;
227 pos = (direction>0)?(1-GROUND_PHASE):(GROUND_PHASE);
229 fract = (x-min)/(max-min);
231 if( fract < pos) {
232 fract = fract/pos;
233 return fabsf( cosf(PI*fract/2))*PHASE_AMP*s->ball.jump;
234 } else {
235 fract = (pos-fract)/(1-pos);
236 return fabsf( sinf(PI*fract/2))*PHASE_AMP*s->ball.jump;
240 float get_move_y( GameState* s, unsigned char player) {
241 float pct, dest, x_len, y_len;
242 float py, by, pa, move_x, phase;
244 if( player == 1) {
245 py = s->player1.y;
246 } else {
247 py = s->player2.y;
249 by = s->ball.y - get_phase( s);
250 pa = PLAYER_AREA;
251 move_x = s->ball.move_x;
252 phase = get_phase( s);
254 /* 0.0 .. 1.0 for racket hit position */
255 pct = ((py+pa)-by)/(2*pa);
256 limit_value( &pct, 0.0, 1.0);
258 /* Y destination for ball */
259 dest = GAME_Y_MIN + pct*(GAME_Y_MAX-GAME_Y_MIN);
261 /* lengths for the ball's journey */
262 x_len = GAME_X_MAX - GAME_X_MIN;
263 y_len = dest - py + phase;
265 /* return the should-be value for move_y */
266 return (y_len*move_x)/(x_len);