1 // Apricots all routine
2 // Author: M.D.Snellgrove
3 // Date: 26/3/2002
4 // History:
5 
6 // Changes by M Snellgrove 6/7/2003
7 // drand() function needed for Windows portability now located here
8 
9 #include "apricots.h"
10 
11 // Random number generator function
12 
drand()13 double drand(){
14 
15   return (((double)(rand() % RAND_MAX)) / ( ((double)RAND_MAX) - 1.0 ));
16 
17 }
18 
19 // Wrap function
20 
wrap(int n,int min,int max)21 int wrap(int n, int min, int max){
22 
23   return ((((n-min) % (max-min)) + max-min) % (max-min)) + min;
24 
25 }
26 
27 // Limit function (integers)
28 
limit(int n,int min,int max)29 int limit(int n, int min, int max){
30 
31   if (n > max) return max;
32   if (n < min) return min;
33   return n;
34 
35 }
36 
37 // Limit function (doubles)
38 
dlimit(double n,double min,double max)39 double dlimit(double n, double min, double max){
40 
41   if (n > max) return max;
42   if (n < min) return min;
43   return n;
44 
45 }
46 
47 // Sign function
48 
sign(int n)49 int sign(int n){
50 
51   if (n > 0) return 1;
52   if (n < 0) return -1;
53   return 0;
54 
55 }
56 
57 // Animate smoke
58 
animate_smoke(linkedlist<smoketype> & smoke)59 void animate_smoke(linkedlist <smoketype> &smoke){
60 
61   smoke.reset();
62   while(smoke.next()){
63     smoke().y = smoke().y - (2.0*GAME_SPEED);
64     smoke().time++;
65     if (smoke().time == int(28/GAME_SPEED)) smoke.kill();
66   }
67 
68 }
69 
70 // Animate flames
71 
animate_flames(linkedlist<firetype> & flame,linkedlist<smoketype> & smoke)72 void animate_flames(linkedlist <firetype> &flame,
73                     linkedlist <smoketype> &smoke){
74 
75   flame.reset();
76   while(flame.next()){
77     flame().time++;
78     if (flame().time % (int(10/GAME_SPEED)) == 0){
79       smoketype newsmoke;
80       newsmoke.x = flame().x;
81       newsmoke.y = flame().y;
82       newsmoke.time = 0;
83       smoke.add(newsmoke);
84     }
85     if (flame().time == int(280/GAME_SPEED)) flame.kill();
86   }
87 
88 }
89 
90 // Animate explosions
91 
animate_explosions(linkedlist<firetype> & explosion)92 void animate_explosions(linkedlist <firetype> &explosion){
93 
94   explosion.reset();
95   while(explosion.next()){
96     explosion().time++;
97     int maxtime = 0;
98     switch(explosion().type){
99       case 0:
100         maxtime = int(14/GAME_SPEED);
101         break;
102       case 1:
103         maxtime = int(22/GAME_SPEED);
104         break;
105       case 2:
106         maxtime = int(4/GAME_SPEED);
107         break;
108       case 3:
109         maxtime = int(20/GAME_SPEED);
110         break;
111       case 4:
112         maxtime = int(5/GAME_SPEED);
113         break;
114     }
115     if (explosion().time == maxtime) explosion.kill();
116   }
117 
118 }
119 
120 // Rotate radars
121 
rotate_radars(linkedlist<radartype> & radar)122 void rotate_radars(linkedlist <radartype> &radar){
123 
124   radar.reset();
125   while(radar.next()){
126     radar().rotate = wrap(radar().rotate+1,0,int(3/GAME_SPEED));
127     if (radar().rotate == 0) radar().image =
128                              wrap(radar().image+1,238,246);
129   }
130 
131 }
132 
133 // Decay lasers
134 
decay_lasers(linkedlist<lasertype> & laser)135 void decay_lasers(linkedlist <lasertype> &laser){
136 
137   laser.reset();
138   while (laser.next()){
139     laser().time--;
140     if (laser().time == 0) laser.kill();
141   }
142 
143 }
144 
145 // Clone planes (for collision detection purposes)
146 
clone_planes(linkedlist<plane> & p,linkedlist<planeclone> & dp,int mission,int & winner)147 void clone_planes(linkedlist <plane> &p, linkedlist <planeclone> &dp,
148                   int mission, int &winner){
149 
150 // Do the cloning
151   p.reset();
152   dp.reset();
153   while (p.next()){
154     dp.next();
155     dp().x = p().x;
156     dp().y = p().y;
157     dp().xs = p().xs;
158     dp().ys = p().ys;
159     dp().d = p().d;
160     dp().state = p().state;
161     dp().hide = p().hide;
162 // Conduct Scoreloss
163     p().score -= dp().scoreloss;
164     dp().scoreloss = 0;
165     p().targetscore = dp().buildingwin;
166 // Check for mission 2 win
167     if ((mission == 2) && (dp().buildingwin == 0) && (p().land == 0) &&
168          (!p().drak)){
169       winner = p().side;
170       p().targetx = -10;
171     }
172   }
173 
174 }
175 
176 // Check for planes colliding with each other
177 
plane_collisions(gamedata & g)178 void plane_collisions(gamedata &g){
179 
180   g.p.reset();
181   while (g.p.next()){
182     g.dp.reset();
183     while (g.dp.next()){
184       if (g.p().id > g.dp().id){
185         if ((g.p().state<3) && (g.dp().state<3) &&
186             ((g.p().state<2) || (g.dp().state<2))){
187           if (g.images[g.p().image+g.p().d].collide(int(g.p().x),int(g.p().y),
188              g.images[g.dp().image+g.dp().d],int(g.dp().x),int(g.dp().y))){
189 // Planes have collided
190              if (g.p().state < 2) g.p().score -= 25;
191              g.p().state = 2;
192              g.p().land = 2;
193              g.p().xs = g.p().xs * 0.5;
194              g.p().ys = g.p().ys * 0.5;
195              g.p().s = 0.0;
196              g.dp().collide = true;
197              firetype bang;
198              bang.x = int(g.p().x);
199              bang.y = int(g.p().y);
200              bang.type = 0;
201              bang.time = 0;
202              g.explosion.add(bang);
203              falltype shrapnel;
204              shrapnel.x = g.p().x + 6.0;
205              shrapnel.y = g.p().y;
206              shrapnel.xs = g.p().xs;
207              shrapnel.ys = g.p().ys;
208              shrapnel.image = g.p().shrapnelimage + int(drand()*3);
209              shrapnel.type = 1;
210              g.fall.add(shrapnel);
211              g.sound.play(SOUND_EXPLODE);
212           }
213         }
214       }
215     }
216   }
217 
218 // Check to see if plane is already hit by another
219   g.p.reset();
220   g.dp.reset();
221   while (g.p.next()){
222     g.dp.next();
223     if (g.dp().collide){
224       if (g.p().state < 2) g.p().score -= 25;
225       g.p().state = 2;
226       g.p().land = 2;
227       g.p().xs = g.p().xs * 0.5;
228       g.p().ys = g.p().ys * 0.5;
229       g.p().s = 0.0;
230       g.dp().collide = false;
231       falltype shrapnel;
232       shrapnel.x = g.p().x + 6.0;
233       shrapnel.y = g.p().y;
234       shrapnel.xs = g.p().xs;
235       shrapnel.ys = g.p().ys;
236       shrapnel.image = g.p().shrapnelimage + int(drand()*3);
237       shrapnel.type = 1;
238       g.fall.add(shrapnel);
239     }
240   }
241 
242 }
243 
244 // Move falls
245 
move_falls(gamedata & g)246 void move_falls(gamedata &g){
247 
248   g.fall.reset();
249   while(g.fall.next()){
250     g.fall().ys += 0.1 * GAME_SPEED * GAME_SPEED;
251     g.fall().x += g.fall().xs;
252     g.fall().y += g.fall().ys;
253     if (g.fall().type == 3){
254       if (g.fall().rotatedelay == 0){
255         g.fall().image = wrap(g.fall().image+g.fall().bombrotate,115,123);
256         g.fall().rotatedelay = int(1/GAME_SPEED);
257       }else{
258         g.fall().rotatedelay--;
259       }
260     }
261     if (fall_collision(g, g.fall())){
262       g.fall.kill();
263     }
264 
265   }
266 
267 }
268 
269 // Move shots
270 
move_shots(linkedlist<shottype> & shot,shape & ground,shape & shotimage,shape & drakmsimage,drakmstype & drakms)271 void move_shots(linkedlist <shottype> &shot, shape &ground, shape &shotimage,
272                 shape &drakmsimage, drakmstype &drakms){
273 
274   shot.reset();
275   while (shot.next()){
276 // move
277     shot().x += shot().xs;
278     shot().y += shot().ys;
279     shot().time--;
280 // ground collisions / off screen / timed out / drakms collision
281     if ((ground.collide(0, 0, shotimage, (int)shot().x, (int)shot().y)) ||
282         (shot().x < -16.0) || (shot().x > GAME_WIDTH) || (shot().y < -16.0) ||
283         (shot().y > GAME_HEIGHT) || (shot().time == 0) ||
284         ((drakms.exist == 1) && (drakmsimage.collide((int)drakms.x,(int)drakms.y,
285                                    shotimage,(int)shot().x,(int)shot().y)))){
286       shot.kill();
287     }
288   }
289 
290 }
291 
292 // Create shot from gun
293 
gunshoot(guntype & gun,linkedlist<shottype> & shot,sampleio & sound,double xmove[17],double ymove[17])294 void gunshoot(guntype &gun, linkedlist <shottype> &shot, sampleio &sound,
295               double xmove[17], double ymove[17]){
296 
297   int d = ((20-gun.d) % 16) + 1;
298   shottype shell;
299   shell.x = gun.x+6.0+12.0*xmove[d];
300   shell.y = gun.y+10.0+12.0*ymove[d];
301   shell.xs = 8.0*xmove[d]*GAME_SPEED;
302   shell.ys = 8.0*ymove[d]*GAME_SPEED;
303   shell.side = gun.side;
304   shell.time = int(40/GAME_SPEED);
305   shot.add(shell);
306   gun.ammo--;
307   gun.firedelay = int(3/GAME_SPEED);
308   sound.play(SOUND_GUNSHOT);
309 
310 }
311 
312 // Fire guns
fire_guns(linkedlist<guntype> & gun,linkedlist<plane> & p,sampleio & sound,building b[MAP_W * 2],linkedlist<shottype> & shot,double xmove[17],double ymove[17])313 void fire_guns(linkedlist <guntype> &gun, linkedlist <plane> &p, sampleio &sound,
314                building b[MAP_W*2], linkedlist <shottype> &shot,
315                double xmove[17], double ymove[17]){
316 
317 
318   gun.reset();
319   while (gun.next()){
320 // Find new target for gun if untargeted
321     if (gun().target == 0){
322       int maxfind = 200;
323       p.reset();
324       while (p.next()){
325         if ((gun().side != p().side) && (p().state < 2) && (p().land==2) &&
326             (gun().y > p().y) && (!p().hide)){
327           int dx = abs(gun().x - int(p().x));
328           if (dx < maxfind){
329             maxfind = dx;
330             gun().target = p().id;
331           }
332         }
333       }
334     }else{
335 // Track the target of the gun
336       p.reset();
337       while (p.next()){
338         if (p().id == gun().target){
339           p().gunthreat = gun().xpos;
340           double dx = p().x - double(gun().x);
341           double dy = double(gun().y) - p().y;
342 // Rotate gun
343           if ((gun().rotate == 0) && (dy > 0)){
344             double smartsine = (dy*p().xs+dx*p().ys)/
345                                (8.0*GAME_SPEED*sqrt(dx*dx+dy*dy));
346             smartsine = dlimit(smartsine, -1.0, 1.0);
347             double smartangle = asin(smartsine);
348             int move = sign(int(((atan(dx/dy)+smartangle)*8.0/PI)+4.5)-gun().d);
349             if (move != 0){
350 
351               gun().d = limit(gun().d+move,1,7);
352               b[gun().xpos].image = 162 + gun().d;
353               gun().rotate = int(2/GAME_SPEED);
354             }
355           }
356 // Shoot gun
357           if ((gun().firedelay == 0) && (gun().ammo > 0) && (abs(int(dx))<150)){
358             gunshoot(gun(), shot, sound, xmove, ymove);
359           }
360 // Lose target
361 
362           if ((abs(int(dx))>200) || (p().state>1) || (p().hide) ||
363               (p().land !=2)){
364             gun().target = 0;
365           }
366         }
367       }
368     }
369 // Sort out guntimers
370     if (gun().firedelay > 0) gun().firedelay--;
371     if (gun().rotate > 0) gun().rotate--;
372     if (gun().ammo < 3){
373       gun().reload++;
374       if (gun().reload == int(20/GAME_SPEED)){
375         gun().reload = 0;
376         gun().ammo++;
377       }
378     }
379   }
380 
381 }
382 // Do everything
383 
all(gamedata & g)384 void all(gamedata &g){
385 
386   rotate_radars(g.radar);
387   animate_smoke(g.smoke);
388   animate_flames(g.flame, g.smoke);
389   animate_explosions(g.explosion);
390   decay_lasers(g.laser);
391   clone_planes(g.p, g.dp, g.mission, g.winner);
392   plane_collisions(g);
393   move_falls(g);
394   move_shots(g.shot,g.gamemap.ground,g.images[114],g.images[318],g.drakms);
395   fire_guns(g.gun, g.p, g.sound, g.gamemap.b, g.shot, g.xmove, g.ymove);
396   if (g.drak) drak_main(g);
397 
398 }
399