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