1 // Apricots game routine
2 // Author: M.D.Snellgrove
3 // Date: 24/3/2002
4 // History:
5
6 // Changes by M Harman for Windows version, June 2003:
7 // Changes for input stuff.
8 // Changes to main loop.
9
10 // Changes by M Snellgrove 7/7/2003
11 // Keyboard input tidied up and controls for player 2 re-activated
12
13 // Changed by M Snellgrove 30/7/2003
14 // GAME_SPEED bugfix in smoke timings and bomb y-velocity
15
16 // Changed by M Snellgrove 3/8/2003
17 // Delay loop now fixed (thanks M Harman)
18
19 #include "apricots.h"
20
21 // Fire shot
22
fire_shot(plane & p,linkedlist<shottype> & shot,sampleio & sound,double xmove[17],double ymove[17])23 void fire_shot(plane &p, linkedlist <shottype> &shot, sampleio &sound,
24 double xmove[17], double ymove[17]){
25
26 shottype bullet;
27 bullet.x = p.x + 6.0 + 12.0*xmove[p.d] + p.xs;
28 bullet.y = p.y + 6.0 + 12.0*ymove[p.d] + p.ys;
29 bullet.xs = 8.0 * xmove[p.d] * GAME_SPEED;
30 bullet.ys = 8.0 * ymove[p.d] * GAME_SPEED;
31 bullet.side = p.side;
32 bullet.time = int(40/GAME_SPEED);
33 shot.add(bullet);
34 p.ammo--;
35 p.shotdelay = int(3/GAME_SPEED);
36 sound.play(SOUND_SHOT);
37
38 }
39
40 // Drop bomb
41
drop_bomb(plane & p,linkedlist<falltype> & fall,sampleio & sound,int bombimage[17])42 void drop_bomb(plane &p, linkedlist <falltype> &fall, sampleio &sound,
43 int bombimage[17]){
44
45 falltype bomb;
46 bomb.x = p.x + 5.0 + p.xs;
47 bomb.y = p.y + 16.0 + p.ys;
48 bomb.xs = p.xs;
49 bomb.ys = p.ys + (2.0 * GAME_SPEED);
50 bomb.image = bombimage[p.d];
51 bomb.side = p.side;
52 bomb.type = 3;
53 switch (p.d){
54 case 1: case 9:
55 bomb.bombrotate = 0;
56 break;
57 case 2: case 3: case 4: case 5: case 6: case 7: case 8:
58 bomb.bombrotate = -1;
59 break;
60 case 10: case 11: case 12: case 13: case 14: case 15: case 16:
61 bomb.bombrotate = 1;
62 break;
63 }
64 bomb.rotatedelay = 0;
65 fall.add(bomb);
66 p.bombs--;
67
68
69 p.shotdelay = int(5/GAME_SPEED);
70 sound.play(SOUND_BOMB);
71
72 }
73
74 // Main plane action routine
75
act(gamedata & g,int jx,int jy,bool jb)76 void act(gamedata &g, int jx, int jy, bool jb){
77
78 // Reset hidden/afterburner status
79 g.p().hide = false;
80 g.p().boost = false;
81 g.p().gunthreat = 0;
82
83 switch(g.p().state){
84 case 0: // OK planes
85
86 switch(g.p().land){
87 case 0: // Stationary on runway
88 // Check for mission 0 and mission 1 wins
89 if (((g.mission == 0) || (g.mission == 1)) && (!g.p().drak) &&
90 (g.p().score >= g.targetscore)){
91 g.winner = g.p().side;
92 }
93 // Start Engine
94 if (jy == -1){
95 g.p().s = 0.3*GAME_SPEED*GAME_SPEED;
96 g.p().land = 1;
97 if ((g.p().control) > 0){
98 g.sound.volume(g.p().control-1, 0.0);
99 g.sound.loop(g.p().control-1, g.p().enginesample);
100 }
101 }
102 break;
103
104 case 1: // Taking off plane
105 if (jy == -1){
106 g.p().s = dlimit(g.p().s + 0.3*GAME_SPEED*GAME_SPEED, 0.0,
107 6.0*GAME_SPEED);
108 }
109 // Take off plane
110 if ((jx == -1) && (g.p().s > 2.0*GAME_SPEED) &&
111 (g.base[g.p().side].planed == 13)){
112 g.p().d++;
113 g.p().rotate = g.p().maxrotate;
114 g.p().land = 2;
115 }
116 if ((jx == 1) && (g.p().s > 2.0*GAME_SPEED) &&
117 (g.base[g.p().side].planed == 5)){
118 g.p().d--;
119 g.p().rotate = g.p().maxrotate;
120 g.p().land = 2;
121 }
122 // Off end of runway
123 if (abs(int(g.p().x - g.base[g.p().side].planex)) >
124 g.base[g.p().side].runwaylength){
125 g.p().land = 2;
126 }
127 break;
128
129 case 2: // flying
130 // Navigate plane
131 if ((g.p().rotate == 0) && (jx !=0)){
132 g.p().d = wrap(g.p().d-jx,1,17);
133 g.p().rotate = g.p().maxrotate;
134 }else{
135 if (g.p().rotate > 0){
136 g.p().rotate--;
137 }
138 }
139 // Acceleration / Afterburner Controls
140 {
141 double acceleration = g.accel[g.p().d] * GAME_SPEED * GAME_SPEED;
142 if (g.p().burner){
143 if (jy == -1){
144 acceleration += 0.3*GAME_SPEED*GAME_SPEED;
145 g.p().boost = true;
146 }
147 if ((g.p().s > 6.0*GAME_SPEED) && (jy != -1)){
148 acceleration -= 0.3*GAME_SPEED*GAME_SPEED;
149 }
150 g.p().s = dlimit(g.p().s + acceleration, 0.0, 12.0*GAME_SPEED);
151 }else{
152 g.p().s = dlimit(g.p().s + acceleration, 0.0, 6.0*GAME_SPEED);
153 }
154 }
155 // Stealth Controls
156 if ((jy == -1) && (jx == 0) && (!jb) && (g.p().stealth)){
157 g.p().hide = true;
158 }
159 // Check for shotfire
160 if (g.p().shotdelay == 0){
161 if ((jb) && (g.p().ammo > 0)){
162 fire_shot(g.p(), g.shot, g.sound, g.xmove, g.ymove);
163 }
164 // Check for bombdrop
165 if ((jy == 1) && (g.p().bombs > 0)){
166 drop_bomb(g.p(), g.fall, g.sound, g.bombimage);
167 }
168 }else{
169 g.p().shotdelay--;
170 }
171 break;
172
173 case 3: // Landing plane
174 if ((((g.p().x - g.base[g.p().side].planex) < 2.0) &&
175 (g.base[g.p().side].planed == 13)) ||
176 (((g.p().x - g.base[g.p().side].planex) > -2.0) &&
177 (g.base[g.p().side].planed == 5))){
178 g.p().land = 0;
179 g.p().x = g.base[g.p().side].planex;
180 g.p().y = g.base[g.p().side].planey;
181 g.p().d = g.base[g.p().side].planed;
182 g.p().xs = 0;
183 g.p().ys = 0;
184 g.p().s = 0;
185 g.p().ammo = g.p().maxammo;
186 g.p().bombs = g.p().maxbombs;
187 g.p().coms = 0;
188 g.p().targetx = 0;
189 g.p().targety = 0;
190 g.p().cruiseheight = 0;
191 }
192 break;
193 }
194 // Set speed for planes
195 g.p().xs = g.p().s * g.xmove[g.p().d];
196 g.p().ys = g.p().s * g.ymove[g.p().d];
197 // Check for stall
198 if ((g.p().s < 1.0*GAME_SPEED) && (g.p().land == 2)){
199 g.p().state = 1;
200 if ((g.p().control) > 0){
201 g.sound.stop(g.p().control-1);
202 g.sound.play(SOUND_STALL);
203 }
204 }
205 if (g.p().y < 0){
206 g.p().y = 0;
207 g.p().ys = 0;
208 g.p().state = 1;
209 if ((g.p().control) > 0){
210 g.sound.stop(g.p().control-1);
211 g.sound.play(SOUND_STALL);
212 }
213 }
214 break;
215
216 case 1: // Stalling planes
217 // Navigate plane
218 if ((g.p().rotate == 0) && (jx !=0)){
219 g.p().d = wrap(g.p().d-jx,1,17);
220 g.p().rotate = g.p().maxrotate;
221 }else{
222 if (g.p().rotate > 0){
223 g.p().rotate--;
224 }
225 }
226 // Check for shotfire
227 if (g.p().shotdelay == 0){
228 if ((jb) && (g.p().ammo > 0)){
229 fire_shot(g.p(), g.shot, g.sound, g.xmove, g.ymove);
230 }
231 // Check for bombdrop
232 if ((jy == 1) && (g.p().bombs > 0)){
233 drop_bomb(g.p(), g.fall, g.sound, g.bombimage);
234 }
235 }else{
236 g.p().shotdelay--;
237 }
238 // Gravity and drag
239 g.p().ys += 0.1 * GAME_SPEED * GAME_SPEED;
240 if (fabs(g.p().xs) > 0.02 * GAME_SPEED * GAME_SPEED){
241 g.p().xs -= g.p().xs / fabs(g.p().xs) * 0.02 * GAME_SPEED * GAME_SPEED;
242 }
243 // Recover from Stall
244 if ((g.p().ys > 3.0 * GAME_SPEED) && (g.p().d == 9)){
245 g.p().s = dlimit(g.p().ys, 3.0*GAME_SPEED, 6.0*GAME_SPEED);
246 g.p().state = 0;
247 g.p().xs = g.p().s * g.xmove[g.p().d];
248 g.p().ys = g.p().s * g.ymove[g.p().d];
249 g.p().coms = 0;
250 if ((g.p().control) > 0){
251 double volume = g.p().s / (6.0*GAME_SPEED);
252 g.sound.volume(g.p().control-1, volume * 0.5);
253 g.sound.loop(g.p().control-1, g.p().enginesample);
254 }
255 }
256 break;
257
258 case 2: // Dead planes
259 // Gravity and drag
260 g.p().ys += 0.1 * GAME_SPEED * GAME_SPEED;
261 if (fabs(g.p().xs) > 0.02 * GAME_SPEED * GAME_SPEED){
262 g.p().xs -= g.p().xs/ fabs(g.p().xs) * 0.02 * GAME_SPEED * GAME_SPEED;
263 }
264 // Smoking plane
265 g.p().crash++;
266 if (g.p().crash == int(5/GAME_SPEED)){
267 g.p().crash = 0;
268 smoketype newsmoke;
269 newsmoke.x = int(g.p().x);
270 newsmoke.y = g.p().y;
271 newsmoke.time = 0;
272 g.smoke.add(newsmoke);
273 }
274 break;
275
276 case 3: // Crashed planes
277 g.p().crash++;
278 if (g.p().crash == int(70/GAME_SPEED)){
279 if (!g.p().drak){
280 // Respawn plane to runway
281 g.p().land = 0;
282 g.p().state = 0;
283 g.p().rotate = 0;
284 g.p().crash = 0;
285 g.p().x = g.base[g.p().side].planex;
286 g.p().y = g.base[g.p().side].planey;
287 g.p().d = g.base[g.p().side].planed;
288 g.p().xs = 0;
289 g.p().ys = 0;
290 g.p().s = 0;
291 g.p().ammo = g.p().maxammo;
292 g.p().bombs = g.p().maxbombs;
293 g.p().coms = 0;
294 g.p().targetx = 0;
295 g.p().targety = 0;
296 g.p().cruiseheight = 0;
297 }else{
298 // Expunge drak
299 g.p().state = 4;
300 }
301 }
302 break;
303
304 case 4: // Expunged drak fighter (NB: shouldn't get here!)
305 break;
306
307
308 }
309 // Move the planes
310 g.p().x += g.p().xs;
311 g.p().y += g.p().ys;
312
313 // Control Engine Volume
314 if ((g.p().control) > 0){
315 if ((g.p().state == 0) && (g.p().land > 0)){
316 double volume = g.p().s / (6.0*GAME_SPEED);
317 g.sound.volume(g.p().control-1, volume * 0.5);
318 if ((g.p().boost) && (g.p().enginesample == SOUND_JET)){
319 g.p().enginesample = SOUND_BURNER;
320 g.sound.loop(g.p().control-1,SOUND_BURNER);
321 }
322 if ((!g.p().boost) && (g.p().enginesample == SOUND_BURNER)){
323 g.p().enginesample = SOUND_JET;
324 g.sound.loop(g.p().control-1,SOUND_JET);
325 }
326 }else{
327 g.sound.stop(g.p().control-1);
328 }
329 }
330
331 if (g.p().state < 3) detect_collisions(g);
332
333 }
334
335 // Actual keyboard detection
336
keyboard(Uint8 * keys,int & jx,int & jy,bool & jb,SDLKey up,SDLKey down,SDLKey left,SDLKey right,SDLKey fire)337 void keyboard(Uint8 *keys, int &jx, int &jy, bool &jb, SDLKey up, SDLKey down,
338 SDLKey left, SDLKey right, SDLKey fire){
339
340 if (keys[left]){
341 jx = -1;
342 }
343 if (keys[right]){
344 jx = 1;
345 }
346 if (keys[up]){
347 jy = -1;
348 }
349 if (keys[down]){
350 jy = 1;
351 }
352 if (keys[fire]){
353 jb = true;
354 }
355
356 }
357
358 // Plane control routine
359 // Returns control in arguments jx,jy,jb
360
control(gamedata & g,Uint8 * keys,int & jx,int & jy,bool & jb)361 void control(gamedata &g, Uint8 *keys, int &jx, int &jy, bool &jb){
362
363 switch(g.p().control){
364 case 1: // Player 1
365 keyboard(keys, jx, jy, jb, SDLK_UP, SDLK_DOWN,
366 SDLK_LEFT, SDLK_RIGHT, SDLK_RETURN);
367 break;
368 case 2: // Player 2
369 keyboard(keys, jx, jy, jb, SDLK_s, SDLK_x,
370 SDLK_z, SDLK_c, SDLK_LCTRL);
371 break;
372 default: // Computer controlled
373 computer_ai(g, g.p(), jx, jy, jb);
374 break;
375 }
376
377 }
378
379 // Calculate time pause to stabilize framerate
380
time_left(Uint32 next_time)381 Uint32 time_left(Uint32 next_time){
382
383 Uint32 now = SDL_GetTicks();
384 if(next_time <= now)
385 return 0;
386 else
387 return next_time - now;
388 }
389
390
391 // Main game loop
392
game(gamedata & g)393 void game (gamedata &g){
394
395 SDL_Event event;
396 bool quit = false;
397
398 // Set first tick interval
399 Uint32 next_time = SDL_GetTicks() + TICK_INTERVAL;
400
401 while (!quit && (g.winner == 0)){
402
403 Uint8 *keys = SDL_GetKeyState(NULL);
404 if (keys[SDLK_ESCAPE] == SDL_PRESSED){
405 quit = true;
406 }
407
408 if (SDL_PollEvent(&event) == 1){
409 switch (event.type){
410
411 case SDL_QUIT: {
412 printf("Quit requested, quitting.\n");
413 quit = true;
414 }
415 break;
416 }
417 }
418
419 // Go through each plane individually
420 g.p.reset();
421 while (g.p.next()){
422
423 // Setup virtual joystick
424 int jx = 0;
425
426 int jy = 0;
427 bool jb = false;
428
429 if (g.p().state < 2){
430 control(g, keys, jx, jy, jb);
431 }
432 // Then move the plane
433 act(g, jx, jy, jb);
434 // Remove expunged planes
435 if (g.p().state == 4){
436 g.drakms.fighter[g.p().id-7] = false;
437 g.drakms.fightersout--;
438 g.dp.reset();
439 while(g.dp.next()){
440 if (g.dp().id == g.p().id) g.dp.kill();
441 }
442 g.p.kill();
443 }
444
445 }
446
447 // Then do everything else
448 all(g);
449 drawall(g);
450
451 // Delay for time remaining in TICK_INTERVAL
452 SDL_Delay(time_left(next_time));
453 next_time = SDL_GetTicks() + TICK_INTERVAL;
454
455 }
456 }
457
458