1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "colors.h"
20 #include "collide.h"
21 #include "element.h"
22 #include "ship.h"
23 #include "globdata.h"
24 #include "intel.h"
25 #include "setup.h"
26 #include "units.h"
27 #include "libs/mathlib.h"
28 #include "libs/log.h"
29
30 //#define DEBUG_CYBORG
31
32 COUNT
PlotIntercept(ELEMENT * ElementPtr0,ELEMENT * ElementPtr1,COUNT max_turns,COUNT margin_of_error)33 PlotIntercept (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1,
34 COUNT max_turns, COUNT margin_of_error)
35 {
36 SIZE dy;
37 SIZE time_y_0, time_y_1;
38 POINT dst[2];
39 RECT r0 = {{0, 0}, {0, 0}};
40 RECT r1 = {{0, 0}, {0, 0}};
41 SIZE dx_0, dy_0, dx_1, dy_1;
42
43 if ((ElementPtr0->state_flags | ElementPtr1->state_flags) & FINITE_LIFE)
44 {
45 if (!(ElementPtr0->state_flags & FINITE_LIFE))
46 {
47 if (ElementPtr1->life_span < max_turns)
48 max_turns = ElementPtr1->life_span;
49 }
50 else if (!(ElementPtr1->state_flags & FINITE_LIFE))
51 {
52 if (ElementPtr0->life_span < max_turns)
53 max_turns = ElementPtr0->life_span;
54 }
55 else
56 {
57 if (ElementPtr0->life_span < max_turns)
58 max_turns = ElementPtr0->life_span;
59 if (ElementPtr1->life_span < max_turns)
60 max_turns = ElementPtr1->life_span;
61 }
62 }
63
64 dst[0] = ElementPtr0->current.location;
65 GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx_0, &dy_0);
66 dx_0 = (SIZE)VELOCITY_TO_WORLD ((long)dx_0 * (long)max_turns);
67 dy_0 = (SIZE)VELOCITY_TO_WORLD ((long)dy_0 * (long)max_turns);
68
69 dst[1] = ElementPtr1->current.location;
70 GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx_1, &dy_1);
71 dx_1 = (SIZE)VELOCITY_TO_WORLD ((long)dx_1 * (long)max_turns);
72 dy_1 = (SIZE)VELOCITY_TO_WORLD ((long)dy_1 * (long)max_turns);
73
74 if (margin_of_error)
75 {
76 dst[1].y -= margin_of_error;
77 time_y_0 = 1;
78 time_y_1 = margin_of_error << 1;
79 }
80 else
81 {
82 GetFrameRect (ElementPtr0->IntersectControl.IntersectStamp.frame, &r0);
83 GetFrameRect (ElementPtr1->IntersectControl.IntersectStamp.frame, &r1);
84
85 dst[0].y += DISPLAY_TO_WORLD (r0.corner.y);
86 dst[1].y += DISPLAY_TO_WORLD (r1.corner.y);
87 time_y_0 = DISPLAY_TO_WORLD (r0.extent.height);
88 time_y_1 = DISPLAY_TO_WORLD (r1.extent.height);
89 }
90
91 dy = dst[1].y - dst[0].y;
92 time_y_0 = dy - time_y_0 + 1;
93 time_y_1 = dy + time_y_1 - 1;
94 dy = dy_0 - dy_1;
95
96 if ((time_y_0 <= 0 && time_y_1 >= 0)
97 || (time_y_0 > 0 && dy >= time_y_0)
98 || (time_y_1 < 0 && dy <= time_y_1))
99 {
100 SIZE dx;
101 SIZE time_x_0, time_x_1;
102
103 if (margin_of_error)
104 {
105 dst[1].x -= margin_of_error;
106 time_x_0 = 1;
107 time_x_1 = margin_of_error << 1;
108 }
109 else
110 {
111 dst[0].x += DISPLAY_TO_WORLD (r0.corner.x);
112 dst[1].x += DISPLAY_TO_WORLD (r1.corner.x);
113 time_x_0 = DISPLAY_TO_WORLD (r0.extent.width);
114 time_x_1 = DISPLAY_TO_WORLD (r1.extent.width);
115 }
116
117 dx = dst[1].x - dst[0].x;
118 time_x_0 = dx - time_x_0 + 1;
119 time_x_1 = dx + time_x_1 - 1;
120 dx = dx_0 - dx_1;
121
122 if ((time_x_0 <= 0 && time_x_1 >= 0)
123 || (time_x_0 > 0 && dx >= time_x_0)
124 || (time_x_1 < 0 && dx <= time_x_1))
125 {
126 if (dx == 0 && dy == 0)
127 time_y_0 = time_y_1 = 0;
128 else
129 {
130 SIZE t;
131 long time_beg, time_end, fract;
132
133 if (time_y_1 < 0)
134 {
135 t = time_y_0;
136 time_y_0 = -time_y_1;
137 time_y_1 = -t;
138 }
139 else if (time_y_0 <= 0)
140 {
141 if (dy < 0)
142 time_y_1 = -time_y_0;
143 time_y_0 = 0;
144 }
145 if (dy < 0)
146 dy = -dy;
147 if (dy < time_y_1)
148 time_y_1 = dy;
149
150 if (time_x_1 < 0)
151 {
152 t = time_x_0;
153 time_x_0 = -time_x_1;
154 time_x_1 = -t;
155 }
156 else if (time_x_0 <= 0)
157 {
158 if (dx < 0)
159 time_x_1 = -time_x_0;
160 time_x_0 = 0;
161 }
162 if (dx < 0)
163 dx = -dx;
164 if (dx < time_x_1)
165 time_x_1 = dx;
166
167 if (dx == 0)
168 {
169 time_beg = time_y_0;
170 time_end = time_y_1;
171 fract = dy;
172 }
173 else if (dy == 0)
174 {
175 time_beg = time_x_0;
176 time_end = time_x_1;
177 fract = dx;
178 }
179 else
180 {
181 long time_x, time_y;
182
183 time_x = (long)time_x_0 * (long)dy;
184 time_y = (long)time_y_0 * (long)dx;
185 time_beg = time_x < time_y ? time_y : time_x;
186
187 time_x = (long)time_x_1 * (long)dy;
188 time_y = (long)time_y_1 * (long)dx;
189 time_end = time_x > time_y ? time_y : time_x;
190
191 fract = (long)dx * (long)dy;
192 }
193
194 if ((time_beg *= max_turns) < fract)
195 time_y_0 = 0;
196 else
197 time_y_0 = (SIZE)(time_beg / fract);
198
199 if (time_end >= fract) /* just in case of overflow */
200 time_y_1 = max_turns - 1;
201 else
202 time_y_1 = (SIZE)((time_end * max_turns) / fract);
203 }
204
205 if (time_y_0 <= time_y_1)
206 {
207 if (margin_of_error != 0)
208 return ((COUNT)time_y_0 + 1);
209 else
210 {
211 POINT Pt0, Pt1;
212 VELOCITY_DESC Velocity0, Velocity1;
213 INTERSECT_CONTROL Control0, Control1;
214
215 Pt0 = ElementPtr0->current.location;
216 Velocity0 = ElementPtr0->velocity;
217 Control0 = ElementPtr0->IntersectControl;
218
219 Pt1 = ElementPtr1->current.location;
220 Velocity1 = ElementPtr1->velocity;
221 Control1 = ElementPtr1->IntersectControl;
222
223 if (time_y_0)
224 {
225 GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, time_y_0);
226 Pt0.x += dx_0;
227 Pt0.y += dy_0;
228 Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
229 Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
230
231 GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, time_y_0);
232 Pt1.x += dx_1;
233 Pt1.y += dy_1;
234 Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
235 Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
236 }
237
238 do
239 {
240 TIME_VALUE when;
241
242 ++time_y_0;
243
244 GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, 1);
245 Pt0.x += dx_0;
246 Pt0.y += dy_0;
247
248 GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, 1);
249 Pt1.x += dx_1;
250 Pt1.y += dy_1;
251
252 Control0.IntersectStamp.origin = Control0.EndPoint;
253 Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
254 Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
255
256 Control1.IntersectStamp.origin = Control1.EndPoint;
257 Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
258 Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
259 when = DrawablesIntersect (&Control0,
260 &Control1, MAX_TIME_VALUE);
261 if (when)
262 {
263 if (when == 1
264 && time_y_0 == 1
265 && ((ElementPtr0->state_flags
266 | ElementPtr1->state_flags) & APPEARING))
267 {
268 when = 0;
269 Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
270 Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
271
272 Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
273 Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
274 }
275
276 if (when)
277 return ((COUNT)time_y_0);
278 }
279 } while (time_y_0 < time_y_1);
280 }
281 }
282 }
283 }
284
285 return (0);
286 }
287
288 static void
InitCyborg(STARSHIP * StarShipPtr)289 InitCyborg (STARSHIP *StarShipPtr)
290 {
291 COUNT Index, Divisor;
292
293 Index = StarShipPtr->RaceDescPtr->characteristics.max_thrust
294 * StarShipPtr->RaceDescPtr->characteristics.thrust_increment;
295 if ((Divisor = StarShipPtr->RaceDescPtr->characteristics.turn_wait
296 + StarShipPtr->RaceDescPtr->characteristics.thrust_wait) > 0)
297 Index /= Divisor;
298 else
299 Index >>= 1;
300 #ifdef PRINT_MI
301 {
302 char *shipName;
303
304 shipName = GetStringAddress (
305 StarShipPtr->RaceDescPtr->ship_data.race_strings);
306 log_add (log_Debug, "MI(%s) -- <%u:%u> = %u", shipName,
307 StarShipPtr->RaceDescPtr->characteristics.max_thrust *
308 StarShipPtr->RaceDescPtr->characteristics.thrust_increment,
309 Divisor, Index);
310 }
311 #endif /* PRINT_MI */
312 StarShipPtr->RaceDescPtr->cyborg_control.ManeuverabilityIndex = Index;
313 }
314
315 static void
ship_movement(ELEMENT * ShipPtr,EVALUATE_DESC * EvalDescPtr)316 ship_movement (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
317 {
318 if (EvalDescPtr->which_turn == 0)
319 EvalDescPtr->which_turn = 1;
320
321 switch (EvalDescPtr->MoveState)
322 {
323 case PURSUE:
324 Pursue (ShipPtr, EvalDescPtr);
325 break;
326 case AVOID:
327 #ifdef NOTYET
328 Avoid (ShipPtr, EvalDescPtr);
329 break;
330 #endif /* NOTYET */
331 case ENTICE:
332 Entice (ShipPtr, EvalDescPtr);
333 break;
334 case NO_MOVEMENT:
335 break;
336 }
337 }
338
339 BOOLEAN
ship_weapons(ELEMENT * ShipPtr,ELEMENT * OtherPtr,COUNT margin_of_error)340 ship_weapons (ELEMENT *ShipPtr, ELEMENT *OtherPtr, COUNT margin_of_error)
341 {
342 SIZE delta_x, delta_y;
343 COUNT n, num_weapons;
344 ELEMENT Ship;
345 HELEMENT Weapon[6];
346 STARSHIP *StarShipPtr;
347
348 if (OBJECT_CLOAKED (OtherPtr))
349 margin_of_error += DISPLAY_TO_WORLD (40);
350
351 Ship = *ShipPtr;
352 GetNextVelocityComponents (&Ship.velocity,
353 &delta_x, &delta_y, 1);
354 Ship.next.location.x =
355 Ship.current.location.x + delta_x;
356 Ship.next.location.y =
357 Ship.current.location.y + delta_y;
358 Ship.current.location = Ship.next.location;
359
360 GetElementStarShip (&Ship, &StarShipPtr);
361 num_weapons =
362 (*StarShipPtr->RaceDescPtr->init_weapon_func) (&Ship, Weapon);
363
364 if ((n = num_weapons))
365 {
366 HELEMENT *WeaponPtr, w;
367 //STARSHIP *StarShipPtr;
368 ELEMENT *EPtr;
369
370 WeaponPtr = &Weapon[0];
371 do
372 {
373 w = *WeaponPtr;
374 if (w)
375 {
376 LockElement (w, &EPtr);
377 if (EPtr->state_flags & APPEARING)
378 {
379 EPtr->next = EPtr->current;
380 InitIntersectStartPoint (EPtr);
381 InitIntersectEndPoint (EPtr);
382 InitIntersectFrame (EPtr);
383 }
384
385 if (PlotIntercept (EPtr, OtherPtr,
386 EPtr->life_span, margin_of_error))
387 {
388 UnlockElement (w);
389 break;
390 }
391
392 UnlockElement (w);
393 FreeElement (w);
394 }
395 ++WeaponPtr;
396 } while (--n);
397
398 if ((num_weapons = n))
399 {
400 do
401 {
402 w = *WeaponPtr++;
403 if (w)
404 FreeElement (w);
405 } while (--n);
406 }
407 }
408
409 return (num_weapons > 0);
410 }
411
412 void
ship_intelligence(ELEMENT * ShipPtr,EVALUATE_DESC * ObjectsOfConcern,COUNT ConcernCounter)413 ship_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
414 COUNT ConcernCounter)
415 {
416 BOOLEAN ShipMoved, ShipFired;
417 COUNT margin_of_error;
418 STARSHIP *StarShipPtr;
419
420 GetElementStarShip (ShipPtr, &StarShipPtr);
421
422 ShipMoved = TRUE;
423 if (ShipPtr->turn_wait == 0)
424 ShipMoved = FALSE;
425 if (ShipPtr->thrust_wait == 0)
426 ShipMoved = FALSE;
427
428 ShipFired = TRUE;
429 if (StarShipPtr->weapon_counter == 0)
430 {
431 StarShipPtr->ship_input_state &= ~WEAPON;
432 if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON))
433 ShipFired = FALSE;
434 }
435
436 if (StarShipPtr->control & AWESOME_RATING)
437 margin_of_error = 0;
438 else if (StarShipPtr->control & GOOD_RATING)
439 margin_of_error = DISPLAY_TO_WORLD (20);
440 else /* if (StarShipPtr->control & STANDARD_RATING) */
441 margin_of_error = DISPLAY_TO_WORLD (40);
442
443 ObjectsOfConcern += ConcernCounter;
444 while (ConcernCounter--)
445 {
446 --ObjectsOfConcern;
447 if (ObjectsOfConcern->ObjectPtr)
448 {
449 if (!ShipMoved
450 && (ConcernCounter != ENEMY_WEAPON_INDEX
451 || ObjectsOfConcern->MoveState == PURSUE
452 || (ObjectsOfConcern->ObjectPtr->state_flags & CREW_OBJECT)
453 || MANEUVERABILITY (
454 &StarShipPtr->RaceDescPtr->cyborg_control
455 ) >= MEDIUM_SHIP))
456 {
457 ship_movement (ShipPtr, ObjectsOfConcern);
458 ShipMoved = TRUE;
459 }
460 if (!ShipFired
461 && (ConcernCounter == ENEMY_SHIP_INDEX
462 || (ConcernCounter == ENEMY_WEAPON_INDEX
463 && ObjectsOfConcern->MoveState != AVOID
464 #ifdef NEVER
465 && !(StarShipPtr->control & STANDARD_RATING)
466 #endif /* NEVER */
467 )))
468 {
469 ShipFired = ship_weapons (ShipPtr,
470 ObjectsOfConcern->ObjectPtr, margin_of_error);
471 if (ShipFired)
472 StarShipPtr->ship_input_state |= WEAPON;
473 }
474 }
475 }
476 }
477
478 BOOLEAN
TurnShip(ELEMENT * ShipPtr,COUNT angle)479 TurnShip (ELEMENT *ShipPtr, COUNT angle)
480 {
481 COUNT f, ship_delta_facing;
482 STARSHIP *StarShipPtr;
483
484 GetElementStarShip (ShipPtr, &StarShipPtr);
485 f = StarShipPtr->ShipFacing;
486 ship_delta_facing = NORMALIZE_FACING (ANGLE_TO_FACING (angle) - f);
487 if (ship_delta_facing)
488 {
489 if (ship_delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
490 ship_delta_facing =
491 NORMALIZE_FACING (ship_delta_facing +
492 (TFB_Random () & 1 ?
493 ANGLE_TO_FACING (OCTANT >> 1) :
494 -ANGLE_TO_FACING (OCTANT >> 1)));
495
496 if (ship_delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
497 {
498 StarShipPtr->ship_input_state |= RIGHT;
499 ++f;
500 ShipPtr->next.image.frame =
501 IncFrameIndex (ShipPtr->current.image.frame);
502 }
503 else
504 {
505 StarShipPtr->ship_input_state |= LEFT;
506 --f;
507 ShipPtr->next.image.frame =
508 DecFrameIndex (ShipPtr->current.image.frame);
509 }
510
511 #ifdef NOTYET
512 if (((StarShipPtr->ship_input_state & (LEFT | RIGHT))
513 ^ (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) == (LEFT | RIGHT))
514 StarShipPtr->ship_input_state &= ~(LEFT | RIGHT);
515 else
516 #endif /* NOTYET */
517 {
518 StarShipPtr->ShipFacing = NORMALIZE_FACING (f);
519
520 return (TRUE);
521 }
522 }
523
524 return (FALSE);
525 }
526
527 BOOLEAN
ThrustShip(ELEMENT * ShipPtr,COUNT angle)528 ThrustShip (ELEMENT *ShipPtr, COUNT angle)
529 {
530 BOOLEAN ShouldThrust;
531 STARSHIP *StarShipPtr;
532
533 GetElementStarShip (ShipPtr, &StarShipPtr);
534 if (StarShipPtr->ship_input_state & THRUST)
535 ShouldThrust = TRUE;
536 else if (NORMALIZE_FACING (ANGLE_TO_FACING (angle)
537 - ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity))) == 0
538 && (StarShipPtr->cur_status_flags
539 & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
540 && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL))
541 ShouldThrust = FALSE;
542 else
543 {
544 SIZE ship_delta_facing;
545
546 ship_delta_facing =
547 NORMALIZE_FACING (ANGLE_TO_FACING (angle)
548 - StarShipPtr->ShipFacing + ANGLE_TO_FACING (QUADRANT));
549 if (ship_delta_facing == ANGLE_TO_FACING (QUADRANT)
550 || ((StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)
551 && ship_delta_facing <= ANGLE_TO_FACING (HALF_CIRCLE)))
552 ShouldThrust = TRUE;
553 else
554 ShouldThrust = FALSE;
555 }
556
557 if (ShouldThrust)
558 {
559 inertial_thrust (ShipPtr);
560
561 StarShipPtr->ship_input_state |= THRUST;
562 }
563
564 return (ShouldThrust);
565 }
566
567 void
Pursue(ELEMENT * ShipPtr,EVALUATE_DESC * EvalDescPtr)568 Pursue (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
569 {
570 BYTE maneuver_state;
571 COUNT desired_thrust_angle, desired_turn_angle;
572 SIZE delta_x, delta_y;
573 SIZE ship_delta_x, ship_delta_y;
574 SIZE other_delta_x, other_delta_y;
575 ELEMENT *OtherObjPtr;
576 VELOCITY_DESC ShipVelocity, OtherVelocity;
577
578 ShipVelocity = ShipPtr->velocity;
579 GetNextVelocityComponents (&ShipVelocity,
580 &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn);
581 ShipPtr->next.location.x =
582 ShipPtr->current.location.x + ship_delta_x;
583 ShipPtr->next.location.y =
584 ShipPtr->current.location.y + ship_delta_y;
585
586 OtherObjPtr = EvalDescPtr->ObjectPtr;
587 OtherVelocity = OtherObjPtr->velocity;
588 GetNextVelocityComponents (&OtherVelocity,
589 &other_delta_x, &other_delta_y, EvalDescPtr->which_turn);
590
591 delta_x = (OtherObjPtr->current.location.x + other_delta_x)
592 - ShipPtr->next.location.x;
593 delta_y = (OtherObjPtr->current.location.y + other_delta_y)
594 - ShipPtr->next.location.y;
595 delta_x = WRAP_DELTA_X (delta_x);
596 delta_y = WRAP_DELTA_Y (delta_y);
597 desired_thrust_angle = ARCTAN (delta_x, delta_y);
598
599 maneuver_state = 0;
600 if (ShipPtr->turn_wait == 0)
601 maneuver_state |= LEFT | RIGHT;
602 if (ShipPtr->thrust_wait == 0
603 && ((OtherObjPtr->state_flags & PLAYER_SHIP)
604 || elementsOfSamePlayer (OtherObjPtr, ShipPtr)
605 || OtherObjPtr->preprocess_func == crew_preprocess))
606 maneuver_state |= THRUST;
607
608 desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE);
609 /* other player's ship */
610 if ((OtherObjPtr->state_flags & PLAYER_SHIP)
611 && OtherObjPtr->mass_points <= MAX_SHIP_MASS)
612 {
613 STARSHIP *StarShipPtr;
614 STARSHIP *EnemyStarShipPtr;
615
616 GetElementStarShip (ShipPtr, &StarShipPtr);
617 GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr);
618 if ((MANEUVERABILITY (
619 &StarShipPtr->RaceDescPtr->cyborg_control
620 ) >= FAST_SHIP
621 && WEAPON_RANGE (&StarShipPtr->RaceDescPtr->cyborg_control)
622 > CLOSE_RANGE_WEAPON)
623 || (EvalDescPtr->which_turn >= 24
624 && (StarShipPtr->RaceDescPtr->characteristics.max_thrust * 2 / 3 <
625 EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust
626 || (EnemyStarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))))
627 {
628 UWORD ship_flags;
629
630 ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags;
631 /* you're maneuverable */
632 if (MANEUVERABILITY (
633 &StarShipPtr->RaceDescPtr->cyborg_control
634 ) >= MEDIUM_SHIP)
635 {
636 UWORD fire_flags;
637 COUNT facing;
638
639 for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing;
640 fire_flags <= FIRES_LEFT;
641 fire_flags <<= 1, facing += QUADRANT)
642 {
643 if
644 (
645 /* he's dangerous in this direction */
646 (ship_flags & fire_flags)
647 /* he's facing direction you want to go */
648 && NORMALIZE_ANGLE (
649 desired_turn_angle - facing + OCTANT
650 ) <= QUADRANT
651 && (
652 /* he's moving */
653 (other_delta_x != 0 || other_delta_y != 0)
654 &&
655 /* he's coasting backwards */
656 NORMALIZE_ANGLE (
657 (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE)
658 - facing + (OCTANT + (OCTANT >> 1)))
659 <= ((OCTANT + (OCTANT >> 1)) << 1))
660 )
661 {
662 /* catch him on the back side */
663 desired_thrust_angle = desired_turn_angle;
664 break;
665 }
666 }
667 }
668
669 if (desired_thrust_angle != desired_turn_angle
670 && (other_delta_x || other_delta_y)
671 && EvalDescPtr->which_turn >= 24
672 && NORMALIZE_ANGLE (desired_thrust_angle
673 - GetVelocityTravelAngle (&OtherVelocity)
674 + OCTANT) <= QUADRANT
675 && ((NORMALIZE_ANGLE (
676 GetVelocityTravelAngle (&OtherVelocity)
677 - GetVelocityTravelAngle (&ShipVelocity)
678 + OCTANT) <= QUADRANT
679 && (((StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED)
680 && !(StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))
681 || (ship_flags & DONT_CHASE)))
682 || NORMALIZE_ANGLE (
683 desired_turn_angle
684 - FACING_TO_ANGLE (StarShipPtr->ShipFacing)
685 + OCTANT) <= QUADRANT))
686 desired_thrust_angle = desired_turn_angle;
687 }
688 }
689
690 if (maneuver_state & (LEFT | RIGHT))
691 TurnShip (ShipPtr, desired_thrust_angle);
692
693 if (maneuver_state & THRUST)
694 ThrustShip (ShipPtr, desired_thrust_angle);
695 }
696
697 void
Entice(ELEMENT * ShipPtr,EVALUATE_DESC * EvalDescPtr)698 Entice (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
699 {
700 BYTE maneuver_state;
701 COUNT desired_thrust_angle, desired_turn_angle;
702 COUNT cone_of_fire, travel_angle;
703 SIZE delta_x, delta_y;
704 SIZE ship_delta_x, ship_delta_y;
705 SIZE other_delta_x, other_delta_y;
706 ELEMENT *OtherObjPtr;
707 VELOCITY_DESC ShipVelocity, OtherVelocity;
708 STARSHIP *StarShipPtr;
709 RACE_DESC *RDPtr;
710
711 ShipVelocity = ShipPtr->velocity;
712 GetNextVelocityComponents (&ShipVelocity,
713 &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn);
714 ShipPtr->next.location.x =
715 ShipPtr->current.location.x + ship_delta_x;
716 ShipPtr->next.location.y =
717 ShipPtr->current.location.y + ship_delta_y;
718
719 OtherObjPtr = EvalDescPtr->ObjectPtr;
720 OtherVelocity = OtherObjPtr->velocity;
721 GetNextVelocityComponents (&OtherVelocity,
722 &other_delta_x, &other_delta_y, EvalDescPtr->which_turn);
723
724 delta_x = (OtherObjPtr->current.location.x + other_delta_x)
725 - ShipPtr->next.location.x;
726 delta_y = (OtherObjPtr->current.location.y + other_delta_y)
727 - ShipPtr->next.location.y;
728 delta_x = WRAP_DELTA_X (delta_x);
729 delta_y = WRAP_DELTA_Y (delta_y);
730 desired_thrust_angle = ARCTAN (delta_x, delta_y);
731
732 maneuver_state = 0;
733 if (ShipPtr->turn_wait == 0)
734 maneuver_state |= LEFT | RIGHT;
735 if (ShipPtr->thrust_wait == 0)
736 maneuver_state |= THRUST;
737
738 delta_x = ship_delta_x - other_delta_x;
739 delta_y = ship_delta_y - other_delta_y;
740 travel_angle = ARCTAN (delta_x, delta_y);
741 desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE);
742
743 GetElementStarShip (ShipPtr, &StarShipPtr);
744 RDPtr = StarShipPtr->RaceDescPtr;
745 if (EvalDescPtr->MoveState == AVOID)
746 {
747 desired_turn_angle =
748 NORMALIZE_ANGLE (desired_turn_angle - EvalDescPtr->facing);
749
750 if (NORMALIZE_FACING (ANGLE_TO_FACING (desired_turn_angle)))
751 {
752 if (desired_turn_angle <= HALF_CIRCLE)
753 desired_thrust_angle = RIGHT;
754 else /* if (desired_turn_angle > HALF_CIRCLE) */
755 desired_thrust_angle = LEFT;
756 }
757 else
758 {
759 desired_turn_angle = NORMALIZE_ANGLE (
760 FACING_TO_ANGLE (StarShipPtr->ShipFacing)
761 - EvalDescPtr->facing
762 );
763 if ((desired_turn_angle & (HALF_CIRCLE - 1)) == 0)
764 desired_thrust_angle = TFB_Random () & 1 ? RIGHT : LEFT;
765 else
766 desired_thrust_angle = desired_turn_angle < HALF_CIRCLE ? RIGHT : LEFT;
767 }
768
769 if (desired_thrust_angle == LEFT)
770 {
771 #define FLANK_LEFT -QUADRANT
772 #define SHIP_LEFT -OCTANT
773 desired_thrust_angle = EvalDescPtr->facing
774 + FLANK_LEFT - (SHIP_LEFT >> 1);
775 }
776 else
777 {
778 #define FLANK_RIGHT QUADRANT
779 #define SHIP_RIGHT OCTANT
780 desired_thrust_angle = EvalDescPtr->facing
781 + FLANK_RIGHT - (SHIP_RIGHT >> 1);
782 }
783
784 desired_thrust_angle = NORMALIZE_ANGLE (desired_thrust_angle);
785 }
786 else if (GRAVITY_MASS (OtherObjPtr->mass_points))
787 {
788 COUNT planet_facing;
789
790 planet_facing = NORMALIZE_FACING (ANGLE_TO_FACING (desired_thrust_angle));
791 cone_of_fire = NORMALIZE_FACING (
792 planet_facing
793 - StarShipPtr->ShipFacing
794 + ANGLE_TO_FACING (QUADRANT));
795
796 if (RDPtr->characteristics.thrust_increment !=
797 RDPtr->characteristics.max_thrust)
798 maneuver_state &= ~THRUST;
799
800 /* if not pointing towards planet */
801 if (cone_of_fire > ANGLE_TO_FACING (QUADRANT << 1))
802 desired_turn_angle = desired_thrust_angle;
803 /* if pointing directly at planet */
804 else if (cone_of_fire == ANGLE_TO_FACING (QUADRANT)
805 && NORMALIZE_FACING (ANGLE_TO_FACING (travel_angle)) != planet_facing)
806 desired_turn_angle = travel_angle;
807 else if (cone_of_fire == 0
808 || cone_of_fire == ANGLE_TO_FACING (QUADRANT << 1)
809 || (!(maneuver_state & THRUST)
810 && (cone_of_fire < ANGLE_TO_FACING (OCTANT)
811 || cone_of_fire > ANGLE_TO_FACING ((QUADRANT << 1) - OCTANT))))
812 {
813 desired_turn_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
814 if (NORMALIZE_ANGLE (desired_turn_angle
815 - travel_angle + QUADRANT) > HALF_CIRCLE)
816 desired_turn_angle = travel_angle;
817 if (ShipPtr->thrust_wait == 0)
818 maneuver_state |= THRUST;
819 }
820
821 desired_thrust_angle = desired_turn_angle;
822 }
823 else
824 {
825 COUNT WRange;
826
827 WRange = WEAPON_RANGE (
828 &RDPtr->cyborg_control
829 );
830
831 cone_of_fire = NORMALIZE_ANGLE (desired_turn_angle
832 - EvalDescPtr->facing + OCTANT);
833 if (OtherObjPtr->state_flags & PLAYER_SHIP)
834 {
835 UWORD fire_flags, ship_flags;
836 COUNT facing;
837 STARSHIP *EnemyStarShipPtr;
838
839 GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr);
840 ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags;
841 for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing;
842 fire_flags <= FIRES_LEFT;
843 fire_flags <<= 1, facing += QUADRANT)
844 {
845 if
846 (
847 /* he's dangerous in this direction */
848 (ship_flags & fire_flags)
849 /* he's facing direction you want to go */
850 && (cone_of_fire = NORMALIZE_ANGLE (
851 desired_turn_angle - facing + OCTANT
852 )) <= QUADRANT
853 /* he's moving */
854 && ((other_delta_x != 0 || other_delta_y != 0)
855 /* he's coasting backwards */
856 && NORMALIZE_ANGLE (
857 (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE)
858 - facing + OCTANT) <= QUADRANT)
859 )
860 {
861 /* need to be close for a kill */
862 if (WRange < LONG_RANGE_WEAPON
863 && EvalDescPtr->which_turn <= 32)
864 {
865 /* catch him on the back side */
866 desired_thrust_angle = desired_turn_angle;
867 goto DoManeuver;
868 }
869
870 break;
871 }
872 }
873
874 if (EvalDescPtr->which_turn <= 8
875 && RDPtr->characteristics.max_thrust <=
876 EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust)
877 goto DoManeuver;
878 }
879
880 if
881 (
882 #ifdef NOTYET
883 WRange < LONG_RANGE_WEAPON
884 &&
885 #endif /* NOTYET */
886 /* not at full speed */
887 !(StarShipPtr->cur_status_flags
888 & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
889 && (PlotIntercept (
890 ShipPtr, OtherObjPtr, 40, CLOSE_RANGE_WEAPON << 1
891 )
892 #ifdef NOTYET
893 ||
894 (
895 /* object's facing direction you want to go */
896 cone_of_fire <= QUADRANT
897 /* and you're basically going in that direction */
898 && (travel_angle == FULL_CIRCLE
899 || NORMALIZE_ANGLE (travel_angle
900 - desired_thrust_angle + QUADRANT) <= HALF_CIRCLE)
901 /* and object's in range */
902 && PlotIntercept (ShipPtr, OtherObjPtr, 1, WRange)
903 )
904 #endif /* NOTYET */
905 )
906 )
907 {
908 if
909 (
910 /* pointed straight at him */
911 NORMALIZE_ANGLE (desired_thrust_angle
912 - FACING_TO_ANGLE (StarShipPtr->ShipFacing) + OCTANT) <= QUADRANT
913 /* or not exposed to business end */
914 || cone_of_fire > QUADRANT
915 )
916 {
917 desired_thrust_angle = desired_turn_angle;
918 }
919 else
920 {
921 #ifdef NOTYET
922 if
923 (
924 travel_angle != FULL_CIRCLE
925 && NORMALIZE_ANGLE (travel_angle
926 - desired_turn_angle + OCTANT) <= QUADRANT
927 )
928 {
929 desired_turn_angle =
930 NORMALIZE_ANGLE ((EvalDescPtr->facing + HALF_CIRCLE)
931 + (travel_angle - desired_turn_angle));
932 if (!(maneuver_state & (LEFT | RIGHT)))
933 maneuver_state &= ~THRUST;
934 }
935
936 if (maneuver_state & (LEFT | RIGHT))
937 {
938 TurnShip (ShipPtr, desired_turn_angle);
939 maneuver_state &= ~(LEFT | RIGHT);
940 }
941 #endif /* NOTYET */
942
943 desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
944 desired_turn_angle = desired_thrust_angle;
945 }
946 }
947 else if ((cone_of_fire = PlotIntercept (
948 ShipPtr, OtherObjPtr, 10, WRange
949 #ifdef OLD
950 - (WRange >> 3)
951 #else /* !OLD */
952 - (WRange >> 2)
953 #endif /* OLD */
954 )))
955 {
956 if (RDPtr->characteristics.thrust_increment !=
957 RDPtr->characteristics.max_thrust
958 /* and already at full speed */
959 && (StarShipPtr->cur_status_flags
960 & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
961 /* and facing away from enemy */
962 && (NORMALIZE_ANGLE (desired_turn_angle
963 - ARCTAN (ship_delta_x, ship_delta_y)
964 + (OCTANT + 2)) <= ((OCTANT + 2) << 1)
965 /* or not on collision course */
966 || !PlotIntercept (
967 ShipPtr, OtherObjPtr, 30, CLOSE_RANGE_WEAPON << 1
968 )))
969 maneuver_state &= ~THRUST;
970 /* veer off */
971 else if (cone_of_fire == 1
972 || RDPtr->characteristics.thrust_increment !=
973 RDPtr->characteristics.max_thrust)
974 {
975 if (maneuver_state & (LEFT | RIGHT))
976 {
977 TurnShip (ShipPtr, desired_turn_angle);
978 maneuver_state &= ~(LEFT | RIGHT);
979 }
980
981 if (NORMALIZE_ANGLE (desired_thrust_angle
982 - ARCTAN (ship_delta_x, ship_delta_y)
983 + (OCTANT + 2)) <= ((OCTANT + 2) << 1))
984 desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
985 else
986 desired_thrust_angle = desired_turn_angle;
987 }
988 }
989 }
990
991 DoManeuver:
992 if (maneuver_state & (LEFT | RIGHT))
993 TurnShip (ShipPtr, desired_thrust_angle);
994
995 if (maneuver_state & THRUST)
996 ThrustShip (ShipPtr, desired_thrust_angle);
997 }
998
999 void
Avoid(ELEMENT * ShipPtr,EVALUATE_DESC * EvalDescPtr)1000 Avoid (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
1001 {
1002 (void) ShipPtr; /* Satisfying compiler (unused parameter) */
1003 (void) EvalDescPtr; /* Satisfying compiler (unused parameter) */
1004 }
1005
1006 BATTLE_INPUT_STATE
tactical_intelligence(ComputerInputContext * context,STARSHIP * StarShipPtr)1007 tactical_intelligence (ComputerInputContext *context, STARSHIP *StarShipPtr)
1008 {
1009 ELEMENT *ShipPtr;
1010 ELEMENT Ship;
1011 COUNT ShipFacing;
1012 HELEMENT hElement, hNextElement;
1013 COUNT ConcernCounter;
1014 EVALUATE_DESC ObjectsOfConcern[10];
1015 BOOLEAN ShipMoved, UltraManeuverable;
1016 STARSHIP *EnemyStarShipPtr;
1017 RACE_DESC *RDPtr;
1018 RACE_DESC *EnemyRDPtr;
1019
1020 RDPtr = StarShipPtr->RaceDescPtr;
1021
1022 if (RDPtr->cyborg_control.ManeuverabilityIndex == 0)
1023 InitCyborg (StarShipPtr);
1024
1025 LockElement (StarShipPtr->hShip, &ShipPtr);
1026 if (RDPtr->ship_info.crew_level == 0
1027 || GetPrimType (&DisplayArray[ShipPtr->PrimIndex]) == NO_PRIM)
1028 {
1029 UnlockElement (StarShipPtr->hShip);
1030 return (0);
1031 }
1032
1033 ShipMoved = TRUE;
1034 /* Disable ship's special completely for the Standard AI */
1035 if (StarShipPtr->control & STANDARD_RATING)
1036 ++StarShipPtr->special_counter;
1037
1038 #ifdef DEBUG_CYBORG
1039 if (!(ShipPtr->state_flags & FINITE_LIFE)
1040 && ShipPtr->life_span == NORMAL_LIFE)
1041 ShipPtr->life_span += 2; /* make ship invulnerable */
1042 #endif /* DEBUG_CYBORG */
1043 Ship = *ShipPtr;
1044 UnlockElement (StarShipPtr->hShip);
1045 ShipFacing = StarShipPtr->ShipFacing;
1046
1047 for (ConcernCounter = 0;
1048 ConcernCounter <= FIRST_EMPTY_INDEX; ++ConcernCounter)
1049 {
1050 ObjectsOfConcern[ConcernCounter].ObjectPtr = 0;
1051 ObjectsOfConcern[ConcernCounter].MoveState = NO_MOVEMENT;
1052 ObjectsOfConcern[ConcernCounter].which_turn = (COUNT)~0;
1053 }
1054 --ConcernCounter;
1055
1056 UltraManeuverable = (BOOLEAN)(
1057 RDPtr->characteristics.thrust_increment ==
1058 RDPtr->characteristics.max_thrust
1059 && MANEUVERABILITY (&RDPtr->cyborg_control) >= MEDIUM_SHIP
1060 );
1061
1062 if (Ship.turn_wait == 0)
1063 {
1064 ShipMoved = FALSE;
1065 StarShipPtr->ship_input_state &= ~(LEFT | RIGHT);
1066 }
1067 if (Ship.thrust_wait == 0)
1068 {
1069 ShipMoved = FALSE;
1070 StarShipPtr->ship_input_state &= ~THRUST;
1071 }
1072
1073 for (hElement = GetHeadElement ();
1074 hElement != 0; hElement = hNextElement)
1075 {
1076 EVALUATE_DESC ed;
1077
1078 ed.MoveState = NO_MOVEMENT;
1079
1080 LockElement (hElement, &ed.ObjectPtr);
1081 hNextElement = GetSuccElement (ed.ObjectPtr);
1082 if (CollisionPossible (ed.ObjectPtr, &Ship))
1083 {
1084 SIZE dx, dy;
1085
1086 dx = ed.ObjectPtr->next.location.x
1087 - Ship.next.location.x;
1088 dy = ed.ObjectPtr->next.location.y
1089 - Ship.next.location.y;
1090 dx = WRAP_DELTA_X (dx);
1091 dy = WRAP_DELTA_Y (dy);
1092 if (GRAVITY_MASS (ed.ObjectPtr->mass_points))
1093 {
1094 COUNT maneuver_turn, ship_bounds;
1095 RECT ship_footprint = {{0, 0}, {0, 0}};
1096
1097 if (UltraManeuverable)
1098 maneuver_turn = 16;
1099 else if (MANEUVERABILITY (&RDPtr->cyborg_control) <= MEDIUM_SHIP)
1100 maneuver_turn = 48;
1101 else
1102 maneuver_turn = 32;
1103
1104 GetFrameRect (SetAbsFrameIndex (
1105 Ship.IntersectControl.IntersectStamp.frame, 0
1106 ), &ship_footprint);
1107 ship_bounds = (COUNT)(ship_footprint.extent.width
1108 + ship_footprint.extent.height);
1109
1110 if (!ShipMoved && (ed.which_turn =
1111 PlotIntercept (ed.ObjectPtr, &Ship, maneuver_turn,
1112 DISPLAY_TO_WORLD (30 + (ship_bounds * 3 /* << 2 */)))))
1113 {
1114 if (ed.which_turn > 1
1115 || PlotIntercept (ed.ObjectPtr, &Ship, 1,
1116 DISPLAY_TO_WORLD (35 + ship_bounds))
1117 || PlotIntercept (ed.ObjectPtr, &Ship,
1118 maneuver_turn << 1,
1119 DISPLAY_TO_WORLD (40 + ship_bounds)) > 1)
1120 {
1121 ed.facing = ARCTAN (-dx, -dy);
1122 if (UltraManeuverable)
1123 ed.MoveState = AVOID;
1124 else // Try a gravity whip
1125 ed.MoveState = ENTICE;
1126
1127 ObjectsOfConcern[GRAVITY_MASS_INDEX] = ed;
1128 }
1129 else if (!UltraManeuverable &&
1130 !IsVelocityZero (&Ship.velocity))
1131 { // Try an orbital insertion, don't thrust
1132 ++Ship.thrust_wait;
1133 if (Ship.turn_wait)
1134 ShipMoved = TRUE;
1135 }
1136 }
1137 }
1138 else if (ed.ObjectPtr->state_flags & PLAYER_SHIP)
1139 {
1140 GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr);
1141 EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr;
1142 if (EnemyRDPtr->cyborg_control.ManeuverabilityIndex == 0)
1143 InitCyborg (EnemyStarShipPtr);
1144
1145 ed.which_turn = WORLD_TO_TURN (
1146 square_root ((long)dx * dx + (long)dy * dy));
1147 if (ed.which_turn >
1148 ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn)
1149 {
1150 UnlockElement (hElement);
1151 continue;
1152 }
1153 else if (ed.which_turn == 0)
1154 ed.which_turn = 1;
1155
1156 ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr = ed.ObjectPtr;
1157 ObjectsOfConcern[ENEMY_SHIP_INDEX].facing =
1158 #ifdef MAYBE
1159 OBJECT_CLOAKED (ed.ObjectPtr) ?
1160 GetVelocityTravelAngle (&ed.ObjectPtr->velocity) :
1161 #endif /* MAYBE */
1162 FACING_TO_ANGLE (EnemyStarShipPtr->ShipFacing);
1163 ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn = ed.which_turn;
1164
1165 if (ShipMoved
1166 || ed.ObjectPtr->mass_points > MAX_SHIP_MASS
1167 || (WEAPON_RANGE (&RDPtr->cyborg_control) < LONG_RANGE_WEAPON
1168 && (WEAPON_RANGE (&RDPtr->cyborg_control) <= CLOSE_RANGE_WEAPON
1169 || (WEAPON_RANGE (&EnemyRDPtr->cyborg_control) >= LONG_RANGE_WEAPON
1170 && (EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON))
1171 || (
1172 #ifdef OLD
1173 MANEUVERABILITY (&RDPtr->cyborg_control) <
1174 MANEUVERABILITY (&EnemyRDPtr->cyborg_control)
1175 #else /* !OLD */
1176 RDPtr->characteristics.max_thrust <
1177 EnemyRDPtr->characteristics.max_thrust
1178 #endif /* !OLD */
1179 && WEAPON_RANGE (&RDPtr->cyborg_control) <
1180 WEAPON_RANGE (&EnemyRDPtr->cyborg_control)))))
1181 ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = PURSUE;
1182 else
1183 ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = ENTICE;
1184
1185 if ((EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & IMMEDIATE_WEAPON)
1186 && ship_weapons (ed.ObjectPtr, &Ship, 0))
1187 {
1188 ed.which_turn = 1;
1189 ed.MoveState = AVOID;
1190 ed.facing = ObjectsOfConcern[ENEMY_SHIP_INDEX].facing;
1191
1192 ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed;
1193 }
1194 }
1195 else if (ed.ObjectPtr->pParent == 0)
1196 {
1197 if (!(ed.ObjectPtr->state_flags & FINITE_LIFE))
1198 {
1199 ed.which_turn = WORLD_TO_TURN (
1200 square_root ((long)dx * dx + (long)dy * dy)
1201 );
1202
1203 if (ed.which_turn <
1204 ObjectsOfConcern[FIRST_EMPTY_INDEX].which_turn)
1205 {
1206 ed.MoveState = PURSUE;
1207 ed.facing = GetVelocityTravelAngle (
1208 &ed.ObjectPtr->velocity
1209 );
1210
1211 ObjectsOfConcern[FIRST_EMPTY_INDEX] = ed;
1212 }
1213 }
1214 }
1215 else if (!elementsOfSamePlayer (ed.ObjectPtr, &Ship)
1216 && ed.ObjectPtr->preprocess_func != crew_preprocess
1217 && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn > 1
1218 && ed.ObjectPtr->life_span > 0)
1219 {
1220 GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr);
1221 EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr;
1222 if (((EnemyRDPtr->ship_info.ship_flags & SEEKING_WEAPON)
1223 && ed.ObjectPtr->next.image.farray !=
1224 EnemyRDPtr->ship_data.special)
1225 || ((EnemyRDPtr->ship_info.ship_flags & SEEKING_SPECIAL)
1226 && ed.ObjectPtr->next.image.farray ==
1227 EnemyRDPtr->ship_data.special))
1228 {
1229 if ((!(ed.ObjectPtr->state_flags & (FINITE_LIFE | CREW_OBJECT))
1230 && RDPtr->characteristics.max_thrust > DISPLAY_TO_WORLD (8))
1231 || NORMALIZE_ANGLE (GetVelocityTravelAngle (
1232 &ed.ObjectPtr->velocity
1233 ) - ARCTAN (-dx, -dy)
1234 + QUADRANT) > HALF_CIRCLE)
1235 ed.which_turn = 0;
1236 else
1237 {
1238 ed.which_turn = WORLD_TO_TURN (
1239 square_root ((long)dx * dx + (long)dy * dy)
1240 );
1241
1242 ed.MoveState = ENTICE;
1243 if (UltraManeuverable)
1244 {
1245 if (ed.which_turn == 0)
1246 ed.which_turn = 1;
1247 else if (ed.which_turn > 16)
1248 ed.which_turn = 0;
1249 }
1250 else if (ed.which_turn == 0)
1251 ed.which_turn = 1;
1252 else if (ed.which_turn > 16
1253 || (MANEUVERABILITY (
1254 &RDPtr->cyborg_control
1255 ) > MEDIUM_SHIP
1256 && ed.which_turn > 8))
1257 ed.which_turn = 0;
1258 }
1259 }
1260 else if (!(StarShipPtr->control & AWESOME_RATING))
1261 ed.which_turn = 0;
1262 else
1263 {
1264 ed.which_turn =
1265 PlotIntercept (ed.ObjectPtr,
1266 &Ship, ed.ObjectPtr->life_span,
1267 DISPLAY_TO_WORLD (40));
1268 ed.MoveState = AVOID;
1269 }
1270
1271 if (ed.which_turn > 0
1272 && (ed.which_turn <
1273 ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn
1274 || (ed.which_turn ==
1275 ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn
1276 && ed.MoveState == AVOID)))
1277 {
1278 ed.facing = GetVelocityTravelAngle (
1279 &ed.ObjectPtr->velocity
1280 );
1281
1282 ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed;
1283 }
1284 }
1285 else if ((ed.ObjectPtr->state_flags & CREW_OBJECT)
1286 && ((!(ed.ObjectPtr->state_flags & IGNORE_SIMILAR)
1287 && elementsOfSamePlayer (ed.ObjectPtr, &Ship))
1288 || ed.ObjectPtr->preprocess_func == crew_preprocess)
1289 && ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn > 1)
1290 {
1291 ed.which_turn = WORLD_TO_TURN (
1292 square_root ((long)dx * dx + (long)dy * dy)
1293 );
1294
1295 if (ed.which_turn == 0)
1296 ed.which_turn = 1;
1297
1298 if (ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn >
1299 ed.which_turn
1300 && (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 32
1301 || (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 8
1302 && StarShipPtr->hShip == ed.ObjectPtr->hTarget)))
1303 {
1304 ed.MoveState = PURSUE;
1305 ed.facing = 0;
1306 ObjectsOfConcern[CREW_OBJECT_INDEX] = ed;
1307 }
1308 }
1309 }
1310 UnlockElement (hElement);
1311 }
1312
1313 RDPtr->cyborg_control.intelligence_func (&Ship, ObjectsOfConcern,
1314 ConcernCounter);
1315 #ifdef DEBUG_CYBORG
1316 StarShipPtr->ship_input_state &= ~SPECIAL;
1317 #endif /* DEBUG_CYBORG */
1318
1319 StarShipPtr->ShipFacing = ShipFacing;
1320 {
1321 BATTLE_INPUT_STATE InputState;
1322
1323 InputState = 0;
1324 if (StarShipPtr->ship_input_state & LEFT)
1325 InputState |= BATTLE_LEFT;
1326 else if (StarShipPtr->ship_input_state & RIGHT)
1327 InputState |= BATTLE_RIGHT;
1328 if (StarShipPtr->ship_input_state & THRUST)
1329 InputState |= BATTLE_THRUST;
1330 if (StarShipPtr->ship_input_state & WEAPON)
1331 InputState |= BATTLE_WEAPON;
1332 if (StarShipPtr->ship_input_state & SPECIAL)
1333 InputState |= BATTLE_SPECIAL;
1334
1335 (void) context;
1336 return (InputState);
1337 }
1338 }
1339
1340