1 // Apricots computer intelligence routine
2 // Author: M.D.Snellgrove
3 // Date: 25/3/2002
4 // History:
5 
6 #include "apricots.h"
7 
8 // Follow target routine
9 
followtarget(plane & p,int & jx,int & jy,int rx,int ry,bool reverse)10 void followtarget(plane &p, int &jx, int &jy, int rx, int ry, bool reverse){
11   int targetd = 0;
12   if (abs(rx) > 150){
13 // Long distance cruising
14     targetd = wrap(9 - 5*sign(rx) - p.d, -7, 9);
15     if (p.y < p.cruiseheight) targetd = wrap(9 - 4*sign(rx) - p.d, -7, 9);
16     if (p.y < GAME_HEIGHT - 160){
17       if ((rx > 0) && (p.d == 13)){
18         targetd = -1;
19         p.coms = 9;
20       }
21       if ((rx < 0) && (p.d == 5)){
22         targetd = 1;
23         p.coms = 8;
24       }
25     }
26 // Afterburner if available
27     if ((targetd == 0) && (p.burner) && (abs(rx) > 200)) jy = -1;
28   }else{
29 // Track target directly
30      targetd = wrap(int((atan2(double(ry),double(-rx))/PI*8)+13.5) - p.d, -7, 9);
31 // Go other way if reverse flag set
32      if (reverse) targetd = wrap(targetd+8, -7, 9);
33   }
34 // Equilibrize
35   if (targetd == 8) targetd = int(drand()*2.0)*16 - 8;
36 // Choose direction
37   jx = -sign(targetd);
38 
39 }
40 
41 // Main Computer AI routine
42 
computer_ai(gamedata & g,plane & p,int & jx,int & jy,bool & jb)43 void computer_ai(gamedata &g, plane &p, int &jx, int &jy, bool &jb){
44 
45   switch(g.p().land){
46     case 0: // Landed Plane (takes off)
47       jy = -1;
48       break;
49 
50     case 1: // Taking off plane (accelerates)
51       jy = -1;
52       if (p.s > 5.0 * GAME_SPEED){
53         jx = sign(9-p.d);
54       }
55       break;
56 
57     case 2: // Flying plane
58       if (p.rotate == 0){
59         if ((p.coms == 0) && (p.state == 0)){
60 // Stall Avoidance
61           if (((p.y < 24.0) && (p.ys < 0.0)) || (p.s < 3.0*GAME_SPEED)) p.coms = 4;
62 // Drak mothership avoidance
63           if (g.drakms.exist == 1){
64             double dx = p.x - g.drakms.x + (p.xs - g.drakms.xs)*4.0/GAME_SPEED;
65             if ((dx > -40.0) && (dx < 136.0)){
66               if ((p.y > 46.0) && (p.y < 106.0) &&
67                   ((p.ys < 0.0) || (p.y < 86.0))) p.coms = 4;
68               if ((p.y <= 46.0) && (p.ys >= 0.0) &&
69                   ((p.ys > 0.0) || (p.y > 26.0))) p.coms = 1;
70             }
71           }
72 // Groundheight tracking
73           int px = int(p.x + 8)/16;
74           if (((p.d == 5) || (p.d == 13)) && ((p.y+20.0 > g.gamemap.steepheight[px]) ||
75                (p.y+20.0 > g.gamemap.steepheight[px+1]))) p.coms = 1;
76           if ((p.d > 2) && (p.d < 8)) px = limit(px-1, 0, MAP_W*2-1);
77           if ((p.d > 10) && (p.d < 16)) px = limit(px+1, 0, MAP_W*2-1);
78           if (p.ys < 0.0){
79             if ((p.y+5.0 > g.gamemap.smoothheight[px]) ||
80                 (p.y+5.0 > g.gamemap.smoothheight[px+1])) p.coms = 1;
81 
82           }else{
83             if ((p.y+40.0 > g.gamemap.smoothheight[px]) ||
84                 (p.y+40.0 > g.gamemap.smoothheight[px+1])) p.coms = 1;
85           }
86           if ((p.y-40.0 > g.gamemap.realheight[px]) ||
87               (p.y-40.0 > g.gamemap.realheight[px+1])) p.coms = 6;
88 // Landing runway approach
89           if (p.targetx == -10){
90             int dx = int(p.x) - 32*g.base[p.side].mapx - g.base[p.side].runwayx;
91             if ((dx > 10) && (dx < -10 + g.base[p.side].runwaylength) &&
92                 ((p.d == 6) || (p.d == 7) || (p.d == 11) || (p.d == 12))){
93               p.coms = 0;
94               if (g.base[p.side].planey - int(p.y) < 50) p.coms = 11;
95             }
96           }
97 // Side of map avoidance
98           if (int(p.x) < 100) p.coms = 2;
99           if (int(p.x) > GAME_WIDTH - 116) p.coms = 3;
100         }
101 // Recover from stall
102         if (p.state == 1) p.coms = 5;
103 // Choose action depending on coms
104         switch (p.coms){
105           case 0: // Action!
106 // Land if out of shots and bombs
107             if ((p.ammo == 0) && (p.bombs == 0)) p.targetx = -10;
108 // Land if mission is done
109             if ((g.mission < 2) && (p.score >= g.targetscore)) p.targetx = -10;
110             if ((g.mission == 2) && (p.targetscore == 0)) p.targetx = -10;
111 // Drak fighters don't land
112             if ((p.targetx == -10) && (p.drak)) p.targetx = 0;
113 // Check for gunthreat and change target if target not already a gun
114             if ((p.gunthreat > 0) && (p.bombs > 0)){
115               if (p.targetx > 0){
116                 if (g.gamemap.b[p.targetx].type != 5){
117                   p.targetx = p.gunthreat;
118                   p.targety = g.gamemap.b[p.targetx].y;
119                 }
120               }else{
121                 p.targetx = p.gunthreat;
122                 p.targety = g.gamemap.b[p.targetx].y;
123               }
124             }
125 // Find target if no target exists
126             if (p.targetx == 0){
127 // Try a building
128               p.targetx = int(drand()*(MAP_W*2-3))+2;
129               if ((g.gamemap.b[p.targetx].type < 3) || (g.gamemap.b[p.targetx].points < 0) ||
130                   (g.gamemap.b[p.targetx].side == p.side) || (p.bombs == 0) ||
131                   ((g.gamemap.b[p.targetx].side == 0) && (g.mission == 2))){
132 // Building unsuitable target so try plane instead
133                 p.targetx = 0;
134                 int tryplaneid = int(drand()*g.planes) + 1;
135                 if ((((g.mission != 2) && (int(drand()*2) == 1)) || (int(drand()*30) == 1) ||
136                      (p.drak)) && (p.ammo > 0)){
137                   g.dp.reset();
138                   while (g.dp.next()){
139 // Check if plane is suitable target and select if so
140                     if ((g.dp().id == tryplaneid) && (g.dp().side != p.side) &&
141                         (g.dp().state < 2) && (!g.dp().hide)) p.targetx = -tryplaneid;
142                   }
143                 }
144               }else{
145 // Building suitable so becomes target
146                 p.targety = g.gamemap.b[p.targetx].y;
147               }
148 // Set new cruiseheight if changed target
149               if (p.cruiseheight == 0) p.cruiseheight = 20 + int(drand()*(GAME_HEIGHT-176));
150               if (p.targetx == 0){
151 // Chase false target
152                 if (p.xs > 0){
153                   followtarget(p, jx, jy, -160, 0, false);
154                 }else{
155                   followtarget(p, jx, jy, 160, 0, false);
156                 }
157 // If still no target for too long then land
158                 p.targety++;
159                 if (p.targety == int(200/GAME_SPEED)) p.targetx = -10;
160               }
161             }
162 // Target is building
163             if (p.targetx > 0){
164               int rx = int(p.x) - 16*p.targetx + 8;
165               int ry = int(p.y) + 40 - p.targety;
166               followtarget(p, jx, jy, rx, ry,
167                            ((abs(rx) < 110) && (g.gamemap.b[p.targetx].type == 5)));
168 // Check to see if can still bomb the building
169               if ((g.gamemap.b[p.targetx].type == 0) || (p.bombs == 0)){
170                 p.targetx = 0;
171                 p.targety = 0;
172                 p.cruiseheight = 0;
173               }
174             }
175 // Target is plane
176             if ((p.targetx < 0) && (p.targetx > -10)){
177               g.dp.reset();
178               while (g.dp.next()){
179                 if (g.dp().id == -p.targetx){
180 // Check to see if can still shoot plane
181                   if ((g.dp().state > 1) || (p.ammo == 0) || ((g.dp().hide) &&
182                        (int(drand()*40) == 1))){
183                     p.targetx = 0;
184                     p.targety = 0;
185                     p.cruiseheight = 0;
186                   }else{
187                     int rx = int(p.x - g.dp().x);
188                     int ry = int(p.y - g.dp().y);
189                     followtarget(p, jx, jy, rx, ry, (rx*rx + ry*ry < 2500));
190                   }
191                 }
192               }
193             }
194 // Target is runway
195             if (p.targetx == -10){
196               int rx = int(p.x) - 32*g.base[p.side].mapx - g.base[p.side].runwayx
197                                 - g.base[p.side].runwaylength/2;
198               int ry = int(p.y) -g.base[p.side].planey;
199               followtarget(p, jx, jy, rx, ry, false);
200             }
201             break;
202 
203           case 1: // Go upwards
204             if ((p.d > 3) && (p.d < 9)) jx = 1;
205             if ((p.d > 9) && (p.d < 15)) jx = -1;
206             if (p.d == 9){
207               if (int(p.x) < 100) jx = -1;
208               if (int(p.x) > GAME_WIDTH - 116) jx = 1;
209               if (jx == 0){
210                 int px = int(p.x+8)/16 - 1;
211                 jx = sign(g.gamemap.smoothheight[px] - g.gamemap.smoothheight[px+3]);
212                 if (jx == 0) jx = int(drand()*2.0)*2 -1;
213               }
214             }
215             p.coms = 0;
216             break;
217 
218           case 2: // Go right
219             if ((p.d == 1) || (p.d > 13)) jx = 1;
220             if ((p.d > 7) && (p.d < 13)) jx = -1;
221             if ((p.d < 8) && (p.d > 1)){
222               jx = 1;
223               if (p.s < 4.0 * GAME_SPEED) jx = -1;
224               if (int(p.y) < 34) jx = -1;
225               int px = int(p.x+8)/16;
226               if ((int(p.y)+45 > g.gamemap.realheight[px]) &&
227                   (int(p.y)+45 > g.gamemap.realheight[px+1])) jx = 1;
228             }
229             if (jx == 1) p.coms = 7;
230             if (jx == -1) p.coms = 8;
231             if (p.d == 13) p.coms = 0;
232             break;
233 
234           case 3: // Go left
235             if (p.d < 5) jx = -1;
236             if ((p.d > 5) && (p.d < 11)) jx = 1;
237             if (p.d > 10){
238               jx = -1;
239               if (p.s < 4.0 * GAME_SPEED) jx = 1;
240               if (int(p.y) < 34) jx = 1;
241               int px = int(p.x+8)/16;
242               if ((int(p.y)+45 > g.gamemap.realheight[px]) &&
243                   (int(p.y)+45 > g.gamemap.realheight[px+1])) jx = -1;
244             }
245             if (jx == 1) p.coms = 9;
246             if (jx == -1) p.coms = 10;
247             if (p.d == 5) p.coms = 0;
248             break;
249 
250           case 4: // Level off
251             if (p.d < 8) jx = -1;
252             if (p.d > 10) jx = 1;
253             if (p.d == 1) jx = int(drand()*2.0)*2 -1;
254             if ((p.d > 3) && (p.d < 15)) p.coms = 0;
255             break;
256 
257           case 5: // Go down
258             if (p.d < 9) jx = -1;
259             if (p.d > 9) jx = 1;
260             if (p.d == 1) jx = int(drand()*2.0)*2 -1;
261             if ((p.d == 9) && (p.s > 3.0 * GAME_SPEED)) p.coms = 0;
262             break;
263 
264           case 6: // Go upwards lots
265             if ((p.d > 1) && (p.d < 9)) jx = 1;
266             if (p.d > 9) jx = -1;
267             if (p.d == 9){
268               if (int(p.x) < 100) jx = -1;
269               if (int(p.x) > GAME_WIDTH - 116) jx = 1;
270               if (jx == 0){
271                 int px = int(p.x+8)/16 - 1;
272                 jx = sign(g.gamemap.smoothheight[px] - g.gamemap.smoothheight[px+3]);
273                 if (jx == 0) jx = int(drand()*2.0)*2 -1;
274               }
275             }
276             p.coms = 0;
277             break;
278 
279           case 7: // Go right +ve
280             if (p.d != 13){
281               jx = 1;
282             }else{
283               p.coms = 0;
284             }
285             break;
286 
287           case 8: // Go right -ve
288             if (p.d != 13){
289               jx = -1;
290             }else{
291               p.coms = 0;
292             }
293             break;
294 
295           case 9: // Go left +ve
296             if (p.d != 5){
297               jx = 1;
298             }else{
299               p.coms = 0;
300             }
301             break;
302 
303           case 10: // Go left -ve
304             if (p.d != 5){
305               jx = -1;
306             }else{
307               p.coms = 0;
308             }
309             break;
310 
311           case 11: // Landing
312             if (p.d == 6) jx = -1;
313             if (p.d == 12) jx = 1;
314             int dx = int(p.x) - 32*g.base[p.side].mapx - g.base[p.side].runwayx;
315             if ((dx < 10) || (dx > -10 + g.base[p.side].runwaylength)) p.coms = 0;
316             break;
317 
318         }
319 // Wiggle to avoid gunfire
320         if ((p.gunthreat > 0) && (jx == 0) && (!p.hide)) jx = int(drand()*2.0)*2 - 1;
321       }
322 // Afterburner for jet if speed slow
323       if ((p.burner) && (p.s < 5.0 * GAME_SPEED)) jy = -1;
324 // Stealth ability
325       if (p.stealth) jy = -1;
326       if (p.shotdelay == 0){
327 // Launch bomb
328         if (p.targetx > 0){
329           double rx = p.x - double(16*p.targetx) + 8.0 + p.xs;
330           double ry = p.y - double(p.targety) + 14.0 + p.ys;
331           double a = (p.ys + 2.0*GAME_SPEED)*(p.ys + 2.0*GAME_SPEED)
332 			          - (ry*0.2) * GAME_SPEED * GAME_SPEED;
333           if (a > 0){
334             if (abs(int(rx + p.xs*10.0/(GAME_SPEED*GAME_SPEED)*(-p.ys-2.0*GAME_SPEED+sqrt(a)))) < 6) jy = 1;
335           }
336         }
337 // Fire shot
338         if ((p.s < 8.0*GAME_SPEED) && (p.ammo > 0)){
339           g.dp.reset();
340           while (g.dp.next()){
341             if ((g.dp().side != p.side) && (g.dp().state < 2) &&
342                 (!g.dp().hide)){
343               double rx = p.x - g.dp().x;
344               double ry = p.y - g.dp().y;
345               double d = sqrt(rx*rx + ry*ry);
346               if (d < 100.0){
347                 double smartsine = ((ry*g.dp().xs - rx*g.dp().ys)/(8.0*GAME_SPEED)/d);
348                 smartsine = dlimit(smartsine, -1.0, 1.0);
349                 double smartangle = asin(smartsine);
350                 double angle = atan2(rx,ry);
351                 int bogied = wrap((int(((angle-smartangle)/PI*8.0)+1.5)), 1, 17);
352                 if (bogied == p.d) jb = true;
353               }
354             }
355           }
356         }
357       }
358       break;
359   }
360 
361 }
362