diff options
Diffstat (limited to 'src/uqm/cyborg.c')
-rw-r--r-- | src/uqm/cyborg.c | 1339 |
1 files changed, 1339 insertions, 0 deletions
diff --git a/src/uqm/cyborg.c b/src/uqm/cyborg.c new file mode 100644 index 0000000..a50f3b8 --- /dev/null +++ b/src/uqm/cyborg.c @@ -0,0 +1,1339 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "colors.h" +#include "collide.h" +#include "element.h" +#include "ship.h" +#include "globdata.h" +#include "intel.h" +#include "setup.h" +#include "units.h" +#include "libs/mathlib.h" +#include "libs/log.h" + +//#define DEBUG_CYBORG + +COUNT +PlotIntercept (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1, + COUNT max_turns, COUNT margin_of_error) +{ + SIZE dy; + SIZE time_y_0, time_y_1; + POINT dst[2]; + RECT r0 = {{0, 0}, {0, 0}}; + RECT r1 = {{0, 0}, {0, 0}}; + SIZE dx_0, dy_0, dx_1, dy_1; + + if ((ElementPtr0->state_flags | ElementPtr1->state_flags) & FINITE_LIFE) + { + if (!(ElementPtr0->state_flags & FINITE_LIFE)) + { + if (ElementPtr1->life_span < max_turns) + max_turns = ElementPtr1->life_span; + } + else if (!(ElementPtr1->state_flags & FINITE_LIFE)) + { + if (ElementPtr0->life_span < max_turns) + max_turns = ElementPtr0->life_span; + } + else + { + if (ElementPtr0->life_span < max_turns) + max_turns = ElementPtr0->life_span; + if (ElementPtr1->life_span < max_turns) + max_turns = ElementPtr1->life_span; + } + } + + dst[0] = ElementPtr0->current.location; + GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx_0, &dy_0); + dx_0 = (SIZE)VELOCITY_TO_WORLD ((long)dx_0 * (long)max_turns); + dy_0 = (SIZE)VELOCITY_TO_WORLD ((long)dy_0 * (long)max_turns); + + dst[1] = ElementPtr1->current.location; + GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx_1, &dy_1); + dx_1 = (SIZE)VELOCITY_TO_WORLD ((long)dx_1 * (long)max_turns); + dy_1 = (SIZE)VELOCITY_TO_WORLD ((long)dy_1 * (long)max_turns); + + if (margin_of_error) + { + dst[1].y -= margin_of_error; + time_y_0 = 1; + time_y_1 = margin_of_error << 1; + } + else + { + GetFrameRect (ElementPtr0->IntersectControl.IntersectStamp.frame, &r0); + GetFrameRect (ElementPtr1->IntersectControl.IntersectStamp.frame, &r1); + + dst[0].y += DISPLAY_TO_WORLD (r0.corner.y); + dst[1].y += DISPLAY_TO_WORLD (r1.corner.y); + time_y_0 = DISPLAY_TO_WORLD (r0.extent.height); + time_y_1 = DISPLAY_TO_WORLD (r1.extent.height); + } + + dy = dst[1].y - dst[0].y; + time_y_0 = dy - time_y_0 + 1; + time_y_1 = dy + time_y_1 - 1; + dy = dy_0 - dy_1; + + if ((time_y_0 <= 0 && time_y_1 >= 0) + || (time_y_0 > 0 && dy >= time_y_0) + || (time_y_1 < 0 && dy <= time_y_1)) + { + SIZE dx; + SIZE time_x_0, time_x_1; + + if (margin_of_error) + { + dst[1].x -= margin_of_error; + time_x_0 = 1; + time_x_1 = margin_of_error << 1; + } + else + { + dst[0].x += DISPLAY_TO_WORLD (r0.corner.x); + dst[1].x += DISPLAY_TO_WORLD (r1.corner.x); + time_x_0 = DISPLAY_TO_WORLD (r0.extent.width); + time_x_1 = DISPLAY_TO_WORLD (r1.extent.width); + } + + dx = dst[1].x - dst[0].x; + time_x_0 = dx - time_x_0 + 1; + time_x_1 = dx + time_x_1 - 1; + dx = dx_0 - dx_1; + + if ((time_x_0 <= 0 && time_x_1 >= 0) + || (time_x_0 > 0 && dx >= time_x_0) + || (time_x_1 < 0 && dx <= time_x_1)) + { + if (dx == 0 && dy == 0) + time_y_0 = time_y_1 = 0; + else + { + SIZE t; + long time_beg, time_end, fract; + + if (time_y_1 < 0) + { + t = time_y_0; + time_y_0 = -time_y_1; + time_y_1 = -t; + } + else if (time_y_0 <= 0) + { + if (dy < 0) + time_y_1 = -time_y_0; + time_y_0 = 0; + } + if (dy < 0) + dy = -dy; + if (dy < time_y_1) + time_y_1 = dy; + + if (time_x_1 < 0) + { + t = time_x_0; + time_x_0 = -time_x_1; + time_x_1 = -t; + } + else if (time_x_0 <= 0) + { + if (dx < 0) + time_x_1 = -time_x_0; + time_x_0 = 0; + } + if (dx < 0) + dx = -dx; + if (dx < time_x_1) + time_x_1 = dx; + + if (dx == 0) + { + time_beg = time_y_0; + time_end = time_y_1; + fract = dy; + } + else if (dy == 0) + { + time_beg = time_x_0; + time_end = time_x_1; + fract = dx; + } + else + { + long time_x, time_y; + + time_x = (long)time_x_0 * (long)dy; + time_y = (long)time_y_0 * (long)dx; + time_beg = time_x < time_y ? time_y : time_x; + + time_x = (long)time_x_1 * (long)dy; + time_y = (long)time_y_1 * (long)dx; + time_end = time_x > time_y ? time_y : time_x; + + fract = (long)dx * (long)dy; + } + + if ((time_beg *= max_turns) < fract) + time_y_0 = 0; + else + time_y_0 = (SIZE)(time_beg / fract); + + if (time_end >= fract) /* just in case of overflow */ + time_y_1 = max_turns - 1; + else + time_y_1 = (SIZE)((time_end * max_turns) / fract); + } + + if (time_y_0 <= time_y_1) + { + if (margin_of_error != 0) + return ((COUNT)time_y_0 + 1); + else + { + POINT Pt0, Pt1; + VELOCITY_DESC Velocity0, Velocity1; + INTERSECT_CONTROL Control0, Control1; + + Pt0 = ElementPtr0->current.location; + Velocity0 = ElementPtr0->velocity; + Control0 = ElementPtr0->IntersectControl; + + Pt1 = ElementPtr1->current.location; + Velocity1 = ElementPtr1->velocity; + Control1 = ElementPtr1->IntersectControl; + + if (time_y_0) + { + GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, time_y_0); + Pt0.x += dx_0; + Pt0.y += dy_0; + Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x); + Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y); + + GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, time_y_0); + Pt1.x += dx_1; + Pt1.y += dy_1; + Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x); + Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y); + } + + do + { + TIME_VALUE when; + + ++time_y_0; + + GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, 1); + Pt0.x += dx_0; + Pt0.y += dy_0; + + GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, 1); + Pt1.x += dx_1; + Pt1.y += dy_1; + + Control0.IntersectStamp.origin = Control0.EndPoint; + Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x); + Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y); + + Control1.IntersectStamp.origin = Control1.EndPoint; + Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x); + Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y); + when = DrawablesIntersect (&Control0, + &Control1, MAX_TIME_VALUE); + if (when) + { + if (when == 1 + && time_y_0 == 1 + && ((ElementPtr0->state_flags + | ElementPtr1->state_flags) & APPEARING)) + { + when = 0; + Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x); + Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y); + + Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x); + Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y); + } + + if (when) + return ((COUNT)time_y_0); + } + } while (time_y_0 < time_y_1); + } + } + } + } + + return (0); +} + +static void +InitCyborg (STARSHIP *StarShipPtr) +{ + COUNT Index, Divisor; + + Index = StarShipPtr->RaceDescPtr->characteristics.max_thrust + * StarShipPtr->RaceDescPtr->characteristics.thrust_increment; + if ((Divisor = StarShipPtr->RaceDescPtr->characteristics.turn_wait + + StarShipPtr->RaceDescPtr->characteristics.thrust_wait) > 0) + Index /= Divisor; + else + Index >>= 1; +#ifdef PRINT_MI + { + char *shipName; + + shipName = GetStringAddress ( + StarShipPtr->RaceDescPtr->ship_data.race_strings); + log_add (log_Debug, "MI(%s) -- <%u:%u> = %u", shipName, + StarShipPtr->RaceDescPtr->characteristics.max_thrust * + StarShipPtr->RaceDescPtr->characteristics.thrust_increment, + Divisor, Index); + } +#endif /* PRINT_MI */ + StarShipPtr->RaceDescPtr->cyborg_control.ManeuverabilityIndex = Index; +} + +static void +ship_movement (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr) +{ + if (EvalDescPtr->which_turn == 0) + EvalDescPtr->which_turn = 1; + + switch (EvalDescPtr->MoveState) + { + case PURSUE: + Pursue (ShipPtr, EvalDescPtr); + break; + case AVOID: +#ifdef NOTYET + Avoid (ShipPtr, EvalDescPtr); + break; +#endif /* NOTYET */ + case ENTICE: + Entice (ShipPtr, EvalDescPtr); + break; + case NO_MOVEMENT: + break; + } +} + +BOOLEAN +ship_weapons (ELEMENT *ShipPtr, ELEMENT *OtherPtr, COUNT margin_of_error) +{ + SIZE delta_x, delta_y; + COUNT n, num_weapons; + ELEMENT Ship; + HELEMENT Weapon[6]; + STARSHIP *StarShipPtr; + + if (OBJECT_CLOAKED (OtherPtr)) + margin_of_error += DISPLAY_TO_WORLD (40); + + Ship = *ShipPtr; + GetNextVelocityComponents (&Ship.velocity, + &delta_x, &delta_y, 1); + Ship.next.location.x = + Ship.current.location.x + delta_x; + Ship.next.location.y = + Ship.current.location.y + delta_y; + Ship.current.location = Ship.next.location; + + GetElementStarShip (&Ship, &StarShipPtr); + num_weapons = + (*StarShipPtr->RaceDescPtr->init_weapon_func) (&Ship, Weapon); + + if ((n = num_weapons)) + { + HELEMENT *WeaponPtr, w; + //STARSHIP *StarShipPtr; + ELEMENT *EPtr; + + WeaponPtr = &Weapon[0]; + do + { + w = *WeaponPtr; + if (w) + { + LockElement (w, &EPtr); + if (EPtr->state_flags & APPEARING) + { + EPtr->next = EPtr->current; + InitIntersectStartPoint (EPtr); + InitIntersectEndPoint (EPtr); + InitIntersectFrame (EPtr); + } + + if (PlotIntercept (EPtr, OtherPtr, + EPtr->life_span, margin_of_error)) + { + UnlockElement (w); + break; + } + + UnlockElement (w); + FreeElement (w); + } + ++WeaponPtr; + } while (--n); + + if ((num_weapons = n)) + { + do + { + w = *WeaponPtr++; + if (w) + FreeElement (w); + } while (--n); + } + } + + return (num_weapons > 0); +} + +void +ship_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern, + COUNT ConcernCounter) +{ + BOOLEAN ShipMoved, ShipFired; + COUNT margin_of_error; + STARSHIP *StarShipPtr; + + GetElementStarShip (ShipPtr, &StarShipPtr); + + ShipMoved = TRUE; + if (ShipPtr->turn_wait == 0) + ShipMoved = FALSE; + if (ShipPtr->thrust_wait == 0) + ShipMoved = FALSE; + + ShipFired = TRUE; + if (StarShipPtr->weapon_counter == 0) + { + StarShipPtr->ship_input_state &= ~WEAPON; + if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON)) + ShipFired = FALSE; + } + + if (StarShipPtr->control & AWESOME_RATING) + margin_of_error = 0; + else if (StarShipPtr->control & GOOD_RATING) + margin_of_error = DISPLAY_TO_WORLD (20); + else /* if (StarShipPtr->control & STANDARD_RATING) */ + margin_of_error = DISPLAY_TO_WORLD (40); + + ObjectsOfConcern += ConcernCounter; + while (ConcernCounter--) + { + --ObjectsOfConcern; + if (ObjectsOfConcern->ObjectPtr) + { + if (!ShipMoved + && (ConcernCounter != ENEMY_WEAPON_INDEX + || ObjectsOfConcern->MoveState == PURSUE + || (ObjectsOfConcern->ObjectPtr->state_flags & CREW_OBJECT) + || MANEUVERABILITY ( + &StarShipPtr->RaceDescPtr->cyborg_control + ) >= MEDIUM_SHIP)) + { + ship_movement (ShipPtr, ObjectsOfConcern); + ShipMoved = TRUE; + } + if (!ShipFired + && (ConcernCounter == ENEMY_SHIP_INDEX + || (ConcernCounter == ENEMY_WEAPON_INDEX + && ObjectsOfConcern->MoveState != AVOID +#ifdef NEVER + && !(StarShipPtr->control & STANDARD_RATING) +#endif /* NEVER */ + ))) + { + ShipFired = ship_weapons (ShipPtr, + ObjectsOfConcern->ObjectPtr, margin_of_error); + if (ShipFired) + StarShipPtr->ship_input_state |= WEAPON; + } + } + } +} + +BOOLEAN +TurnShip (ELEMENT *ShipPtr, COUNT angle) +{ + COUNT f, ship_delta_facing; + STARSHIP *StarShipPtr; + + GetElementStarShip (ShipPtr, &StarShipPtr); + f = StarShipPtr->ShipFacing; + ship_delta_facing = NORMALIZE_FACING (ANGLE_TO_FACING (angle) - f); + if (ship_delta_facing) + { + if (ship_delta_facing == ANGLE_TO_FACING (HALF_CIRCLE)) + ship_delta_facing = + NORMALIZE_FACING (ship_delta_facing + + (TFB_Random () & 1 ? + ANGLE_TO_FACING (OCTANT >> 1) : + -ANGLE_TO_FACING (OCTANT >> 1))); + + if (ship_delta_facing < ANGLE_TO_FACING (HALF_CIRCLE)) + { + StarShipPtr->ship_input_state |= RIGHT; + ++f; + ShipPtr->next.image.frame = + IncFrameIndex (ShipPtr->current.image.frame); + } + else + { + StarShipPtr->ship_input_state |= LEFT; + --f; + ShipPtr->next.image.frame = + DecFrameIndex (ShipPtr->current.image.frame); + } + +#ifdef NOTYET + if (((StarShipPtr->ship_input_state & (LEFT | RIGHT)) + ^ (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) == (LEFT | RIGHT)) + StarShipPtr->ship_input_state &= ~(LEFT | RIGHT); + else +#endif /* NOTYET */ + { + StarShipPtr->ShipFacing = NORMALIZE_FACING (f); + + return (TRUE); + } + } + + return (FALSE); +} + +BOOLEAN +ThrustShip (ELEMENT *ShipPtr, COUNT angle) +{ + BOOLEAN ShouldThrust; + STARSHIP *StarShipPtr; + + GetElementStarShip (ShipPtr, &StarShipPtr); + if (StarShipPtr->ship_input_state & THRUST) + ShouldThrust = TRUE; + else if (NORMALIZE_FACING (ANGLE_TO_FACING (angle) + - ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity))) == 0 + && (StarShipPtr->cur_status_flags + & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)) + && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL)) + ShouldThrust = FALSE; + else + { + SIZE ship_delta_facing; + + ship_delta_facing = + NORMALIZE_FACING (ANGLE_TO_FACING (angle) + - StarShipPtr->ShipFacing + ANGLE_TO_FACING (QUADRANT)); + if (ship_delta_facing == ANGLE_TO_FACING (QUADRANT) + || ((StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED) + && ship_delta_facing <= ANGLE_TO_FACING (HALF_CIRCLE))) + ShouldThrust = TRUE; + else + ShouldThrust = FALSE; + } + + if (ShouldThrust) + { + inertial_thrust (ShipPtr); + + StarShipPtr->ship_input_state |= THRUST; + } + + return (ShouldThrust); +} + +void +Pursue (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr) +{ + BYTE maneuver_state; + COUNT desired_thrust_angle, desired_turn_angle; + SIZE delta_x, delta_y; + SIZE ship_delta_x, ship_delta_y; + SIZE other_delta_x, other_delta_y; + ELEMENT *OtherObjPtr; + VELOCITY_DESC ShipVelocity, OtherVelocity; + + ShipVelocity = ShipPtr->velocity; + GetNextVelocityComponents (&ShipVelocity, + &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn); + ShipPtr->next.location.x = + ShipPtr->current.location.x + ship_delta_x; + ShipPtr->next.location.y = + ShipPtr->current.location.y + ship_delta_y; + + OtherObjPtr = EvalDescPtr->ObjectPtr; + OtherVelocity = OtherObjPtr->velocity; + GetNextVelocityComponents (&OtherVelocity, + &other_delta_x, &other_delta_y, EvalDescPtr->which_turn); + + delta_x = (OtherObjPtr->current.location.x + other_delta_x) + - ShipPtr->next.location.x; + delta_y = (OtherObjPtr->current.location.y + other_delta_y) + - ShipPtr->next.location.y; + delta_x = WRAP_DELTA_X (delta_x); + delta_y = WRAP_DELTA_Y (delta_y); + desired_thrust_angle = ARCTAN (delta_x, delta_y); + + maneuver_state = 0; + if (ShipPtr->turn_wait == 0) + maneuver_state |= LEFT | RIGHT; + if (ShipPtr->thrust_wait == 0 + && ((OtherObjPtr->state_flags & PLAYER_SHIP) + || elementsOfSamePlayer (OtherObjPtr, ShipPtr) + || OtherObjPtr->preprocess_func == crew_preprocess)) + maneuver_state |= THRUST; + + desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE); + /* other player's ship */ + if ((OtherObjPtr->state_flags & PLAYER_SHIP) + && OtherObjPtr->mass_points <= MAX_SHIP_MASS) + { + STARSHIP *StarShipPtr; + STARSHIP *EnemyStarShipPtr; + + GetElementStarShip (ShipPtr, &StarShipPtr); + GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr); + if ((MANEUVERABILITY ( + &StarShipPtr->RaceDescPtr->cyborg_control + ) >= FAST_SHIP + && WEAPON_RANGE (&StarShipPtr->RaceDescPtr->cyborg_control) + > CLOSE_RANGE_WEAPON) + || (EvalDescPtr->which_turn >= 24 + && (StarShipPtr->RaceDescPtr->characteristics.max_thrust * 2 / 3 < + EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust + || (EnemyStarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)))) + { + UWORD ship_flags; + + ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags; + /* you're maneuverable */ + if (MANEUVERABILITY ( + &StarShipPtr->RaceDescPtr->cyborg_control + ) >= MEDIUM_SHIP) + { + UWORD fire_flags; + COUNT facing; + + for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing; + fire_flags <= FIRES_LEFT; + fire_flags <<= 1, facing += QUADRANT) + { + if + ( + /* he's dangerous in this direction */ + (ship_flags & fire_flags) + /* he's facing direction you want to go */ + && NORMALIZE_ANGLE ( + desired_turn_angle - facing + OCTANT + ) <= QUADRANT + && ( + /* he's moving */ + (other_delta_x != 0 || other_delta_y != 0) + && + /* he's coasting backwards */ + NORMALIZE_ANGLE ( + (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE) + - facing + (OCTANT + (OCTANT >> 1))) + <= ((OCTANT + (OCTANT >> 1)) << 1)) + ) + { + /* catch him on the back side */ + desired_thrust_angle = desired_turn_angle; + break; + } + } + } + + if (desired_thrust_angle != desired_turn_angle + && (other_delta_x || other_delta_y) + && EvalDescPtr->which_turn >= 24 + && NORMALIZE_ANGLE (desired_thrust_angle + - GetVelocityTravelAngle (&OtherVelocity) + + OCTANT) <= QUADRANT + && ((NORMALIZE_ANGLE ( + GetVelocityTravelAngle (&OtherVelocity) + - GetVelocityTravelAngle (&ShipVelocity) + + OCTANT) <= QUADRANT + && (((StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED) + && !(StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)) + || (ship_flags & DONT_CHASE))) + || NORMALIZE_ANGLE ( + desired_turn_angle + - FACING_TO_ANGLE (StarShipPtr->ShipFacing) + + OCTANT) <= QUADRANT)) + desired_thrust_angle = desired_turn_angle; + } + } + + if (maneuver_state & (LEFT | RIGHT)) + TurnShip (ShipPtr, desired_thrust_angle); + + if (maneuver_state & THRUST) + ThrustShip (ShipPtr, desired_thrust_angle); +} + +void +Entice (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr) +{ + BYTE maneuver_state; + COUNT desired_thrust_angle, desired_turn_angle; + COUNT cone_of_fire, travel_angle; + SIZE delta_x, delta_y; + SIZE ship_delta_x, ship_delta_y; + SIZE other_delta_x, other_delta_y; + ELEMENT *OtherObjPtr; + VELOCITY_DESC ShipVelocity, OtherVelocity; + STARSHIP *StarShipPtr; + RACE_DESC *RDPtr; + + ShipVelocity = ShipPtr->velocity; + GetNextVelocityComponents (&ShipVelocity, + &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn); + ShipPtr->next.location.x = + ShipPtr->current.location.x + ship_delta_x; + ShipPtr->next.location.y = + ShipPtr->current.location.y + ship_delta_y; + + OtherObjPtr = EvalDescPtr->ObjectPtr; + OtherVelocity = OtherObjPtr->velocity; + GetNextVelocityComponents (&OtherVelocity, + &other_delta_x, &other_delta_y, EvalDescPtr->which_turn); + + delta_x = (OtherObjPtr->current.location.x + other_delta_x) + - ShipPtr->next.location.x; + delta_y = (OtherObjPtr->current.location.y + other_delta_y) + - ShipPtr->next.location.y; + delta_x = WRAP_DELTA_X (delta_x); + delta_y = WRAP_DELTA_Y (delta_y); + desired_thrust_angle = ARCTAN (delta_x, delta_y); + + maneuver_state = 0; + if (ShipPtr->turn_wait == 0) + maneuver_state |= LEFT | RIGHT; + if (ShipPtr->thrust_wait == 0) + maneuver_state |= THRUST; + + delta_x = ship_delta_x - other_delta_x; + delta_y = ship_delta_y - other_delta_y; + travel_angle = ARCTAN (delta_x, delta_y); + desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE); + + GetElementStarShip (ShipPtr, &StarShipPtr); + RDPtr = StarShipPtr->RaceDescPtr; + if (EvalDescPtr->MoveState == AVOID) + { + desired_turn_angle = + NORMALIZE_ANGLE (desired_turn_angle - EvalDescPtr->facing); + + if (NORMALIZE_FACING (ANGLE_TO_FACING (desired_turn_angle))) + { + if (desired_turn_angle <= HALF_CIRCLE) + desired_thrust_angle = RIGHT; + else /* if (desired_turn_angle > HALF_CIRCLE) */ + desired_thrust_angle = LEFT; + } + else + { + desired_turn_angle = NORMALIZE_ANGLE ( + FACING_TO_ANGLE (StarShipPtr->ShipFacing) + - EvalDescPtr->facing + ); + if ((desired_turn_angle & (HALF_CIRCLE - 1)) == 0) + desired_thrust_angle = TFB_Random () & 1 ? RIGHT : LEFT; + else + desired_thrust_angle = desired_turn_angle < HALF_CIRCLE ? RIGHT : LEFT; + } + + if (desired_thrust_angle == LEFT) + { +#define FLANK_LEFT -QUADRANT +#define SHIP_LEFT -OCTANT + desired_thrust_angle = EvalDescPtr->facing + + FLANK_LEFT - (SHIP_LEFT >> 1); + } + else + { +#define FLANK_RIGHT QUADRANT +#define SHIP_RIGHT OCTANT + desired_thrust_angle = EvalDescPtr->facing + + FLANK_RIGHT - (SHIP_RIGHT >> 1); + } + + desired_thrust_angle = NORMALIZE_ANGLE (desired_thrust_angle); + } + else if (GRAVITY_MASS (OtherObjPtr->mass_points)) + { + COUNT planet_facing; + + planet_facing = NORMALIZE_FACING (ANGLE_TO_FACING (desired_thrust_angle)); + cone_of_fire = NORMALIZE_FACING ( + planet_facing + - StarShipPtr->ShipFacing + + ANGLE_TO_FACING (QUADRANT)); + + if (RDPtr->characteristics.thrust_increment != + RDPtr->characteristics.max_thrust) + maneuver_state &= ~THRUST; + + /* if not pointing towards planet */ + if (cone_of_fire > ANGLE_TO_FACING (QUADRANT << 1)) + desired_turn_angle = desired_thrust_angle; + /* if pointing directly at planet */ + else if (cone_of_fire == ANGLE_TO_FACING (QUADRANT) + && NORMALIZE_FACING (ANGLE_TO_FACING (travel_angle)) != planet_facing) + desired_turn_angle = travel_angle; + else if (cone_of_fire == 0 + || cone_of_fire == ANGLE_TO_FACING (QUADRANT << 1) + || (!(maneuver_state & THRUST) + && (cone_of_fire < ANGLE_TO_FACING (OCTANT) + || cone_of_fire > ANGLE_TO_FACING ((QUADRANT << 1) - OCTANT)))) + { + desired_turn_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing); + if (NORMALIZE_ANGLE (desired_turn_angle + - travel_angle + QUADRANT) > HALF_CIRCLE) + desired_turn_angle = travel_angle; + if (ShipPtr->thrust_wait == 0) + maneuver_state |= THRUST; + } + + desired_thrust_angle = desired_turn_angle; + } + else + { + COUNT WRange; + + WRange = WEAPON_RANGE ( + &RDPtr->cyborg_control + ); + + cone_of_fire = NORMALIZE_ANGLE (desired_turn_angle + - EvalDescPtr->facing + OCTANT); + if (OtherObjPtr->state_flags & PLAYER_SHIP) + { + UWORD fire_flags, ship_flags; + COUNT facing; + STARSHIP *EnemyStarShipPtr; + + GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr); + ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags; + for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing; + fire_flags <= FIRES_LEFT; + fire_flags <<= 1, facing += QUADRANT) + { + if + ( + /* he's dangerous in this direction */ + (ship_flags & fire_flags) + /* he's facing direction you want to go */ + && (cone_of_fire = NORMALIZE_ANGLE ( + desired_turn_angle - facing + OCTANT + )) <= QUADRANT + /* he's moving */ + && ((other_delta_x != 0 || other_delta_y != 0) + /* he's coasting backwards */ + && NORMALIZE_ANGLE ( + (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE) + - facing + OCTANT) <= QUADRANT) + ) + { + /* need to be close for a kill */ + if (WRange < LONG_RANGE_WEAPON + && EvalDescPtr->which_turn <= 32) + { + /* catch him on the back side */ + desired_thrust_angle = desired_turn_angle; + goto DoManeuver; + } + + break; + } + } + + if (EvalDescPtr->which_turn <= 8 + && RDPtr->characteristics.max_thrust <= + EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust) + goto DoManeuver; + } + + if + ( +#ifdef NOTYET + WRange < LONG_RANGE_WEAPON + && +#endif /* NOTYET */ + /* not at full speed */ + !(StarShipPtr->cur_status_flags + & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)) + && (PlotIntercept ( + ShipPtr, OtherObjPtr, 40, CLOSE_RANGE_WEAPON << 1 + ) +#ifdef NOTYET + || + ( + /* object's facing direction you want to go */ + cone_of_fire <= QUADRANT + /* and you're basically going in that direction */ + && (travel_angle == FULL_CIRCLE + || NORMALIZE_ANGLE (travel_angle + - desired_thrust_angle + QUADRANT) <= HALF_CIRCLE) + /* and object's in range */ + && PlotIntercept (ShipPtr, OtherObjPtr, 1, WRange) + ) +#endif /* NOTYET */ + ) + ) + { + if + ( + /* pointed straight at him */ + NORMALIZE_ANGLE (desired_thrust_angle + - FACING_TO_ANGLE (StarShipPtr->ShipFacing) + OCTANT) <= QUADRANT + /* or not exposed to business end */ + || cone_of_fire > QUADRANT + ) + { + desired_thrust_angle = desired_turn_angle; + } + else + { +#ifdef NOTYET + if + ( + travel_angle != FULL_CIRCLE + && NORMALIZE_ANGLE (travel_angle + - desired_turn_angle + OCTANT) <= QUADRANT + ) + { + desired_turn_angle = + NORMALIZE_ANGLE ((EvalDescPtr->facing + HALF_CIRCLE) + + (travel_angle - desired_turn_angle)); + if (!(maneuver_state & (LEFT | RIGHT))) + maneuver_state &= ~THRUST; + } + + if (maneuver_state & (LEFT | RIGHT)) + { + TurnShip (ShipPtr, desired_turn_angle); + maneuver_state &= ~(LEFT | RIGHT); + } +#endif /* NOTYET */ + + desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing); +desired_turn_angle = desired_thrust_angle; + } + } + else if ((cone_of_fire = PlotIntercept ( + ShipPtr, OtherObjPtr, 10, WRange +#ifdef OLD + - (WRange >> 3) +#else /* !OLD */ + - (WRange >> 2) +#endif /* OLD */ + ))) + { + if (RDPtr->characteristics.thrust_increment != + RDPtr->characteristics.max_thrust + /* and already at full speed */ + && (StarShipPtr->cur_status_flags + & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)) + /* and facing away from enemy */ + && (NORMALIZE_ANGLE (desired_turn_angle + - ARCTAN (ship_delta_x, ship_delta_y) + + (OCTANT + 2)) <= ((OCTANT + 2) << 1) + /* or not on collision course */ + || !PlotIntercept ( + ShipPtr, OtherObjPtr, 30, CLOSE_RANGE_WEAPON << 1 + ))) + maneuver_state &= ~THRUST; + /* veer off */ + else if (cone_of_fire == 1 + || RDPtr->characteristics.thrust_increment != + RDPtr->characteristics.max_thrust) + { + if (maneuver_state & (LEFT | RIGHT)) + { + TurnShip (ShipPtr, desired_turn_angle); + maneuver_state &= ~(LEFT | RIGHT); + } + + if (NORMALIZE_ANGLE (desired_thrust_angle + - ARCTAN (ship_delta_x, ship_delta_y) + + (OCTANT + 2)) <= ((OCTANT + 2) << 1)) + desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing); + else + desired_thrust_angle = desired_turn_angle; + } + } + } + +DoManeuver: + if (maneuver_state & (LEFT | RIGHT)) + TurnShip (ShipPtr, desired_thrust_angle); + + if (maneuver_state & THRUST) + ThrustShip (ShipPtr, desired_thrust_angle); +} + +void +Avoid (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr) +{ + (void) ShipPtr; /* Satisfying compiler (unused parameter) */ + (void) EvalDescPtr; /* Satisfying compiler (unused parameter) */ +} + +BATTLE_INPUT_STATE +tactical_intelligence (ComputerInputContext *context, STARSHIP *StarShipPtr) +{ + ELEMENT *ShipPtr; + ELEMENT Ship; + COUNT ShipFacing; + HELEMENT hElement, hNextElement; + COUNT ConcernCounter; + EVALUATE_DESC ObjectsOfConcern[10]; + BOOLEAN ShipMoved, UltraManeuverable; + STARSHIP *EnemyStarShipPtr; + RACE_DESC *RDPtr; + RACE_DESC *EnemyRDPtr; + + RDPtr = StarShipPtr->RaceDescPtr; + + if (RDPtr->cyborg_control.ManeuverabilityIndex == 0) + InitCyborg (StarShipPtr); + + LockElement (StarShipPtr->hShip, &ShipPtr); + if (RDPtr->ship_info.crew_level == 0 + || GetPrimType (&DisplayArray[ShipPtr->PrimIndex]) == NO_PRIM) + { + UnlockElement (StarShipPtr->hShip); + return (0); + } + + ShipMoved = TRUE; + /* Disable ship's special completely for the Standard AI */ + if (StarShipPtr->control & STANDARD_RATING) + ++StarShipPtr->special_counter; + +#ifdef DEBUG_CYBORG +if (!(ShipPtr->state_flags & FINITE_LIFE) + && ShipPtr->life_span == NORMAL_LIFE) + ShipPtr->life_span += 2; /* make ship invulnerable */ +#endif /* DEBUG_CYBORG */ + Ship = *ShipPtr; + UnlockElement (StarShipPtr->hShip); + ShipFacing = StarShipPtr->ShipFacing; + + for (ConcernCounter = 0; + ConcernCounter <= FIRST_EMPTY_INDEX; ++ConcernCounter) + { + ObjectsOfConcern[ConcernCounter].ObjectPtr = 0; + ObjectsOfConcern[ConcernCounter].MoveState = NO_MOVEMENT; + ObjectsOfConcern[ConcernCounter].which_turn = (COUNT)~0; + } + --ConcernCounter; + + UltraManeuverable = (BOOLEAN)( + RDPtr->characteristics.thrust_increment == + RDPtr->characteristics.max_thrust + && MANEUVERABILITY (&RDPtr->cyborg_control) >= MEDIUM_SHIP + ); + + if (Ship.turn_wait == 0) + { + ShipMoved = FALSE; + StarShipPtr->ship_input_state &= ~(LEFT | RIGHT); + } + if (Ship.thrust_wait == 0) + { + ShipMoved = FALSE; + StarShipPtr->ship_input_state &= ~THRUST; + } + + for (hElement = GetHeadElement (); + hElement != 0; hElement = hNextElement) + { + EVALUATE_DESC ed; + + ed.MoveState = NO_MOVEMENT; + + LockElement (hElement, &ed.ObjectPtr); + hNextElement = GetSuccElement (ed.ObjectPtr); + if (CollisionPossible (ed.ObjectPtr, &Ship)) + { + SIZE dx, dy; + + dx = ed.ObjectPtr->next.location.x + - Ship.next.location.x; + dy = ed.ObjectPtr->next.location.y + - Ship.next.location.y; + dx = WRAP_DELTA_X (dx); + dy = WRAP_DELTA_Y (dy); + if (GRAVITY_MASS (ed.ObjectPtr->mass_points)) + { + COUNT maneuver_turn, ship_bounds; + RECT ship_footprint = {{0, 0}, {0, 0}}; + + if (UltraManeuverable) + maneuver_turn = 16; + else if (MANEUVERABILITY (&RDPtr->cyborg_control) <= MEDIUM_SHIP) + maneuver_turn = 48; + else + maneuver_turn = 32; + + GetFrameRect (SetAbsFrameIndex ( + Ship.IntersectControl.IntersectStamp.frame, 0 + ), &ship_footprint); + ship_bounds = (COUNT)(ship_footprint.extent.width + + ship_footprint.extent.height); + + if (!ShipMoved && (ed.which_turn = + PlotIntercept (ed.ObjectPtr, &Ship, maneuver_turn, + DISPLAY_TO_WORLD (30 + (ship_bounds * 3 /* << 2 */))))) + { + if (ed.which_turn > 1 + || PlotIntercept (ed.ObjectPtr, &Ship, 1, + DISPLAY_TO_WORLD (35 + ship_bounds)) + || PlotIntercept (ed.ObjectPtr, &Ship, + maneuver_turn << 1, + DISPLAY_TO_WORLD (40 + ship_bounds)) > 1) + { + ed.facing = ARCTAN (-dx, -dy); + if (UltraManeuverable) + ed.MoveState = AVOID; + else // Try a gravity whip + ed.MoveState = ENTICE; + + ObjectsOfConcern[GRAVITY_MASS_INDEX] = ed; + } + else if (!UltraManeuverable && + !IsVelocityZero (&Ship.velocity)) + { // Try an orbital insertion, don't thrust + ++Ship.thrust_wait; + if (Ship.turn_wait) + ShipMoved = TRUE; + } + } + } + else if (ed.ObjectPtr->state_flags & PLAYER_SHIP) + { + GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr); + EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr; + if (EnemyRDPtr->cyborg_control.ManeuverabilityIndex == 0) + InitCyborg (EnemyStarShipPtr); + + ed.which_turn = WORLD_TO_TURN ( + square_root ((long)dx * dx + (long)dy * dy)); + if (ed.which_turn > + ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn) + { + UnlockElement (hElement); + continue; + } + else if (ed.which_turn == 0) + ed.which_turn = 1; + + ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr = ed.ObjectPtr; + ObjectsOfConcern[ENEMY_SHIP_INDEX].facing = +#ifdef MAYBE + OBJECT_CLOAKED (ed.ObjectPtr) ? + GetVelocityTravelAngle (&ed.ObjectPtr->velocity) : +#endif /* MAYBE */ + FACING_TO_ANGLE (EnemyStarShipPtr->ShipFacing); + ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn = ed.which_turn; + + if (ShipMoved + || ed.ObjectPtr->mass_points > MAX_SHIP_MASS + || (WEAPON_RANGE (&RDPtr->cyborg_control) < LONG_RANGE_WEAPON + && (WEAPON_RANGE (&RDPtr->cyborg_control) <= CLOSE_RANGE_WEAPON + || (WEAPON_RANGE (&EnemyRDPtr->cyborg_control) >= LONG_RANGE_WEAPON + && (EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON)) + || ( +#ifdef OLD + MANEUVERABILITY (&RDPtr->cyborg_control) < + MANEUVERABILITY (&EnemyRDPtr->cyborg_control) +#else /* !OLD */ + RDPtr->characteristics.max_thrust < + EnemyRDPtr->characteristics.max_thrust +#endif /* !OLD */ + && WEAPON_RANGE (&RDPtr->cyborg_control) < + WEAPON_RANGE (&EnemyRDPtr->cyborg_control))))) + ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = PURSUE; + else + ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = ENTICE; + + if ((EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & IMMEDIATE_WEAPON) + && ship_weapons (ed.ObjectPtr, &Ship, 0)) + { + ed.which_turn = 1; + ed.MoveState = AVOID; + ed.facing = ObjectsOfConcern[ENEMY_SHIP_INDEX].facing; + + ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed; + } + } + else if (ed.ObjectPtr->pParent == 0) + { + if (!(ed.ObjectPtr->state_flags & FINITE_LIFE)) + { + ed.which_turn = WORLD_TO_TURN ( + square_root ((long)dx * dx + (long)dy * dy) + ); + + if (ed.which_turn < + ObjectsOfConcern[FIRST_EMPTY_INDEX].which_turn) + { + ed.MoveState = PURSUE; + ed.facing = GetVelocityTravelAngle ( + &ed.ObjectPtr->velocity + ); + + ObjectsOfConcern[FIRST_EMPTY_INDEX] = ed; + } + } + } + else if (!elementsOfSamePlayer (ed.ObjectPtr, &Ship) + && ed.ObjectPtr->preprocess_func != crew_preprocess + && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn > 1 + && ed.ObjectPtr->life_span > 0) + { + GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr); + EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr; + if (((EnemyRDPtr->ship_info.ship_flags & SEEKING_WEAPON) + && ed.ObjectPtr->next.image.farray != + EnemyRDPtr->ship_data.special) + || ((EnemyRDPtr->ship_info.ship_flags & SEEKING_SPECIAL) + && ed.ObjectPtr->next.image.farray == + EnemyRDPtr->ship_data.special)) + { + if ((!(ed.ObjectPtr->state_flags & (FINITE_LIFE | CREW_OBJECT)) + && RDPtr->characteristics.max_thrust > DISPLAY_TO_WORLD (8)) + || NORMALIZE_ANGLE (GetVelocityTravelAngle ( + &ed.ObjectPtr->velocity + ) - ARCTAN (-dx, -dy) + + QUADRANT) > HALF_CIRCLE) + ed.which_turn = 0; + else + { + ed.which_turn = WORLD_TO_TURN ( + square_root ((long)dx * dx + (long)dy * dy) + ); + + ed.MoveState = ENTICE; + if (UltraManeuverable) + { + if (ed.which_turn == 0) + ed.which_turn = 1; + else if (ed.which_turn > 16) + ed.which_turn = 0; + } + else if (ed.which_turn == 0) + ed.which_turn = 1; + else if (ed.which_turn > 16 + || (MANEUVERABILITY ( + &RDPtr->cyborg_control + ) > MEDIUM_SHIP + && ed.which_turn > 8)) + ed.which_turn = 0; + } + } + else if (!(StarShipPtr->control & AWESOME_RATING)) + ed.which_turn = 0; + else + { + ed.which_turn = + PlotIntercept (ed.ObjectPtr, + &Ship, ed.ObjectPtr->life_span, + DISPLAY_TO_WORLD (40)); + ed.MoveState = AVOID; + } + + if (ed.which_turn > 0 + && (ed.which_turn < + ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn + || (ed.which_turn == + ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn + && ed.MoveState == AVOID))) + { + ed.facing = GetVelocityTravelAngle ( + &ed.ObjectPtr->velocity + ); + + ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed; + } + } + else if ((ed.ObjectPtr->state_flags & CREW_OBJECT) + && ((!(ed.ObjectPtr->state_flags & IGNORE_SIMILAR) + && elementsOfSamePlayer (ed.ObjectPtr, &Ship)) + || ed.ObjectPtr->preprocess_func == crew_preprocess) + && ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn > 1) + { + ed.which_turn = WORLD_TO_TURN ( + square_root ((long)dx * dx + (long)dy * dy) + ); + + if (ed.which_turn == 0) + ed.which_turn = 1; + + if (ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn > + ed.which_turn + && (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 32 + || (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 8 + && StarShipPtr->hShip == ed.ObjectPtr->hTarget))) + { + ed.MoveState = PURSUE; + ed.facing = 0; + ObjectsOfConcern[CREW_OBJECT_INDEX] = ed; + } + } + } + UnlockElement (hElement); + } + + RDPtr->cyborg_control.intelligence_func (&Ship, ObjectsOfConcern, + ConcernCounter); +#ifdef DEBUG_CYBORG +StarShipPtr->ship_input_state &= ~SPECIAL; +#endif /* DEBUG_CYBORG */ + + StarShipPtr->ShipFacing = ShipFacing; + { + BATTLE_INPUT_STATE InputState; + + InputState = 0; + if (StarShipPtr->ship_input_state & LEFT) + InputState |= BATTLE_LEFT; + else if (StarShipPtr->ship_input_state & RIGHT) + InputState |= BATTLE_RIGHT; + if (StarShipPtr->ship_input_state & THRUST) + InputState |= BATTLE_THRUST; + if (StarShipPtr->ship_input_state & WEAPON) + InputState |= BATTLE_WEAPON; + if (StarShipPtr->ship_input_state & SPECIAL) + InputState |= BATTLE_SPECIAL; + + (void) context; + return (InputState); + } +} + |