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