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