FS2_Open
Open source remastering of the Freespace 2 engine
aicode.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
20 #include "ai/ai.h"
21 #include "ai/ai_profiles.h"
22 #include "ai/aibig.h"
23 #include "ai/aigoals.h"
24 #include "ai/aiinternal.h"
25 #include "asteroid/asteroid.h"
26 #include "autopilot/autopilot.h"
27 #include "cmeasure/cmeasure.h"
28 #include "debugconsole/console.h"
29 #include "freespace2/freespace.h"
31 #include "gamesnd/gamesnd.h"
32 #include "globalincs/linklist.h"
33 #include "hud/hud.h"
34 #include "hud/hudets.h"
35 #include "hud/hudlock.h"
36 #include "iff_defs/iff_defs.h"
37 #include "io/joy_ff.h"
38 #include "io/timer.h"
39 #include "localization/localize.h"
40 #include "math/fvi.h"
41 #include "math/staticrand.h"
42 #include "mission/missiongoals.h"
43 #include "mission/missionlog.h"
44 #include "mission/missionmessage.h"
45 #include "mission/missionparse.h"
47 #include "model/model.h"
48 #include "network/multi.h"
49 #include "network/multimsgs.h"
50 #include "network/multiutil.h"
51 #include "object/deadobjectdock.h"
52 #include "object/objcollide.h"
53 #include "object/object.h"
54 #include "object/objectdock.h"
55 #include "object/objectshield.h"
56 #include "object/waypoint.h"
57 #include "parse/parselo.h"
58 #include "physics/physics.h"
59 #include "playerman/player.h"
60 #include "render/3d.h"
61 #include "ship/afterburner.h"
62 #include "ship/awacs.h"
63 #include "ship/ship.h"
64 #include "ship/shipfx.h"
65 #include "ship/shiphit.h"
66 #include "weapon/beam.h"
67 #include "weapon/flak.h"
68 #include "weapon/swarm.h"
69 #include "weapon/weapon.h"
70 #include <map>
71 #include <limits.h>
72 
73 
74 #ifdef _MSC_VER
75 #pragma optimize("", off)
76 #pragma auto_inline(off)
77 #endif
78 
79 #define UNINITIALIZED_VALUE -99999.9f
80 
81 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
82 
83 #define AICODE_SMALL_MAGNITUDE 0.001f // cosider a vector NULL if mag is less than this
84 
85 #define NEXT_REARM_TIMESTAMP (60*1000) // Ships will re-request rearm, typically, after this long.
86 
87 #define CIRCLE_STRAFE_MAX_DIST 300.0f //Maximum distance for circle strafe behavior.
88 
89 // AIM_CHASE submode defines
90 // SM_STEALTH_FIND
91 #define SM_SF_AHEAD 0
92 #define SM_SF_BEHIND 1
93 #define SM_SF_BAIL 2
94 
95 // SM_STEALTH_SWEEP
96 #define SM_SS_SET_GOAL -1
97 #define SM_SS_BOX0 0
98 #define SM_SS_LR 1
99 #define SM_SS_UL 2
100 #define SM_SS_BOX1 3
101 #define SM_SS_UR 4
102 #define SM_SS_LL 5
103 #define SM_SS_BOX2 6
104 #define SM_SS_DONE 7
105 
106 //XSTR:OFF
107 
109  "CHASE",
110  "EVADE",
111  "GET_BEHIND",
112  "CHASE_LONG",
113  "SQUIGGLE",
114  "GUARD",
115  "AVOID",
116  "WAYPOINTS",
117  "DOCK",
118  "NONE",
119  "BIGSHIP",
120  "PATH",
121  "BE_REARMED",
122  "SAFETY",
123  "EV_WEAPON",
124  "STRAFE",
125  "PLAY_DEAD",
126  "BAY_EMERGE",
127  "BAY_DEPART",
128  "SENTRYGUN",
129  "WARP_OUT",
130 };
131 
132 // Submode text is only valid for CHASE mode.
133 char *Submode_text[] = {
134 "undefined",
135 "CONT_TURN",
136 "ATTACK ",
137 "E_SQUIG ",
138 "E_BRAKE ",
139 "EVADE ",
140 "SUP_ATTAK",
141 "AVOID ",
142 "BEHIND ",
143 "GET_AWAY ",
144 "E_WEAPON ",
145 "FLY_AWAY ",
146 "ATK_4EVER",
147 "STLTH_FND",
148 "STLTH_SWP",
149 "BIG_APPR",
150 "BIG_CIRC",
151 "BIG_PARL"
152 };
153 
155 "ATTACK",
156 "AVOID",
157 "RETREAT1",
158 "RETREAT2",
159 "POSITION"
160 };
161 //XSTR:ON
162 
163 // few forward decs i needed - kazan
164 object * get_wing_leader(int wingnum);
165 int get_wing_index(object *objp, int wingnum);
166 
168 
169 object *Pl_objp;
170 object *En_objp;
171 
172 #define REARM_SOUND_DELAY (3*F1_0) // Amount of time to delay rearm/repair after mode start
173 #define REARM_BREAKOFF_DELAY (3*F1_0) // Amount of time to wait after fully rearmed to breakoff.
174 
175 #define MIN_DIST_TO_WAYPOINT_GOAL 5.0f
176 #define MAX_GUARD_DIST 250.0f
177 #define BIG_GUARD_RADIUS 500.0f
178 
179 #define MAX_EVADE_TIME (15 * 1000) // Max time to evade a weapon.
180 
181 // defines for repair ship stuff.
182 #define MAX_REPAIR_SPEED 25.0f
183 #define MAX_UNDOCK_ABORT_SPEED 2.0f
184 
185 // defines for EMP effect stuff
186 #define MAX_EMP_INACCURACY 50.0f
187 
188 // defines for stealth
189 #define MAX_STEALTH_INACCURACY 50.0f // at max view dist
190 #define STEALTH_MAX_VIEW_DIST 400 // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
191 #define STEALTH_VIEW_CONE_DOT 0.707 // (half angle of 45 degrees)
192 
197 
199 int AI_watch_object = 0; // Debugging, object to spew debug info for.
200 int Mission_all_attack = 0; // !0 means all teams attack all teams.
201 
202 // Constant for flag, Name of flag, In flags or flags2
204  {AIF_NO_DYNAMIC, "no-dynamic", 1, },
205  {AIF_FREE_AFTERBURNER_USE, "free-afterburner-use", 1, },
206 };
207 
208 const char *Skill_level_names(int level, int translate)
209 {
210  const char *str = NULL;
211 
212  #if NUM_SKILL_LEVELS != 5
213  #error Number of skill levels is wrong!
214  #endif
215 
216  if(translate){
217  switch( level ) {
218  case 0:
219  str = XSTR("Very Easy", 469);
220  break;
221  case 1:
222  str = XSTR("Easy", 470);
223  break;
224  case 2:
225  str = XSTR("Medium", 471);
226  break;
227  case 3:
228  str = XSTR("Hard", 472);
229  break;
230  case 4:
231  str = XSTR("Insane", 473);
232  break;
233  default:
234  Int3();
235  }
236  } else {
237  switch( level ) {
238  case 0:
239  str = NOX("Very Easy");
240  break;
241  case 1:
242  str = NOX("Easy");
243  break;
244  case 2:
245  str = NOX("Medium");
246  break;
247  case 3:
248  str = NOX("Hard");
249  break;
250  case 4:
251  str = NOX("Insane");
252  break;
253  default:
254  Int3();
255  }
256  }
257 
258  return str;
259 }
260 
261 #define DELAY_TARGET_TIME (12*1000) // time in milliseconds until a ship can target a new enemy after an order.
262 
264 pnode *Ppfp; // Free pointer in path points.
265 
267 
268 char** Ai_class_names = NULL;
269 
270 typedef struct {
271  int team;
274  char *shipname;
276 
278 
279 int Ai_last_arrive_path; // index of ship_bay path used by last arrival from a fighter bay
280 
281 // forward declarations
282 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
283 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
284 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
285 void ai_cleanup_rearm_mode(object *objp);
288 
289 // The object that is declared to be the leader of the group formation for
290 // the "autopilot"
291 object *Autopilot_flight_leader = NULL;
292 
300 void ai_set_rearm_status(int team, int time)
301 {
302  Assert( time >= 0 );
303 
304  Iff_info[team].ai_rearm_timestamp = timestamp(time * 1000);
305 }
306 
314 {
315  int team;
316 
317  Assert(objp->type == OBJ_SHIP);
318  team = Ships[objp->instance].team;
319 
320  return timestamp_valid(Iff_info[team].ai_rearm_timestamp);
321 }
322 
326 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
327 {
328  Assert(shipname != NULL);
329  int index;
330  huge_fire_info new_info;
331 
332  new_info.weapon_index = weapon_index;
333  new_info.team = team;
334  new_info.max_fire_count = max_fire_count;
335 
336  new_info.shipname = ai_get_goal_target_name( shipname, &index );
337 
338  Ai_huge_fire_info.push_back(new_info);
339 }
340 
348 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
349 {
350  int firer_team, target_signature;
351  ship *firer_ship;
353 
354  Assert( firer_objp->type == OBJ_SHIP );
355  firer_ship = &Ships[firer_objp->instance];
356  firer_team = firer_ship->team;
357 
358  // get target object's signature and try to find it in the list.
359  target_signature = target_objp->signature;
360  for (hfi = Ai_huge_fire_info.begin();hfi != Ai_huge_fire_info.end(); ++hfi ) {
361  int ship_index, signature;
362 
363  if ( hfi->weapon_index == -1 )
364  continue;
365 
366  ship_index = ship_name_lookup( hfi->shipname );
367  if ( ship_index == -1 )
368  continue;
369 
370  signature = Objects[Ships[ship_index].objnum].signature;
371 
372  // sigatures, weapon_index, and team must match
373  if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
374  break;
375  }
376 
377  // return -1 if not found
378  if ( hfi == Ai_huge_fire_info.end() )
379  return -1;
380 
381  // otherwise, we can return the max number of weapons we can fire against target_objps
382  return hfi->max_fire_count;
383 }
384 
389 {
390  Ai_huge_fire_info.clear();
391 }
392 
408 {
409  int i;
410  int pp_xlate[MAX_PATH_POINTS];
411  object *A;
412  ship_obj *so;
413 
414  // Scan all objects and create Path_points xlate table.
415  for (i=0; i<MAX_PATH_POINTS; i++)
416  pp_xlate[i] = 0;
417 
418  // in pp_xlate, mark all used Path_point records
419  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
420  A = &Objects[so->objnum];
421  ship *shipp = &Ships[A->instance];
422  if (shipp->ai_index != -1) {
423  ai_info *aip = &Ai_info[shipp->ai_index];
424 
425  if ((aip->path_length > 0) && (aip->path_start > -1)) {
426 
427  for (i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
428  Assert(pp_xlate[i] == 0); // If this is not 0, then two paths use this point!
429  pp_xlate[i] = 1;
430  }
431  }
432  }
433  }
434 
435  // Now, stuff xlate index in pp_xlate. This is the number to translate any path_start
436  // or path_cur index to.
437  int xlt = 0;
438  for (i=0; i<MAX_PATH_POINTS; i++) {
439  int t = pp_xlate[i];
440 
441  pp_xlate[i] = xlt;
442  if (t != 0)
443  xlt++;
444  }
445 
446  // Update global Path_points free pointer.
447  Ppfp = &Path_points[xlt];
448 
449  // Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
450  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
451  A = &Objects[so->objnum];
452  ship *shipp = &Ships[A->instance];
453  if (shipp->ai_index != -1) {
454  ai_info *aip = &Ai_info[shipp->ai_index];
455 
456  if ((aip->path_length > 0) && (aip->path_start > -1)) {
457  Assert(aip->path_start < MAX_PATH_POINTS);
458  aip->path_start = pp_xlate[aip->path_start];
459 
460  Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
461  aip->path_cur = pp_xlate[aip->path_cur];
462  }
463  }
464  }
465 
466  // Now, compress the buffer.
467  for (i=0; i<MAX_PATH_POINTS; i++)
468  if (i != pp_xlate[i])
469  Path_points[pp_xlate[i]] = Path_points[i];
470 
471 }
472 
477 int hash(unsigned int curval, int newval)
478 {
479  int addval = curval & 1;
480 
481  curval >>= 1;
482  if (addval)
483  curval |= 0x80000000;
484  curval ^= newval;
485 
486  return curval;
487 }
488 
494 {
495  int *ip;
496  unsigned int hashval = 0;
497  int i;
498 
499  ip = (int *) &objp->orient;
500 
501  for (i=0; i<9; i++) {
502  hashval = hash(hashval, *ip);
503  ip++;
504  }
505 
506  ip = (int *) &objp->pos;
507 
508  for (i=0; i<3; i++) {
509  hashval = hash(hashval, *ip);
510  ip++;
511  }
512 
513  return hashval;
514 }
515 
517 {
518  if(Ai_classes != NULL)
519  vm_free(Ai_classes);
520 
521  if(Ai_class_names != NULL)
523 }
524 
530 {
531  int i;
532  for (i = 0; i < NUM_SKILL_LEVELS; i++)
533  {
534  aicp->ai_cmeasure_fire_chance[i] = FLT_MIN;
535  aicp->ai_in_range_time[i] = FLT_MIN;
536  aicp->ai_link_ammo_levels_maybe[i] = FLT_MIN;
537  aicp->ai_link_ammo_levels_always[i] = FLT_MIN;
538  aicp->ai_primary_ammo_burst_mult[i] = FLT_MIN;
539  aicp->ai_link_energy_levels_maybe[i] = FLT_MIN;
540  aicp->ai_link_energy_levels_always[i] = FLT_MIN;
541  aicp->ai_predict_position_delay[i] = INT_MIN;
542  aicp->ai_shield_manage_delay[i] = FLT_MIN;
543  aicp->ai_ship_fire_delay_scale_friendly[i] = FLT_MIN;
544  aicp->ai_ship_fire_delay_scale_hostile[i] = FLT_MIN;
547  aicp->ai_turn_time_scale[i] = FLT_MIN;
548  aicp->ai_glide_attack_percent[i] = FLT_MIN;
549  aicp->ai_circle_strafe_percent[i] = FLT_MIN;
550  aicp->ai_glide_strafe_percent[i] = FLT_MIN;
551  aicp->ai_random_sidethrust_percent[i] = FLT_MIN;
552  aicp->ai_stalemate_time_thresh[i] = FLT_MIN;
553  aicp->ai_stalemate_dist_thresh[i] = FLT_MIN;
554  aicp->ai_chance_to_use_missiles_on_plr[i] = INT_MIN;
555  aicp->ai_max_aim_update_delay[i] = FLT_MIN;
556  aicp->ai_turret_max_aim_update_delay[i] = FLT_MIN;
557  }
558  aicp->ai_profile_flags = 0;
559  aicp->ai_profile_flags_set = 0;
560  aicp->ai_profile_flags2 = 0;
561  aicp->ai_profile_flags2_set = 0;
562 
563  //AI Class autoscale overrides
564  //INT_MIN and FLT_MIN represent the "not set" state
565  for (i = 0; i < NUM_SKILL_LEVELS; i++)
566  {
567  aicp->ai_aburn_use_factor[i] = INT_MIN;
568  aicp->ai_shockwave_evade_chance[i] = FLT_MIN;
569  aicp->ai_get_away_chance[i] = FLT_MIN;
570  aicp->ai_secondary_range_mult[i] = FLT_MIN;
571  }
572  aicp->ai_class_autoscale = true; //Retail behavior is to do the stupid autoscaling
573 }
574 
575 void set_aic_flag(ai_class *aicp, char *name, int flag, int type)
576 {
577  int* flags = (type == AIP_FLAG) ? &(aicp->ai_profile_flags) : &(aicp->ai_profile_flags2);
578  int* set = (type == AIP_FLAG) ? &(aicp->ai_profile_flags_set) : &(aicp->ai_profile_flags2_set);
579 
580  if (optional_string(name))
581  {
582  bool val;
583  stuff_boolean(&val);
584 
585  if (val)
586  *flags |= flag;
587  else
588  *flags &= ~flag;
589 
590  *set |= flag;
591  }
592  else
593  *set &= ~flag;
594 }
595 
597 {
598  ai_class *aicp = &Ai_classes[Num_ai_classes];
599 
600  init_ai_class(aicp);
601 
602  required_string("$Name:");
604 
606 
607  required_string("$accuracy:");
609 
610  required_string("$evasion:");
612 
613  required_string("$courage:");
615 
616  required_string("$patience:");
618 
619  if (optional_string("$Afterburner Use Factor:"))
621 
622  if (optional_string("$Shockwave Evade Chances Per Second:"))
624 
625  if (optional_string("$Get Away Chance:"))
627 
628  if (optional_string("$Secondary Range Multiplier:"))
630 
631  if (optional_string("$Autoscale by AI Class Index:"))
633 
634  //Parse optional values for stuff imported from ai_profiles
635  if (optional_string("$AI Countermeasure Firing Chance:"))
637 
638  if (optional_string("$AI In Range Time:"))
640 
641  if (optional_string("$AI Always Links Ammo Weapons:"))
643 
644  if (optional_string("$AI Maybe Links Ammo Weapons:"))
646 
647  if (optional_string("$Primary Ammo Burst Multiplier:"))
649 
650  if (optional_string("$AI Always Links Energy Weapons:"))
652 
653  if (optional_string("$AI Maybe Links Energy Weapons:"))
655 
656  // represented in fractions of F1_0
657  if (optional_string("$Predict Position Delay:")) {
658  int iLoop;
659  float temp_list[NUM_SKILL_LEVELS];
661  for (iLoop = 0; iLoop < NUM_SKILL_LEVELS; iLoop++)
662  aicp->ai_predict_position_delay[iLoop] = fl2f(temp_list[iLoop]);
663  }
664 
665  if (optional_string("$AI Shield Manage Delay:") || optional_string("$AI Shield Manage Delays:"))
667 
668  if (optional_string("$Friendly AI Fire Delay Scale:"))
670 
671  if (optional_string("$Hostile AI Fire Delay Scale:"))
673 
674  if (optional_string("$Friendly AI Secondary Fire Delay Scale:"))
676 
677  if (optional_string("$Hostile AI Secondary Fire Delay Scale:"))
679 
680  if (optional_string("$AI Turn Time Scale:"))
682 
683  if (optional_string("$Glide Attack Percent:")) {
685  for (int i = 0; i < NUM_SKILL_LEVELS; i++) {
686  if (aicp->ai_glide_attack_percent[i] < 0.0f || aicp->ai_glide_attack_percent[i] > 100.0f) {
687  aicp->ai_glide_attack_percent[i] = 0.0f;
688  Warning(LOCATION, "$Glide Attack Percent should be between 0 and 100.0 (read %f). Setting to 0.", aicp->ai_glide_attack_percent[i]);
689  }
690  aicp->ai_glide_attack_percent[i] /= 100.0;
691  }
692 
693  }
694 
695  if (optional_string("$Circle Strafe Percent:")) {
697  for (int i = 0; i < NUM_SKILL_LEVELS; i++) {
698  if (aicp->ai_circle_strafe_percent[i] < 0.0f || aicp->ai_circle_strafe_percent[i] > 100.0f) {
699  aicp->ai_circle_strafe_percent[i] = 0.0f;
700  Warning(LOCATION, "$Circle Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", aicp->ai_circle_strafe_percent[i]);
701  }
702  aicp->ai_circle_strafe_percent[i] /= 100.0;
703  }
704  }
705 
706  if (optional_string("$Glide Strafe Percent:")) {
708  for (int i = 0; i < NUM_SKILL_LEVELS; i++) {
709  if (aicp->ai_glide_strafe_percent[i] < 0.0f || aicp->ai_glide_strafe_percent[i] > 100.0f) {
710  aicp->ai_glide_strafe_percent[i] = 0.0f;
711  Warning(LOCATION, "$Glide Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", aicp->ai_glide_strafe_percent[i]);
712  }
713  aicp->ai_glide_strafe_percent[i] /= 100.0;
714  }
715  }
716 
717  if (optional_string("$Random Sidethrust Percent:")) {
719  for (int i = 0; i < NUM_SKILL_LEVELS; i++) {
720  if (aicp->ai_random_sidethrust_percent[i] < 0.0f || aicp->ai_random_sidethrust_percent[i] > 100.0f) {
721  aicp->ai_random_sidethrust_percent[i] = 0.0f;
722  Warning(LOCATION, "$Random Sidethrust Percent should be between 0 and 100.0 (read %f). Setting to 0.", aicp->ai_random_sidethrust_percent[i]);
723  }
724  aicp->ai_random_sidethrust_percent[i] /= 100.0;
725  }
726  }
727 
728  if (optional_string("$Stalemate Time Threshold:"))
730 
731  if (optional_string("$Stalemate Distance Threshold:"))
733 
734  if (optional_string("$Chance AI Has to Fire Missiles at Player:"))
736 
737  if (optional_string("$Max Aim Update Delay:"))
739 
740  if (optional_string("$Turret Max Aim Update Delay:"))
742 
743  set_aic_flag(aicp, "$big ships can attack beam turrets on untargeted ships:", AIPF_BIG_SHIPS_CAN_ATTACK_BEAM_TURRETS_ON_UNTARGETED_SHIPS, AIP_FLAG);
744 
745  set_aic_flag(aicp, "$smart primary weapon selection:", AIPF_SMART_PRIMARY_WEAPON_SELECTION, AIP_FLAG);
746 
747  set_aic_flag(aicp, "$smart secondary weapon selection:", AIPF_SMART_SECONDARY_WEAPON_SELECTION, AIP_FLAG);
748 
749  set_aic_flag(aicp, "$smart shield management:", AIPF_SMART_SHIELD_MANAGEMENT, AIP_FLAG);
750 
751  set_aic_flag(aicp, "$smart afterburner management:", AIPF_SMART_AFTERBURNER_MANAGEMENT, AIP_FLAG);
752 
753  set_aic_flag(aicp, "$allow rapid secondary dumbfire:", AIPF_ALLOW_RAPID_SECONDARY_DUMBFIRE, AIP_FLAG);
754 
755  set_aic_flag(aicp, "$huge turret weapons ignore bombs:", AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS, AIP_FLAG);
756 
757  set_aic_flag(aicp, "$don't insert random turret fire delay:", AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY, AIP_FLAG);
758 
759  set_aic_flag(aicp, "$prevent turrets targeting too distant bombs:", AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE, AIP_FLAG);
760 
761  set_aic_flag(aicp, "$smart subsystem targeting for turrets:", AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS, AIP_FLAG);
762 
763  set_aic_flag(aicp, "$allow turrets target weapons freely:", AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY, AIP_FLAG);
764 
765  set_aic_flag(aicp, "$allow vertical dodge:", AIPF_ALLOW_VERTICAL_DODGE, AIP_FLAG);
766 
767  set_aic_flag(aicp, "$no extra collision avoidance vs player:", AIPF2_NO_SPECIAL_PLAYER_AVOID, AIP_FLAG2);
768 
769  set_aic_flag(aicp, "$all ships manage shields:", AIPF2_ALL_SHIPS_MANAGE_SHIELDS, AIP_FLAG2);
770 }
771 
773 {
774  ai_class *aicp;
775 
776  for (int i = 0; i < Num_ai_classes; i++) {
777  aicp = &Ai_classes[i];
778 
779  Ai_class_names[i] = aicp->name;
780  }
781 }
782 
783 #define AI_CLASS_INCREMENT 10
785 {
786  read_file_text("ai.tbl", CF_TYPE_TABLES);
787  reset_parse();
788 
789  //Just in case parse_aitbl is called twice
790  free_ai_stuff();
791 
792  Num_ai_classes = 0;
794  Ai_classes = (ai_class*) vm_malloc(Num_alloced_ai_classes * sizeof(ai_class));
795  Ai_class_names = (char**) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
796 
797  required_string("#AI Classes");
798 
799  while (required_string_either("#End", "$Name:")) {
800 
801  parse_ai_class();
802 
803  Num_ai_classes++;
804 
806  {
808  Ai_classes = (ai_class*) vm_realloc(Ai_classes, Num_alloced_ai_classes * sizeof(ai_class));
809 
810  // Ai_class_names doesn't realloc all that well so we have to do it the hard way.
811  // Luckily, it's contents can be easily replaced so we don't have to save anything.
813  Ai_class_names = (char **) vm_malloc(Num_alloced_ai_classes * sizeof(char*));
815  }
816  }
817 
818  atexit(free_ai_stuff);
819 }
820 
821 LOCAL int ai_inited = 0;
822 
823 //========================= BOOK-KEEPING FUNCTIONS =======================
824 
828 void ai_init()
829 {
830  if ( !ai_inited ) {
831  // Do the first time initialization stuff here
832  try
833  {
834  parse_aitbl();
835  }
836  catch (const parse::ParseException& e)
837  {
838  mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "ai.tbl", e.what()));
839  }
840 
841  ai_inited = 1;
842  }
843 
844  init_semirand();
845 
846  ai_level_init();
847 }
848 
854 {
855  int i;
856 
857  // Do the stuff to reset all ai stuff here
858  for (i=0; i<MAX_AI_INFO ; i++) {
859  Ai_info[i].shipnum = -1;
860  }
861 
862  Ai_goal_signature = 0;
863 
864  for (i = 0; i < Num_iffs; i++)
865  Iff_info[i].ai_rearm_timestamp = timestamp(-1);
866 
867  // clear out the stuff needed for AI firing powerful secondary weapons
869 
871 }
872 
873 // BEGIN STEALTH
874 // -----------------------------------------------------------------------------
875 
880 {
881  if (objp->type == OBJ_SHIP) {
882  if (Ships[objp->instance].flags2 & SF2_STEALTH) {
883  return true;
884  }
885  }
886 
887  // not stealth ship
888  return false;
889 }
890 
894 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
895 {
896  Assert(is_object_stealth_ship(stealth_objp));
897 
898  // set necessary ai info for new stealth target
899  aip->stealth_last_pos = stealth_objp->pos;
900  aip->stealth_velocity = stealth_objp->phys_info.vel;
902 }
903 
904 // -----------------------------------------------------------------------------
905 // Check whether Pl_objp can see a stealth ship object
906 #define STEALTH_NOT_IN_FRUSTUM 0
907 #define STEALTH_IN_FRUSTUM 1
908 #define STEALTH_FULLY_TARGETABLE 2
909 
914 {
915  switch (Game_skill_level) {
916  case 0: // very easy
917  return 0.65f;
918 
919  case 1: // easy
920  return 0.9f;
921 
922  case 2: // medium
923  return 1.0f;
924 
925  case 3: // hard
926  return 1.1f;
927 
928  case 4: // insane
929  return 1.3f;
930 
931  default:
932  Int3();
933  }
934 
935  return 1.0f;
936 }
937 
942 {
943  switch (Game_skill_level) {
944  case 0: // very easy
945  return 1.3f;
946 
947  case 1: // easy
948  return 1.1f;
949 
950  case 2: // medium
951  return 1.0f;
952 
953  case 3: // hard
954  return 0.9f;
955 
956  case 4: // insane
957  return 0.7f;
958 
959  default:
960  Int3();
961  }
962 
963  return 1.0f;
964 }
965 
966 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
967 {
968  ship *shipp;
969  vec3d vec_to_stealth;
970  float dot_to_stealth, dist_to_stealth, max_stealth_dist;
971 
972  Assert(stealth_objp->type == OBJ_SHIP);
973  shipp = &Ships[stealth_objp->instance];
974  Assert(viewer_objp->type == OBJ_SHIP);
975 
976  // check if stealth ship
977  Assert(shipp->flags2 & SF2_STEALTH);
978 
979  // check if in neb and below awac level for visible
980  if ( !ship_is_visible_by_team(stealth_objp, &Ships[viewer_objp->instance]) ) {
981  vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
982  dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
983  dot_to_stealth = vm_vec_dot(&viewer_objp->orient.vec.fvec, &vec_to_stealth) / dist_to_stealth;
984 
985  // get max dist at which stealth is visible
987 
988  // now check if within view frustum
989  float needed_dot_to_stealth;
990  if (dist_to_stealth < 100) {
991  needed_dot_to_stealth = 0.0f;
992  } else {
993  needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
994  }
995  if (dot_to_stealth > needed_dot_to_stealth) {
996  if (dist_to_stealth < max_stealth_dist) {
997  return STEALTH_IN_FRUSTUM;
998  }
999  }
1000 
1001  // not within frustum
1002  return STEALTH_NOT_IN_FRUSTUM;
1003  }
1004 
1005  // visible by awacs level
1006  return STEALTH_FULLY_TARGETABLE;
1007 }
1008 
1009 // END STEALTH
1010 
1017 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1018 {
1019  vec3d v2o;
1020  float dist;
1021 
1022  dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1023 
1024  *to_dot = vm_vec_dot(&objp->orient.vec.fvec, &v2o);
1025 
1026  if (from_dot != NULL)
1027  *from_dot = - vm_vec_dot(&other_objp->orient.vec.fvec, &v2o);
1028 
1029  return dist;
1030 }
1031 
1040 {
1041  object *stealth_objp;
1042 
1043  // make sure I am targeting a stealth ship
1045  stealth_objp = &Objects[aip->target_objnum];
1046 
1047  // if update is due to weapon fire, get exact stealth position
1048  aip->stealth_last_pos = stealth_objp->pos;
1049  aip->stealth_velocity = stealth_objp->phys_info.vel;
1051 }
1052 
1056 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1057 {
1058  object *objp, *weapon_objp;
1059  ai_info *aip;
1060  float old_dist, new_dist;
1061  float old_dot, new_dot;
1062  object *old_weapon_objp = NULL;
1063 
1064  if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1065  return;
1066  }
1067 
1068  objp = &Objects[attacked_objnum];
1069 
1070  // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1071  // an asteroid or bomb).
1072  if ( objp->type != OBJ_SHIP ) {
1073  return;
1074  }
1075 
1076  weapon_objp = &Objects[weapon_objnum];
1077 
1078  aip = &Ai_info[Ships[objp->instance].ai_index];
1079 
1080  // if my target is a stealth ship and is not visible
1081  if (aip->target_objnum >= 0) {
1084  // and the weapon is coming from that stealth ship
1085  if (weapon_objp->parent == aip->target_objnum) {
1086  // update my position estimate for stealth ship
1088  }
1089  }
1090  }
1091  }
1092 
1093  if (aip->danger_weapon_objnum != -1) {
1094  old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1095  if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1096  ;
1097  } else {
1098  aip->danger_weapon_objnum = -1;
1099  }
1100  }
1101 
1102  new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1103 
1104  if (aip->danger_weapon_objnum == -1) {
1105  if (new_dist < 1500.0f) {
1106  if (new_dot > 0.5f) {
1107  aip->danger_weapon_objnum = weapon_objnum;
1108  aip->danger_weapon_signature = weapon_objp->signature;
1109  }
1110  }
1111  } else {
1112  Assert(old_weapon_objp != NULL);
1113  old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1114 
1115  if (old_dot < 0.5f) {
1116  aip->danger_weapon_objnum = -1;
1117  old_dist = 9999.9f;
1118  }
1119 
1120  if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1121  if (new_dist < old_dist) {
1122  aip->danger_weapon_objnum = weapon_objnum;
1123  aip->danger_weapon_signature = weapon_objp->signature;
1124  }
1125  }
1126  }
1127 }
1128 
1129 // If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1130 // (rvec defaults to NULL)
1131 void ai_turn_towards_vector(vec3d *dest, object *objp, float frametime, float turn_time, vec3d *slide_vec, vec3d *rel_pos, float bank_override, int flags, vec3d *rvec, int sexp_flags)
1132 {
1133  matrix curr_orient;
1134  vec3d vel_in, vel_out, desired_fvec, src;
1135  float delta_time;
1136  physics_info *pip;
1137  vec3d vel_limit, acc_limit;
1138  float delta_bank;
1139 
1140  // Don't allow a ship to turn if it has no engine strength.
1141  // AL 3-12-98: objp may not always be a ship!
1142  if ( (objp->type == OBJ_SHIP) && !(sexp_flags & AITTV_VIA_SEXP) ) {
1144  return;
1145  }
1146 
1147  pip = &objp->phys_info;
1148 
1149  vel_in = pip->rotvel;
1150  curr_orient = objp->orient;
1151  delta_time = flFrametime;
1152 
1153  if(turn_time > 0.0f)
1154  {
1155  // Scale turn_time based on skill level and team.
1156  if (!(flags & AITTV_FAST) && !(sexp_flags & AITTV_VIA_SEXP) ){
1157  if (objp->type == OBJ_SHIP){
1159  turn_time *= Ai_info[Ships[objp->instance].ai_index].ai_turn_time_scale;
1160  }
1161  }
1162  }
1163 
1164  // Set max turn rate.
1165  vel_limit.xyz.x = PI2/turn_time;
1166  vel_limit.xyz.y = PI2/turn_time;
1167  vel_limit.xyz.z = PI2/turn_time;
1168  }
1169  else
1170  {
1171  vm_vec_zero(&vel_limit);
1172  }
1173 
1174  // Set rate at which ship can accelerate to its rotational velocity.
1175  // For now, weapons just go much faster.
1176  acc_limit = vel_limit;
1177  if (objp->type == OBJ_WEAPON)
1178  vm_vec_scale(&acc_limit, 8.0f);
1179 
1180  src = objp->pos;
1181 
1182  if (rel_pos != NULL) {
1183  vec3d gun_point;
1184  vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1185  vm_vec_add2(&src, &gun_point);
1186  }
1187 
1188  vm_vec_normalized_dir(&desired_fvec, dest, &src);
1189 
1190  // Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1191  // to be moving towards goal rather than just pointing. So, if slide_vec is !NULL, try to
1192  // make ship move towards goal, not point at goal.
1193  if (slide_vec != NULL) {
1194  vm_vec_add2(&desired_fvec, slide_vec);
1195  vm_vec_normalize(&desired_fvec);
1196  }
1197 
1198  // Should be more general case here. Currently, anything that is not a weapon will bank when it turns.
1199  // Goober5000 - don't bank if sexp says not to
1200  if ( (objp->type == OBJ_WEAPON) || (sexp_flags & AITTV_IGNORE_BANK ) )
1201  delta_bank = 0.0f;
1202  else if ((bank_override) && (iff_x_attacks_y(Ships[objp->instance].team, Player_ship->team))) { // Theoretically, this will only happen for Shivans.
1203  delta_bank = bank_override;
1204  } else {
1205  delta_bank = vm_vec_dot(&curr_orient.vec.rvec, &objp->last_orient.vec.rvec);
1206  delta_bank = 100.0f * (1.0f - delta_bank);
1207  if (vm_vec_dot(&objp->last_orient.vec.fvec, &objp->orient.vec.rvec) < 0.0f)
1208  delta_bank = -delta_bank;
1209  }
1210 
1211  // Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1212  // that is causing ships to inexplicably rotate very far. If you hit the Int3(), set the next statement to be
1213  // the one marked "HERE". (Do this clicking the cursor there, then right clicking. Choose the right option.)
1214  // This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1215  // Note, you'll need to enable the Int3() about ten lines below.
1216 #ifndef NDEBUG
1217 vec3d tvec = objp->orient.vec.fvec;
1218 vec3d vel_in_copy;
1219 matrix objp_orient_copy;
1220 
1221 vel_in_copy = vel_in;
1222 objp_orient_copy = objp->orient;
1223 
1224 vel_in = vel_in_copy; // HERE //-V587
1225 objp->orient = objp_orient_copy; //-V587
1226 #endif
1227 
1228  if (rvec != NULL) {
1229  matrix out_orient, goal_orient;
1230 
1231  vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1232  vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1233  objp->orient = out_orient;
1234  } else {
1235  vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1236  }
1237 
1238 #ifndef NDEBUG
1239 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1240  Assertion(!(delta_time < 0.25f && vm_vec_dot(&objp->orient.vec.fvec, &tvec) < 0.1f), "A ship rotated too far. Offending vessel is %s, please investigate.\n", Ships[objp->instance].ship_name);
1241 }
1242 #endif
1243 
1244  pip->rotvel = vel_out;
1245 }
1246 
1247 // Set aip->target_objnum to objnum
1248 // Update aip->previous_target_objnum.
1249 // If new target (objnum) is different than old target, reset target_time.
1250 int set_target_objnum(ai_info *aip, int objnum)
1251 {
1252  if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1253  return aip->target_objnum;
1254  }
1255 
1256  if (aip->target_objnum == objnum) {
1258  } else {
1260 
1261  // ignore this assert if a multiplayer observer
1262  if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1263  } else {
1264  Assert(objnum != Ships[aip->shipnum].objnum); // make sure not targeting self
1265  }
1266 
1267  // if stealth target, init ai_info for stealth
1268  if ( (objnum >= 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1269  init_ai_stealth_info(aip, &Objects[objnum]);
1270  }
1271 
1272  aip->target_objnum = objnum;
1273  aip->target_time = 0.0f;
1274  aip->time_enemy_near = 0.0f; //SUSHI: Reset the "time near" counter whenever the target is changed
1275  aip->last_hit_target_time = Missiontime; //Also, the "last hit target" time should be reset when the target changes
1276  aip->target_signature = (objnum >= 0) ? Objects[objnum].signature : -1;
1277  // clear targeted subsystem
1278  set_targeted_subsys(aip, NULL, -1);
1279  }
1280 
1281  return aip->target_objnum;
1282 }
1283 
1284 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1285 
1289 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1290 {
1291  Assert(aip != NULL);
1292 
1293  aip->last_subsys_target = aip->targeted_subsys;
1294  aip->targeted_subsys = new_subsys;
1295  aip->targeted_subsys_parent = parent_objnum;
1296 
1297  if ( new_subsys ) {
1298  // Make new_subsys target
1299  if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1300  if ( aip != Player_ai ) {
1301  Assert( aip->shipnum >= 0 );
1303  ship_primary_changed(&Ships[aip->shipnum]); // AL: maybe send multiplayer information when AI ship changes primaries
1304  }
1305  }
1306 
1307  if ( aip == Player_ai ) {
1308  hud_lock_reset(0.5f);
1309  }
1310 
1311  } else {
1312  // Cleanup any subsys path information if it exists
1314  }
1315 
1316  return aip->targeted_subsys;
1317 }
1318 
1330 void ai_object_init(object * obj, int ai_index)
1331 {
1332  ai_info *aip;
1333  Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1334 
1335  aip = &Ai_info[ai_index];
1336 
1337  aip->type = 0; // 0 means not in use.
1338  aip->wing = -1; // Member of what wing? -1 means none.
1339  aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1340  aip->behavior = AIM_NONE;
1341 }
1342 
1347 {
1348  object *objp = &Objects[Ships[aip->shipnum].objnum];
1349 
1350  if (object_is_docked(objp))
1351  {
1352  float ratio = objp->phys_info.mass / dock_calc_total_docked_mass(objp);
1353 
1354  // put cap on how much ship can slow down
1355  if ( (ratio < 0.8f) && !(The_mission.ai_profile->flags & AIPF_NO_MIN_DOCK_SPEED_CAP) ) {
1356  ratio = 0.8f;
1357  }
1358 
1359  // make sure we at least some velocity
1360  if (ratio < 0.1f) {
1361  ratio = 0.1f;
1362  }
1363 
1364  if (AI_ci.forward > ratio) {
1365  AI_ci.forward = ratio;
1366  }
1367  }
1368 }
1369 
1370 void accelerate_ship(ai_info *aip, float accel)
1371 {
1372  aip->prev_accel = accel;
1373  AI_ci.forward = accel;
1375 }
1376 
1377 void change_acceleration(ai_info *aip, float delta_accel)
1378 {
1379  float new_accel;
1380 
1381  if (delta_accel < 0.0f) {
1382  if (aip->prev_accel > 0.0f)
1383  aip->prev_accel = 0.0f;
1384  } else if (aip->prev_accel < 0.0f)
1385  aip->prev_accel = 0.0f;
1386 
1387  new_accel = aip->prev_accel + delta_accel * flFrametime;
1388 
1389  if (new_accel > 1.0f)
1390  new_accel = 1.0f;
1391  else if (new_accel < -1.0f)
1392  new_accel = -1.0f;
1393 
1394  aip->prev_accel = new_accel;
1395 
1396  AI_ci.forward = new_accel;
1398 }
1399 
1400 void set_accel_for_target_speed(object *objp, float tspeed)
1401 {
1402  float max_speed;
1403  ai_info *aip;
1404 
1405  aip = &Ai_info[Ships[objp->instance].ai_index];
1406 
1407  max_speed = Ships[objp->instance].current_max_speed;
1408 
1409  if (max_speed > 0.0f) {
1410  AI_ci.forward = tspeed/max_speed;
1411  } else {
1412  AI_ci.forward = 0.0f;
1413  }
1414 
1415  aip->prev_accel = AI_ci.forward;
1416 
1418 }
1419 
1424 void project_point_to_perimeter(vec3d *perim_point, vec3d *pos, float radius, vec3d *vp)
1425 {
1426  vec3d v1;
1427  float mag;
1428 
1429  vm_vec_sub(&v1, vp, pos);
1430  mag = vm_vec_mag(&v1);
1431 
1432  if (mag == 0.0f) {
1433  Warning(LOCATION, "projectable point is at center of sphere.");
1434  vm_vec_make(&v1, 0.0f, radius, 0.0f);
1435  } else {
1436  vm_vec_normalize(&v1);
1437  vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1438  }
1439 
1440  vm_vec_add2(&v1, pos);
1441  *perim_point = v1;
1442 }
1443 
1457 void get_tangent_point(vec3d *tan1, vec3d *p0, vec3d *centerp, vec3d *p1, float radius)
1458 {
1459  vec3d dest_vec, v2c, perp_vec, temp_vec, v2;
1460  float dist, ratio;
1461 
1462  // Detect condition of point inside sphere.
1463  if (vm_vec_dist(p0, centerp) < radius)
1464  project_point_to_perimeter(tan1, centerp, radius, p0);
1465  else {
1466  vm_vec_normalized_dir(&v2c, centerp, p0);
1467 
1468  // Compute perpendicular vector using p0, centerp, p1
1469  vm_vec_normal(&temp_vec, p0, centerp, p1);
1470  vm_vec_sub(&v2, centerp, p0);
1471  vm_vec_cross(&perp_vec, &temp_vec, &v2);
1472 
1473  vm_vec_normalize(&perp_vec);
1474 
1475  dist = vm_vec_dist_quick(p0, centerp);
1476  ratio = dist / radius;
1477 
1478  if (ratio < 2.0f)
1479  vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1480  else
1481  vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1482 
1483  vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1484  }
1485 }
1486 
1491 void turn_towards_point(object *objp, vec3d *point, vec3d *slide_vec, float bank_override)
1492 {
1493  ai_info *aip;
1494  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1495 
1496  // check if in formation and if not leader, don't change rotvel.z (bank to match leader elsewhere)
1497  if (aip->ai_flags & AIF_FORMATION) {
1498  if (&Objects[aip->goal_objnum] != objp) {
1499  float rotvel_z = objp->phys_info.rotvel.xyz.z;
1500  ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1501  objp->phys_info.rotvel.xyz.z = rotvel_z;
1502  }
1503  } else {
1504  // normal turn
1505  ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1506  }
1507 }
1508 
1513 void turn_away_from_point(object *objp, vec3d *point, float bank_override)
1514 {
1515  vec3d opposite_point;
1516 
1517  vm_vec_sub(&opposite_point, &objp->pos, point);
1518  vm_vec_add2(&opposite_point, &objp->pos);
1519 
1520  ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1521 }
1522 
1523 
1524 // --------------------------------------------------------------------------
1525 // Given an object and a point, turn tangent to the point, resulting in
1526 // a circling behavior.
1527 // Make object *objp turn around the point *point with a radius of radius.
1528 // Note that this isn't the same as following a circle of radius radius with
1529 // center *point, but it should be adequate.
1530 // Note that if you want to circle an object without hitting it, you should use
1531 // about twice that object's radius for radius, else you'll certainly bump into it.
1532 // Return dot product to goal point.
1533 float turn_towards_tangent(object *objp, vec3d *point, float radius)
1534 {
1535  vec3d vec_to_point;
1536  vec3d goal_point;
1537  vec3d perp_point; // point radius away from *point on vector to objp->pos
1538  vec3d up_vec, perp_vec;
1539  vec3d v2g;
1540 
1541  vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1542  vm_vec_cross(&up_vec, &vec_to_point, &objp->orient.vec.fvec);
1543  vm_vec_cross(&perp_vec, &vec_to_point, &up_vec);
1544 
1545  vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1546  if (vm_vec_dot(&objp->orient.vec.fvec, &perp_vec) > 0.0f) {
1547  vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1548  } else {
1549  vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1550  }
1551 
1552  turn_towards_point(objp, &goal_point, NULL, 0.0f);
1553 
1554  vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1555  return vm_vec_dot(&objp->orient.vec.fvec, &v2g);
1556 }
1557 
1558 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1559 {
1560  vec3d r_vec, theta_vec;
1561  vec3d center_vec, vec_on_cylinder, sph_r_vec;
1562  float center_obj_z;
1563 
1564  // find closest z of center objp
1565  vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1566  center_obj_z = vm_vec_dot(&sph_r_vec, &center_objp->orient.vec.fvec);
1567 
1568  // find pt on axis with closest z
1569  vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.vec.fvec, center_obj_z);
1570 
1571  // get r_vec
1572  vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1573 
1574  Assert( (vm_vec_dot(&r_vec, &center_objp->orient.vec.fvec) < 0.0001));
1575 
1576  // get theta vec - perp to r_vec and z_vec
1577  vm_vec_cross(&theta_vec, &center_objp->orient.vec.fvec, &r_vec);
1578 
1579 #ifndef NDEBUG
1580  float mag;
1581  mag = vm_vec_normalize(&theta_vec);
1582  Assert(mag > 0.9999 && mag < 1.0001);
1583 #endif
1584 
1585  vec3d temp;
1586  vm_vec_cross(&temp, &r_vec, &theta_vec);
1587 
1588 #ifndef NDEBUG
1589  float dot;
1590  dot = vm_vec_dot(&temp, &center_objp->orient.vec.fvec);
1591  Assert( dot >0.9999 && dot < 1.0001);
1592 #endif
1593 
1594  // find pt on clylinder with closest z
1595  vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1596 
1597  vec3d goal_pt, v2g;
1598  vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1599 
1600  turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1601 
1602  vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1603  return vm_vec_dot(&objp->orient.vec.fvec, &v2g);
1604 }
1605 
1609 void get_tangent_point(vec3d *goal_point, object *objp, vec3d *point, float radius)
1610 {
1611  vec3d vec_to_point;
1612  vec3d perp_point; // point radius away from *point on vector to objp->pos
1613  vec3d up_vec, perp_vec;
1614 
1615  vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1616  vm_vec_cross(&up_vec, &vec_to_point, &objp->orient.vec.fvec);
1617 
1618  while (IS_VEC_NULL(&up_vec)) {
1619  vec3d rnd_vec;
1620 
1621  vm_vec_rand_vec_quick(&rnd_vec);
1622  vm_vec_add2(&rnd_vec, &objp->orient.vec.fvec);
1623  vm_vec_cross(&up_vec, &vec_to_point, &rnd_vec);
1624  }
1625 
1626  vm_vec_cross(&perp_vec, &vec_to_point, &up_vec);
1627  vm_vec_normalize(&perp_vec);
1628 
1629  vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1630 
1631  if (vm_vec_dot(&objp->orient.vec.fvec, &perp_vec) > 0.0f) {
1632  vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1633  } else {
1634  vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1635  }
1636 }
1637 
1639 
1643 int object_is_targetable(object *target, ship *viewer)
1644 {
1645  // if target is ship, check if visible by team
1646  if (target->type == OBJ_SHIP)
1647  {
1648  if (ship_is_visible_by_team(target, viewer)) {
1649  return 1;
1650  }
1651 
1652  // for AI partially targetable works as fully targetable, except for stealth ship
1653  if (Ships[target->instance].flags2 & SF2_STEALTH) {
1654  // if not team targetable, check if within frustum
1655  if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_IN_FRUSTUM ) {
1656  return 1;
1657  } else {
1658  return 0;
1659  }
1660  }
1661  }
1662 
1663  // if not fully targetable by team, check awacs level with viewer
1664  // allow targeting even if only only partially targetable to player
1665  float radar_return = awacs_get_level(target, viewer);
1666  if ( radar_return > 0.4 ) {
1667  return 1;
1668  } else {
1669  return 0;
1670  }
1671 }
1672 
1676 int num_enemies_attacking(int objnum)
1677 {
1678  object *objp;
1679  ship *sp;
1680  ship_subsys *ssp;
1681  ship_obj *so;
1682  int count;
1683 
1684  count = 0;
1685 
1686  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1687  objp = &Objects[so->objnum];
1688  Assert(objp->instance != -1);
1689  sp = &Ships[objp->instance];
1690 
1691  if (Ai_info[sp->ai_index].target_objnum == objnum)
1692  count++;
1693 
1694  // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
1695  if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
1696 
1697  // loop through all the subsystems, check if turret has objnum as a target
1698  ssp = GET_FIRST(&sp->subsys_list);
1699  while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
1700 
1701  if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
1702  if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
1703  count++;
1704  }
1705  }
1706  ssp = GET_NEXT( ssp );
1707  } // end while
1708  }
1709  }
1710 
1711  return count;
1712 }
1713 
1720 {
1721  ship *shipp;
1722  ai_info *aip;
1723  float lowest_max_speed;
1724  int wingnum;
1725  object *o;
1726  ship_obj *so;
1727 
1728  Assert(objp->type == OBJ_SHIP);
1729  Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
1730  shipp = &Ships[objp->instance];
1731  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
1732  aip = &Ai_info[shipp->ai_index];
1733 
1734  wingnum = aip->wing;
1735 
1736  lowest_max_speed = shipp->current_max_speed;
1737 
1738  if ( wingnum == -1 )
1739  return lowest_max_speed;
1740 
1741  Assert(wingnum >= 0);
1742 
1743  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1744  o = &Objects[so->objnum];
1745  ship *oshipp = &Ships[o->instance];
1746  ai_info *oaip = &Ai_info[oshipp->ai_index];
1747 
1748  if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
1749  // Note: If a ship in the wing has a super low max speed, probably its engines are disabled. So, fly along and
1750  // ignore the poor guy.
1751  float cur_max = oshipp->current_max_speed;
1752 
1753  if (object_is_docked(o)) {
1754  cur_max *= o->phys_info.mass / dock_calc_total_docked_mass(o);
1755  }
1756 
1757  if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
1758  lowest_max_speed = cur_max;
1759  }
1760  }
1761  }
1762 
1763  return lowest_max_speed;
1764 }
1765 
1770 {
1771  ship *shipp;
1772  ai_info *aip;
1773  float lowest_max_av_ab_speed;
1774  float recharge_scale;
1775  int wingnum;
1776  object *o;
1777  ship_obj *so;
1778  ship_info *sip;
1779 
1780  Assert(objp->type == OBJ_SHIP);
1781  Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
1782  shipp = &Ships[objp->instance];
1783  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
1784  aip = &Ai_info[shipp->ai_index];
1785  sip = &Ship_info[shipp->ship_info_index];
1786 
1787  wingnum = aip->wing;
1788 
1789  if (((shipp->flags2 & SF2_AFTERBURNER_LOCKED) || !(sip->flags & SIF_AFTERBURNER)) || (shipp->current_max_speed < 5.0f) || (objp->phys_info.afterburner_max_vel.xyz.z <= shipp->current_max_speed) || !(aip->ai_flags & AIF_FREE_AFTERBURNER_USE)) {
1790  lowest_max_av_ab_speed = shipp->current_max_speed;
1791  }
1792  else
1793  {
1795  recharge_scale = sip->afterburner_recover_rate * recharge_scale / (sip->afterburner_burn_rate + sip->afterburner_recover_rate * recharge_scale);
1796  lowest_max_av_ab_speed = recharge_scale * (objp->phys_info.afterburner_max_vel.xyz.z - shipp->current_max_speed) + shipp->current_max_speed;
1797  }
1798 
1799 
1800  if ( wingnum == -1 )
1801  return lowest_max_av_ab_speed;
1802 
1803  Assert(wingnum >= 0);
1804 
1805  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1806  o = &Objects[so->objnum];
1807  ship *oshipp = &Ships[o->instance];
1808  ai_info *oaip = &Ai_info[oshipp->ai_index];
1809  ship_info *osip = &Ship_info[oshipp->ship_info_index];
1810 
1811  if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum) && (oaip->ai_flags & AIF_FORMATION)) {
1812 
1813  float cur_max;
1815  cur_max = oshipp->current_max_speed;
1816  }
1817  else
1818  {
1820  recharge_scale = osip->afterburner_recover_rate * recharge_scale / (osip->afterburner_burn_rate + osip->afterburner_recover_rate * recharge_scale);
1821  cur_max = recharge_scale * (o->phys_info.afterburner_max_vel.xyz.z - oshipp->current_max_speed) + oshipp->current_max_speed;
1822  }
1823 
1824  if (object_is_docked(o)) {
1825  cur_max *= o->phys_info.mass / dock_calc_total_docked_mass(o);
1826  }
1827  // Note: If a ship in the wing has a super low max speed, probably its engines are disabled. So, fly along and
1828  // ignore the poor guy.
1829  if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_av_ab_speed)) {
1830  lowest_max_av_ab_speed = cur_max;
1831  }
1832  }
1833  }
1834 
1835  return lowest_max_av_ab_speed;
1836 }
1837 
1843 int is_ignore_object_sub(int *ignore_objnum, int *ignore_signature, int objnum)
1844 {
1845  // Not ignoring anything.
1846  if (*ignore_objnum == UNUSED_OBJNUM)
1847  {
1848  return 0;
1849  }
1850  // This means it's ignoring an object, not a wing.
1851  else if (*ignore_objnum >= 0)
1852  {
1853  // see if this object became invalid
1854  if (Objects[*ignore_objnum].signature != *ignore_signature)
1855  {
1856  // reset
1857  *ignore_objnum = UNUSED_OBJNUM;
1858  }
1859  // objects and signatures match
1860  else if (*ignore_objnum == objnum)
1861  {
1862  // found it
1863  return 1;
1864  }
1865 
1866  return 0;
1867  }
1868  // Ignoring a wing.
1869  else
1870  {
1871  Int3(); // Should never happen. I thought I removed this behavior! -- MK, 5/17/98
1872  return 0;
1873  }
1874 }
1875 
1876 // Goober5000
1878 {
1879  int i;
1880 
1881  for (i = 0; i < MAX_IGNORE_NEW_OBJECTS; i++)
1882  {
1883  if (is_ignore_object_sub(&aip->ignore_new_objnums[i], &aip->ignore_new_signatures[i], objnum))
1884  return i;
1885  }
1886 
1887  return -1;
1888 }
1889 
1890 // Goober5000
1891 int is_ignore_object(ai_info *aip, int objnum, int just_the_original = 0)
1892 {
1893  // check original (retail) ignore
1894  if (is_ignore_object_sub(&aip->ignore_objnum, &aip->ignore_signature, objnum))
1895  return 1;
1896 
1897  // check new ignore
1898  if (!just_the_original)
1899  {
1900  if (find_ignore_new_object_index(aip, objnum) >= 0)
1901  return 1;
1902  }
1903 
1904  return 0;
1905 }
1906 
1907 
1908 
1909 
1910 typedef struct eval_nearest_objnum {
1911  int objnum;
1912  object *trial_objp;
1915  float range;
1921 
1922 
1924 {
1925  ai_info *aip;
1926  ship_subsys *attacking_subsystem;
1927  ship *shipp = &Ships[eno->trial_objp->instance];
1928 
1929  aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
1930 
1931  attacking_subsystem = aip->targeted_subsys;
1932 
1933  if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
1934  if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
1935 #ifndef NDEBUG
1937  return;
1938 #endif
1939  // If only supposed to attack ship in a specific wing, don't attack other ships.
1940  if ((eno->enemy_wing != -1) && (shipp->wingnum != eno->enemy_wing))
1941  return;
1942 
1943  // Don't keep firing at a ship that is in its death throes.
1944  if (shipp->flags & SF_DYING)
1945  return;
1946 
1947  if (is_ignore_object(aip, OBJ_INDEX(eno->trial_objp)))
1948  return;
1949 
1950  if (eno->trial_objp->flags & OF_PROTECTED)
1951  return;
1952 
1953  if (shipp->flags & SF_ARRIVING)
1954  return;
1955 
1956  ship_info *sip = &Ship_info[shipp->ship_info_index];
1957 
1958  if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
1959  return;
1960 
1961  if (iff_matches_mask(shipp->team, eno->enemy_team_mask)) {
1962  float dist;
1963  int num_attacking;
1964 
1965  // Allow targeting of stealth in nebula by his firing at me
1966  // This is done for a specific ship, not generally.
1967  if ( !eno->check_danger_weapon_objnum ) {
1968  // check if can be targeted if inside nebula
1969  if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
1970  // check if stealth ship is visible, but not "targetable"
1971  if ( !((shipp->flags2 & SF2_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
1972  return;
1973  }
1974  }
1975  }
1976 
1977  // if objnum is BIG or HUGE, find distance to bbox
1978  if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1979  vec3d box_pt;
1980  // check if inside bbox
1981  int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
1982  if (inside) {
1983  dist = 10.0f;
1984  // on the box
1985  } else {
1986  dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
1987  }
1988  } else {
1989  dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
1990  }
1991 
1992  // Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
1993  if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
1994  dist = dist * 0.5f;
1995  }
1996 
1997  num_attacking = num_enemies_attacking(OBJ_INDEX(eno->trial_objp));
1998  if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
1999  if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2000  dist *= (float) (num_attacking+2)/2.0f; // prevents lots of ships from attacking same target
2001  }
2002 
2003  if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2004  dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS; // Favor attacking non-players based on skill level.
2005  }
2006 
2007  if (dist < eno->nearest_dist) {
2008  eno->nearest_dist = dist;
2009  eno->nearest_objnum = OBJ_INDEX(eno->trial_objp);
2010  }
2011  }
2012  }
2013  }
2014  }
2015 
2016 }
2017 
2018 
2029 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2030 {
2031  object *danger_weapon_objp;
2032  ai_info *aip;
2033  ship_obj *so;
2034 
2035  // initialize eno struct
2036  eval_nearest_objnum eno;
2037  eno.enemy_team_mask = enemy_team_mask;
2038  eno.enemy_wing = enemy_wing;
2039  eno.max_attackers = max_attackers;
2040  eno.objnum = objnum;
2041  eno.range = range;
2042  eno.nearest_dist = range;
2043  eno.nearest_objnum = -1;
2045 
2046  // go through the list of all ships and evaluate as potential targets
2047  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2048  eno.trial_objp = &Objects[so->objnum];
2050  }
2051 
2052  // check if danger_weapon_objnum has will show a stealth ship
2053  aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2054  if (aip->danger_weapon_objnum >= 0) {
2055  danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2056  // validate weapon
2057  if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2058  Assert(danger_weapon_objp->type == OBJ_WEAPON);
2059  // check if parent is a ship
2060  if (danger_weapon_objp->parent >= 0) {
2061  if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2062  // check if stealthy
2063  if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2064  // check if weapon is laser
2065  if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2066  // check stealth ship by its laser fire
2068  eno.trial_objp = &Objects[danger_weapon_objp->parent];
2070  }
2071  }
2072  }
2073  }
2074  }
2075  }
2076 
2077  // If only looking for target in certain wing and couldn't find anything in
2078  // that wing, look for any object.
2079  if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2080  return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2081  }
2082 
2083  return eno.nearest_objnum;
2084 }
2085 
2092 int find_nearby_threat(int objnum, int enemy_team_mask, float range, int *count)
2093 {
2094  int nearest_objnum;
2095  float nearest_dist;
2096  object *objp;
2097  ship_obj *so;
2098 
2099  nearest_objnum = -1;
2100  nearest_dist = range;
2101 
2102  *count = 0;
2103 
2104  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2105  objp = &Objects[so->objnum];
2106 
2107  if ( OBJ_INDEX(objp) != objnum ) {
2108  if (Ships[objp->instance].flags & SF_DYING)
2109  continue;
2110 
2112  continue;
2113 
2114  if (iff_matches_mask(Ships[objp->instance].team, enemy_team_mask)) {
2115  float dist;
2116 
2117  dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2118 
2119  if (dist < range) {
2120  (*count)++;
2121 
2122  if (dist < nearest_dist) {
2123  nearest_dist = dist;
2124  nearest_objnum = OBJ_INDEX(objp);
2125  }
2126  }
2127  }
2128  }
2129  }
2130 
2131  return nearest_objnum;
2132 }
2133 
2137 int num_turrets_attacking(object *turret_parent, int target_objnum)
2138 {
2139  ship_subsys *ss;
2140  ship *shipp;
2141  int count = 0;
2142  shipp = &Ships[turret_parent->instance];
2143 
2144  Assert(turret_parent->type == OBJ_SHIP);
2145 
2146  for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2147  // check if subsys is alive
2148  if (ss->current_hits <= 0.0f) {
2149  continue;
2150  }
2151 
2152  // check if it's a turret
2153  if (ss->system_info->type != SUBSYSTEM_TURRET) {
2154  continue;
2155  }
2156 
2157  // if the turret is locked
2158  if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2159  continue;
2160  }
2161 
2162  // check if turret is targeting target_objnum
2163  if (ss->turret_enemy_objnum == target_objnum) {
2164  count++;
2165  }
2166  }
2167 
2168  return count;
2169 }
2170 
2175 {
2176  return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2177 }
2178 
2186 int find_enemy(int objnum, float range, int max_attackers)
2187 {
2188  int enemy_team_mask;
2189 
2190  if (objnum < 0)
2191  return -1;
2192 
2193  enemy_team_mask = iff_get_attackee_mask(obj_team(&Objects[objnum]));
2194 
2195  // if target_objnum != -1, use that as goal.
2196  ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2199  if (aip->target_objnum != -1) {
2200  int target_objnum = aip->target_objnum;
2201 
2202  // DKA don't undo object as target in nebula missions.
2203  // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range. (BAD)
2204  if ( Objects[target_objnum].signature == aip->target_signature ) {
2205  if (iff_matches_mask(Ships[Objects[target_objnum].instance].team, enemy_team_mask)) {
2206  if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2207  return target_objnum;
2208  }
2209  }
2210  } else {
2211  aip->target_objnum = -1;
2212  aip->target_signature = -1;
2213  }
2214  }
2215 
2216  return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2217 
2218  } else {
2219  aip->target_objnum = -1;
2220  aip->target_signature = -1;
2221  return -1;
2222  }
2223 }
2224 
2230 {
2231  if (aip->ai_flags & AIF_AWAITING_REPAIR) {
2232  object *repair_obj;
2233 
2234  if (aip->support_ship_objnum == -1) {
2235  repair_obj = NULL;
2236  } else {
2237  repair_obj = &Objects[aip->support_ship_objnum];
2238  }
2239  ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
2240  }
2241  aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP); // Might request again after 30 seconds.
2242 }
2243 
2245 {
2246  if (Ships[objp->instance].team == Player_ship->team){
2247  aip->avoid_check_timestamp = timestamp(0); // Force a check for collision next frame.
2248  }
2249 }
2250 
2257 void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp)
2258 {
2259  int temp;
2260  ai_info *aip;
2261 
2262  Assert(attacker != NULL);
2263  Assert(attacker->instance != -1);
2264  Assert(Ships[attacker->instance].ai_index != -1);
2265 
2266  aip = &Ai_info[Ships[attacker->instance].ai_index];
2267  force_avoid_player_check(attacker, aip);
2268 
2269  aip->ok_to_target_timestamp = timestamp(0); // Guarantee we can target.
2270 
2271  if (attacker == attacked) {
2272  Int3(); // Bogus! Who tried to get me to attack myself! Trace out and fix!
2273  return;
2274  }
2275 
2276  // Only set to chase if a fighter or bomber, otherwise just return.
2277  if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (attacked != NULL)) {
2278  nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
2279  }
2280 
2281  // This is how "engage enemy" gets processed
2282  if (attacked == NULL) {
2284  // nebula safe
2285  set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4));
2286  } else {
2287  // check if we can see atacked in nebula
2288  if (aip->target_objnum != OBJ_INDEX(attacked)) {
2289  aip->aspect_locked_time = 0.0f;
2290  }
2291  set_target_objnum(aip, OBJ_INDEX(attacked));
2292  }
2293 
2294  ai_set_goal_maybe_abort_dock(attacker, aip);
2295  aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
2296 
2297  // Goober5000
2298  temp = find_ignore_new_object_index(aip, aip->target_objnum);
2299  if (temp >= 0)
2300  {
2302  }
2303  else if (is_ignore_object(aip, aip->target_objnum, 1))
2304  {
2306  }
2307 
2308  aip->mode = AIM_CHASE;
2309  aip->submode = SM_ATTACK; // AL 12-15-97: need to set submode? I got an assert() where submode was bogus
2310  aip->submode_start_time = Missiontime; // for AIM_CHASE... it may have been not set correctly here
2311 
2312  if (ssp == NULL) {
2313  set_targeted_subsys(aip, NULL, -1);
2314  if (aip->target_objnum != -1) {
2315  Objects[aip->target_objnum].flags &= ~OF_PROTECTED; // If ship had been protected, unprotect it.
2316  }
2317  } else {
2318  Int3(); // Not supported yet!
2319  }
2320 }
2321 
2326 void ai_attack_wing(object *attacker, int wingnum)
2327 {
2328  ai_info *aip;
2329 
2330  Assert(attacker != NULL);
2331  Assert(attacker->instance != -1);
2332  Assert(Ships[attacker->instance].ai_index != -1);
2333 
2334  aip = &Ai_info[Ships[attacker->instance].ai_index];
2335 
2336  aip->enemy_wing = wingnum;
2337  aip->mode = AIM_CHASE;
2338  aip->submode = SM_ATTACK; // AL 12-15-97: need to set submode? I got an assert() where submode was bogus
2339  aip->submode_start_time = Missiontime; // for AIM_CHASE... it may have been not set correctly here
2340 
2341  aip->ok_to_target_timestamp = timestamp(0); // Guarantee we can target.
2342 
2343  int count = Wings[wingnum].current_count;
2344  if (count > 0) {
2345  int index;
2346 
2347  index = (int) (frand() * count);
2348 
2349  if (index >= count)
2350  index = 0;
2351 
2352  set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
2353 
2354  ai_set_goal_maybe_abort_dock(attacker, aip);
2355  aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
2356  }
2357 }
2358 
2362 void ai_evade_object(object *evader, object *evaded)
2363 {
2364  ai_info *aip;
2365 
2366  Assert(evader != NULL);
2367  Assert(evaded != NULL);
2368  Assert(evader->instance != -1);
2369  Assert(Ships[evader->instance].ai_index != -1);
2370 
2371  if (evaded == evader) {
2372  Int3(); // Bogus! Who tried to get me to evade myself! Trace out and fix!
2373  return;
2374  }
2375 
2376  aip = &Ai_info[Ships[evader->instance].ai_index];
2377 
2378  set_target_objnum(aip, OBJ_INDEX(evaded));
2379  aip->mode = AIM_EVADE;
2380 
2381 }
2382 
2389 int compact_ignore_new_objects(ai_info *aip, int force = 0)
2390 {
2391  if (force)
2393 
2394  for (int current_index = 0; current_index < MAX_IGNORE_NEW_OBJECTS; current_index++)
2395  {
2396  int next_occupied_index = -1;
2397 
2398  // skip occupied slots
2399  if (aip->ignore_new_objnums[current_index] != UNUSED_OBJNUM)
2400  {
2401  // prune invalid objects
2402  if (Objects[aip->ignore_new_objnums[current_index]].signature != aip->ignore_new_signatures[current_index])
2403  aip->ignore_new_objnums[current_index] = UNUSED_OBJNUM;
2404  else
2405  continue;
2406  }
2407 
2408  // find an index to move downward
2409  for (int i = current_index + 1; i < MAX_IGNORE_NEW_OBJECTS; i++)
2410  {
2411  // skip empty slots
2412  if (aip->ignore_new_objnums[i] == UNUSED_OBJNUM)
2413  continue;
2414 
2415  // found one
2416  next_occupied_index = i;
2417  break;
2418  }
2419 
2420  // are all higher slots empty?
2421  if (next_occupied_index < 0)
2422  return current_index;
2423 
2424  // move the occupied slot down to this one
2425  aip->ignore_new_objnums[current_index] = aip->ignore_new_objnums[next_occupied_index];
2426  aip->ignore_new_signatures[current_index] = aip->ignore_new_signatures[next_occupied_index];
2427 
2428  // empty the occupied slot
2429  aip->ignore_new_objnums[next_occupied_index] = UNUSED_OBJNUM;
2430  aip->ignore_new_signatures[next_occupied_index] = -1;
2431  }
2432 
2433  // all slots are occupied
2434  return MAX_IGNORE_NEW_OBJECTS;
2435 }
2436 
2440 void ai_ignore_object(object *ignorer, object *ignored, int ignore_new)
2441 {
2442  ai_info *aip;
2443 
2444  Assert(ignorer != NULL);
2445  Assert(ignored != NULL);
2446  Assert(ignorer->instance != -1);
2447  Assert(Ships[ignorer->instance].ai_index != -1);
2448  Assert(ignorer != ignored);
2449 
2450  aip = &Ai_info[Ships[ignorer->instance].ai_index];
2451 
2452  // Goober5000 - new behavior
2453  if (ignore_new)
2454  {
2455  int num_objects;
2456 
2457  // compact the array
2458  num_objects = compact_ignore_new_objects(aip);
2459 
2460  // make sure we're not adding a duplicate
2461  if (find_ignore_new_object_index(aip, OBJ_INDEX(ignored)) >= 0)
2462  return;
2463 
2464  // if we can't add a new one; "forget" one
2465  if (num_objects >= MAX_IGNORE_NEW_OBJECTS)
2466  num_objects = compact_ignore_new_objects(aip, 1);
2467 
2468  // add it
2469  aip->ignore_new_objnums[num_objects] = OBJ_INDEX(ignored);
2470  aip->ignore_new_signatures[num_objects] = ignored->signature;
2471  }
2472  // retail behavior
2473  else
2474  {
2475  aip->ignore_objnum = OBJ_INDEX(ignored);
2476  aip->ignore_signature = ignored->signature;
2477  aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
2478  ignored->flags |= OF_PROTECTED; // set protected bit of ignored ship.
2479  }
2480 }
2481 
2485 void ai_ignore_wing(object *ignorer, int wingnum)
2486 {
2487  ai_info *aip;
2488 
2489  Assert(ignorer != NULL);
2490  Assert(ignorer->instance != -1);
2491  Assert(Ships[ignorer->instance].ai_index != -1);
2492  Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
2493 
2494  aip = &Ai_info[Ships[ignorer->instance].ai_index];
2495 
2496  aip->ignore_objnum = -(wingnum +1);
2497  aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
2498 }
2499 
2500 
2512 void add_path_point(vec3d *pos, int path_num, int path_index, int modify_index)
2513 {
2514  pnode *pnp;
2515 
2516  if (modify_index == -1) {
2517  Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
2518  pnp = Ppfp;
2519  Ppfp++;
2520  } else {
2521  Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
2522  pnp = &Path_points[modify_index];
2523  }
2524 
2525  pnp->pos = *pos;
2526  pnp->path_num = path_num;
2527  pnp->path_index = path_index;
2528 }
2529 
2534 void bisect_chord(vec3d *p0, vec3d *p1, vec3d *centerp, float radius)
2535 {
2536  vec3d tvec;
2537  vec3d new_pnt;
2538 
2539  vm_vec_add(&tvec, p0, p1);
2540  vm_vec_sub2(&tvec, centerp);
2541  vm_vec_sub2(&tvec, centerp);
2542  if (vm_vec_mag_quick(&tvec) < 0.1f) {
2543  vm_vec_sub(&tvec, p0, p1);
2544  if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
2545  tvec.xyz.x = -tvec.xyz.z;
2546  } else {
2547  tvec.xyz.y = -tvec.xyz.x;
2548  }
2549  }
2550 
2551  vm_vec_normalize(&tvec);
2552  vm_vec_scale(&tvec, radius);
2553  vm_vec_add(&new_pnt, centerp, &tvec);
2554 
2555  add_path_point(&new_pnt, -1, -1, -1);
2556 }
2557 
2571 void create_path_to_point(vec3d *curpos, vec3d *goalpos, object *curobjp, object *goalobjp, int subsys_path)
2572 {
2573  // If can't cast vector to goalpos, then create an intermediate point.
2574  if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
2575  vec3d tan1;
2576  float radius;
2577 
2578  // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
2579  // trying to avoid. This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
2580  // want ships to reach their path destination without flying to points that sit on the radius of
2581  // a small ship
2582  radius = goalobjp->radius;
2583  if (subsys_path) {
2584  if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
2585  radius = SUBSYS_PATH_DIST;
2586  }
2587  }
2588 
2589  // The intermediate point is at the intersection of:
2590  // tangent to *goalobjp sphere at point *goalpos
2591  // tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
2592  // Note, there are two tangents through *curpos, unless *curpos is on the
2593  // sphere. The tangent that causes the nearer intersection (to *goalpos) is chosen.
2594  get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
2595 
2596  // If we can't reach tan1 from curpos, insert a new point.
2597  if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
2598  bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
2599 
2600  add_path_point(&tan1, -1, -1, -1);
2601 
2602  // If we can't reach goalpos from tan1, insert a new point.
2603  if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
2604  bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
2605  }
2606 
2607 }
2608 
2621 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
2622 {
2623  polymodel *pm;
2624  int i, modelnum, model_instance_num;
2625  vec3d v1;
2626  int pp_index; // index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
2627  int start_index, finish_index;
2628  vec3d submodel_offset, local_vert;
2629  bool rotating_submodel;
2630 
2631  // Initialize pp_index.
2632  // If pnp == NULL, that means we're creating new points. If not NULL, then modify in place.
2633  if (pnp == NULL)
2634  pp_index = -1; // This tells add_path_point to create a new point.
2635  else
2636  pp_index = 0; // pp_index will get assigned to index in Path_points to reuse.
2637 
2638  if (dir == 1) {
2639  start_index = 0;
2640  finish_index = MIN(count, mp->nverts);
2641  } else {
2642  Assert(dir == -1); // direction must be up by 1 or down by 1 and it's neither!
2643  start_index = mp->nverts-1;
2644  finish_index = MAX(-1, mp->nverts-1-count);
2645  }
2646 
2647  // Goober5000 - check for rotating submodels
2648  modelnum = Ship_info[Ships[objp->instance].ship_info_index].model_num;
2649  model_instance_num = Ships[objp->instance].model_instance_num;
2650  pm = model_get(modelnum);
2651  if ((mp->parent_submodel >= 0) && (pm->submodel[mp->parent_submodel].movement_type >= 0))
2652  {
2653  rotating_submodel = true;
2654 
2655  model_find_submodel_offset(&submodel_offset, modelnum, mp->parent_submodel);
2656  }
2657  else
2658  {
2659  rotating_submodel = false;
2660  }
2661 
2662  int offset = 0;
2663  for (i=start_index; i != finish_index; i += dir)
2664  {
2665  // Globalize the point.
2666  // Goober5000 - handle rotating submodels
2667  if (rotating_submodel)
2668  {
2669  // movement... find location of point like with docking code and spark generation
2670  vm_vec_sub(&local_vert, &mp->verts[i].pos, &submodel_offset);
2671  model_instance_find_world_point(&v1, &local_vert, model_instance_num, mp->parent_submodel, &objp->orient, &objp->pos);
2672  }
2673  else
2674  {
2675  // no movement... calculate as in original code
2676  vm_vec_unrotate(&v1, &mp->verts[i].pos, &objp->orient);
2677  vm_vec_add2(&v1, &objp->pos);
2678  }
2679 
2680  if ( randomize_pnt == i ) {
2681  vec3d v_rand;
2682  static_randvec(OBJ_INDEX(objp), &v_rand);
2683  vm_vec_scale(&v_rand, 30.0f);
2684  vm_vec_add2(&v1, &v_rand);
2685  }
2686 
2687  if (pp_index != -1)
2688  pp_index = pnp-Path_points + offset;
2689 
2690  add_path_point(&v1, path_num, i, pp_index);
2691  offset++;
2692  }
2693 }
2694 
2695 
2706 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
2707 {
2708  ship *shipp = &Ships[pl_objp->instance];
2709  ai_info *aip = &Ai_info[shipp->ai_index];
2710 
2711  ship_info *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
2712  polymodel *pm = model_get(Ship_info[Ships[mobjp->instance].ship_info_index].model_num);
2713  int num_points;
2714  model_path *mp;
2715  pnode *ppfp_start = Ppfp;
2716  vec3d gp0;
2717 
2718  Assert(path_num >= 0);
2719 
2720  // Do garbage collection if necessary.
2721  if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
2723  ppfp_start = Ppfp;
2724  }
2725 
2726  aip->path_start = Ppfp - Path_points;
2727  Assert(path_num < pm->n_paths);
2728 
2729  mp = &pm->paths[path_num];
2730  num_points = mp->nverts;
2731 
2732  Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
2733 
2734  vm_vec_unrotate(&gp0, &mp->verts[0].pos, &mobjp->orient);
2735  vm_vec_add2(&gp0, &mobjp->pos);
2736 
2737  if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
2738  vec3d perim_point1;
2739  vec3d perim_point2;
2740 
2741  perim_point2 = pl_objp->pos;
2742 
2743  // If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
2744  // Assume it can fly "straight" out to the bounding sphere.
2745  if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
2746  project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
2747  add_path_point(&perim_point2, path_num, -1, -1);
2748  }
2749 
2750  // If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
2751  if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
2752  project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
2753  create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
2754  add_path_point(&perim_point1, path_num, -1, -1);
2755  } else { // The predefined path extends outside the sphere. Create path to that point.
2756  create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
2757  }
2758  }
2759 
2760  // AL 12-31-97: If following a subsystem path, add random vector to second last path point
2761  if ( subsys_path ) {
2762  copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
2763  } else {
2764  copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
2765  }
2766 
2767  aip->path_cur = aip->path_start;
2768  aip->path_dir = PD_FORWARD;
2769  aip->path_objnum = OBJ_INDEX(mobjp);
2770  aip->mp_index = path_num;
2771  aip->path_length = Ppfp - ppfp_start;
2772  aip->path_next_check_time = timestamp(1);
2773 
2775 
2776  aip->path_next_create_time = timestamp(1000); // OK to try to create one second later
2777  aip->path_create_pos = pl_objp->pos;
2778  aip->path_create_orient = pl_objp->orient;
2779 
2780  //Get path departure orientation from ships.tbl if it exists, otherwise zero it
2781  SCP_string pathName(mp->name);
2782  if (osip->pathMetadata.find(pathName) != osip->pathMetadata.end() && !IS_VEC_NULL(&osip->pathMetadata[pathName].departure_rvec))
2783  {
2784  vm_vec_copy_normalize(&aip->path_depart_orient, &osip->pathMetadata[pathName].departure_rvec);
2785  }
2786  else
2787  {
2789  }
2790 
2791  aip->ai_flags &= ~AIF_USE_EXIT_PATH; // ensure this flag is cleared
2792 }
2793 
2800 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
2801 {
2802  ship *shipp = &Ships[pl_objp->instance];
2803  ai_info *aip = &Ai_info[shipp->ai_index];
2804 
2805  polymodel *pm = model_get(Ship_info[Ships[mobjp->instance].ship_info_index].model_num);
2806  int num_points;
2807  model_path *mp;
2808  pnode *ppfp_start = Ppfp;
2809 
2810  Assert(path_num >= 0);
2811 
2812  // Do garbage collection if necessary.
2813  if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
2815  ppfp_start = Ppfp;
2816  }
2817 
2818  aip->path_start = Ppfp - Path_points;
2819  Assert(path_num < pm->n_paths);
2820 
2821  mp = &pm->paths[path_num];
2822  num_points = mp->nverts;
2823 
2824  Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
2825 
2826  copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
2827 
2828  aip->path_cur = aip->path_start;
2829  aip->path_dir = PD_FORWARD;
2830  aip->path_objnum = OBJ_INDEX(mobjp);
2831  aip->mp_index = path_num;
2832  aip->path_length = Ppfp - ppfp_start;
2833  aip->path_next_check_time = timestamp(1);
2834 
2835  aip->ai_flags |= AIF_USE_EXIT_PATH; // mark as exit path, referenced in maybe
2836 }
2837 
2842 int pp_collide_any(vec3d *curpos, vec3d *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
2843 {
2844  ship_obj *so;
2845 
2846  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2847  object *objp = &Objects[so->objnum];
2848 
2849  if (big_only_flag) {
2851  continue;
2852  }
2853 
2854  if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
2855  if (pp_collide(curpos, goalpos, objp, radius))
2856  return OBJ_INDEX(objp);
2857  }
2858  }
2859 
2860  return -1;
2861 }
2862 
2863 // Used to create docking paths and other pre-defined paths through ships.
2864 // Creates a path in absolute space.
2865 // Create a path into the object objnum.
2866 //
2867 // input:
2868 // pl_objp: object that will use the path
2869 // objnum: Object to find path to.
2870 // path_num: model path index to use
2871 // exit_flag: true means this is an exit path in the model
2872 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
2873 // Exit:
2874 // ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
2875 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
2876 {
2877  ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
2878 
2879  Assert(path_num >= 0);
2880 
2881  // This is test code, find an object with paths.
2882  if (objnum != -1) {
2883  object *objp = &Objects[objnum];
2884 
2885  if (objp->type == OBJ_SHIP) {
2886  polymodel *pm;
2887 
2888  ship *shipp = &Ships[objp->instance];
2889  pm = model_get(Ship_info[shipp->ship_info_index].model_num);
2890  if(path_num >= pm->n_paths)
2891  Error(LOCATION,"ai_find_path tring to find a path (%d) that doesn't exist, on ship %s", path_num, shipp->ship_name);
2892 
2893  aip->goal_objnum = objnum;
2894  aip->goal_signature = objp->signature;
2895  if (exit_flag)
2896  create_model_exit_path(pl_objp, objp, path_num);
2897  else
2898  create_model_path(pl_objp, objp, path_num, subsys_path);
2899  return;
2900  }
2901 
2902  }
2903 }
2904 
2905 extern int vector_object_collision(vec3d *start_pos, vec3d *end_pos, object *objp, float radius_scale);
2906 
2907 // Maybe make *objp avoid a player object.
2908 // For now, 4/6/98, only check Player_obj.
2909 // If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
2910 // Set aip->avoid_goal_point
2911 int maybe_avoid_player(object *objp, vec3d *goal_pos)
2912 {
2913  ai_info *aip;
2914  vec3d cur_pos, new_goal_pos;
2915  object *player_objp;
2916  vec3d n_vec_to_goal, n_vec_to_player;
2917 
2918  aip = &Ai_info[Ships[objp->instance].ai_index];
2919 
2921  return 0;
2922 
2923  player_objp = Player_obj;
2924 
2925  float speed_time;
2926 
2927  // How far two ships could be apart and still collide within one second.
2928  speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
2929 
2930  float obj_obj_dist;
2931 
2932  obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
2933 
2934  if (obj_obj_dist > speed_time*2.0f)
2935  return 0;
2936 
2937  cur_pos = objp->pos;
2938 
2939  new_goal_pos = *goal_pos;
2940 
2941  float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
2942  vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
2943 
2944  if (dist > speed_time*2.0f) {
2945  vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
2946  }
2947 
2948  if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
2950 
2951  vec3d avoid_vec;
2952 
2953  vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
2954  if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
2955  vm_vec_copy_scale(&avoid_vec, &objp->orient.vec.rvec, frand()-0.5f);
2956  vm_vec_scale_add2(&avoid_vec, &objp->orient.vec.uvec, frand()-0.5f);
2957  vm_vec_normalize(&avoid_vec);
2958  } else {
2959  vec3d tvec1;
2960  vm_vec_normalize(&avoid_vec);
2961  vm_vec_cross(&tvec1, &n_vec_to_goal, &avoid_vec);
2962  vm_vec_cross(&avoid_vec, &tvec1, &n_vec_to_player);
2963  }
2964 
2965  // Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
2966  // should fly in to avoid the player while still approaching its goal.
2967  vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
2968 
2969  aip->avoid_check_timestamp = timestamp(1000);
2970 
2971  return 1;
2972  } else {
2974  aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
2975 
2976  return 0;
2977  }
2978 }
2979 
2980 // Make object *still_objp enter AIM_STILL mode.
2981 // Make it point at view_pos.
2982 void ai_stay_still(object *still_objp, vec3d *view_pos)
2983 {
2984  ship *shipp;
2985  ai_info *aip;
2986 
2987  Assert(still_objp->type == OBJ_SHIP);
2988  Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
2989 
2990  shipp = &Ships[still_objp->instance];
2991  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2992 
2993  aip = &Ai_info[shipp->ai_index];
2994 
2995  aip->mode = AIM_STILL;
2996 
2997  // If view_pos not NULL, point at that point. Else, point at a point directly in front of ship. Ie, don't turn.
2998  if (view_pos != NULL)
2999  aip->goal_point = *view_pos;
3000  else
3001  vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.vec.fvec, 100.0f);
3002 }
3003 
3004 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3005 // when two objects have completed docking. used because we can dock object initially at misison load
3006 // time (meaning that ai_dock() might never get called). docker has docked with dockee (i.e. docker
3007 // would be a freighter and dockee would be a cargo).
3008 void ai_do_objects_docked_stuff(object *docker, int docker_point, object *dockee, int dockee_point, bool update_clients)
3009 {
3010  Assert((docker != NULL) && (dockee != NULL));
3011 
3012  // make sure they're not already docked!
3013  if (dock_check_find_direct_docked_object(docker, dockee))
3014  {
3015  Warning(LOCATION, "Call to ai_do_objects_docked_stuff when objects are already docked! Trace out and fix!\n");
3016  return;
3017  }
3018 
3019  // link the two objects
3020  dock_dock_objects(docker, docker_point, dockee, dockee_point);
3021 
3022  if (docker->type == OBJ_SHIP && dockee->type == OBJ_SHIP)
3023  {
3024  // maybe set support ship info
3025  if ((Ship_info[Ships[docker->instance].ship_info_index].flags & SIF_SUPPORT)
3026  || (Ship_info[Ships[dockee->instance].ship_info_index].flags & SIF_SUPPORT))
3027  {
3028  ai_info *docker_aip = &Ai_info[Ships[docker->instance].ai_index];
3029  ai_info *dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3030 
3031 #ifndef NDEBUG
3032  // support ship can only dock with one thing at a time
3033  if (Ship_info[Ships[docker->instance].ship_info_index].flags & SIF_SUPPORT)
3034  Assert(docker->dock_list->next == NULL);
3035  else
3036  Assert(dockee->dock_list->next == NULL);
3037 #endif
3038 
3039  // set stuff for both objects
3040  docker_aip->support_ship_objnum = OBJ_INDEX(dockee);
3041  dockee_aip->support_ship_objnum = OBJ_INDEX(docker);
3042  docker_aip->support_ship_signature = dockee->signature;
3043  dockee_aip->support_ship_signature = docker->signature;
3044  }
3045  }
3046 
3047  // add multiplayer hook here to deal with docked objects.
3048  if ( MULTIPLAYER_MASTER && update_clients)
3049  send_ai_info_update_packet( docker, AI_UPDATE_DOCK, dockee );
3050 }
3051 
3052 // code which is called when objects become undocked. Equivalent of above function.
3053 // Goober5000 - dockee must always be non-NULL
3055 {
3056  Assert((docker != NULL) && (dockee != NULL));
3057 
3058  // make sure they're not already undocked!
3059  if (!dock_check_find_direct_docked_object(docker, dockee))
3060  {
3061  Warning(LOCATION, "Call to ai_do_objects_undocked_stuff when objects are already undocked! Trace out and fix!\n");
3062  return;
3063  }
3064 
3065  // add multiplayer hook here to deal with undocked objects. Do it before we
3066  // do anything else. We don't need to send info for both objects, since multi
3067  // only supports one docked object
3068  if ( MULTIPLAYER_MASTER )
3069  send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK, dockee );
3070 
3071  if (docker->type == OBJ_SHIP && dockee->type == OBJ_SHIP)
3072  {
3073  ai_info *docker_aip = &Ai_info[Ships[docker->instance].ai_index];
3074  ai_info *dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3075 
3076  // clear stuff for both objects
3077  docker_aip->ai_flags &= ~AIF_BEING_REPAIRED;
3078  dockee_aip->ai_flags &= ~AIF_BEING_REPAIRED;
3079  docker_aip->support_ship_objnum = -1;
3080  dockee_aip->support_ship_objnum = -1;
3081  docker_aip->support_ship_signature = -1;
3082  dockee_aip->support_ship_signature = -1;
3083  }
3084 
3085  // unlink the two objects
3086  dock_undock_objects(docker, dockee);
3087 }
3088 
3089 
3090 // --------------------------------------------------------------------------
3091 // Interface from goals code to AI.
3092 // Cause *docker to dock with *dockee.
3093 // priority is priority of goal from goals code.
3094 // dock_type is:
3095 // AIDO_DOCK set goal of docking
3096 // AIDO_DOCK_NOW immediately dock, used for ships that need to be docked at mission start
3097 // AIDO_UNDOCK set goal of undocking
3098 void ai_dock_with_object(object *docker, int docker_index, object *dockee, int dockee_index, int dock_type)
3099 {
3100  Assert(docker != NULL);
3101  Assert(dockee != NULL);
3102  Assert(docker->type == OBJ_SHIP);
3103  Assert(dockee->type == OBJ_SHIP);
3104  Assert(docker->instance >= 0);
3105  Assert(dockee->instance >= 0);
3106  Assert(Ships[docker->instance].ai_index >= 0);
3107  Assert(Ships[dockee->instance].ai_index >= 0);
3108  Assert(docker_index >= 0);
3109  Assert(dockee_index >= 0);
3110 
3111  ai_info *aip = &Ai_info[Ships[docker->instance].ai_index];
3112 
3113  aip->goal_objnum = OBJ_INDEX(dockee);
3114  aip->goal_signature = dockee->signature;
3115 
3116  aip->mode = AIM_DOCK;
3117 
3118  switch (dock_type) {
3119  case AIDO_DOCK:
3120  aip->submode = AIS_DOCK_0;
3122  break;
3123  case AIDO_DOCK_NOW:
3124  aip->submode = AIS_DOCK_4A;
3126  break;
3127  case AIDO_UNDOCK:
3128  aip->submode = AIS_UNDOCK_0;
3130  break;
3131  default:
3132  Int3(); // Bogus dock_type.
3133  }
3134 
3135  // Goober5000 - we no longer need to set dock_path_index because it's easier to grab the path from the dockpoint
3136  // a debug check would be a good thing here, though
3137 #ifndef NDEBUG
3138  if (dock_type == AIDO_UNDOCK)
3139  {
3140  polymodel *pm = model_get(Ship_info[Ships[dockee->instance].ship_info_index].model_num);
3141  Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3142  }
3143 #endif
3144 
3145  // dock instantly
3146  if (dock_type == AIDO_DOCK_NOW)
3147  {
3148  // set model animations correctly
3149  // (fortunately, this function is called AFTER model_anim_set_initial_states in the sea of ship creation
3150  // functions, which is necessary for model animations to start from t=0 at the correct positions)
3151  ship *shipp = &Ships[docker->instance];
3152  ship *goal_shipp = &Ships[dockee->instance];
3153  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_1, docker_index, 1, true);
3154  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, 1, true);
3155  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, 1, true);
3156  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, 1, true);
3157  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, 1, true);
3158  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, 1, true);
3159  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, 1, true);
3160  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, 1, true);
3161 
3162  dock_orient_and_approach(docker, docker_index, dockee, dockee_index, DOA_DOCK_STAY);
3163  ai_do_objects_docked_stuff( docker, docker_index, dockee, dockee_index, false );
3164  }
3165  // pick a path to use to start docking
3166  else
3167  {
3168  int path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3169 
3170  // make sure we have a path
3171  if (path_num < 0)
3172  {
3173  Error(LOCATION, "Cannot find a dock path for ship %s, dock index %d. Aborting dock.\n", Ships[dockee->instance].ship_name, dockee_index);
3175  return;
3176  }
3177 
3178  ai_find_path(docker, OBJ_INDEX(dockee), path_num, 0);
3179  }
3180 }
3181 
3182 
3183 /*
3184  * Cause a ship to fly toward a ship.
3185  */
3186 void ai_start_fly_to_ship(object *objp, int shipnum)
3187 {
3188  ai_info *aip;
3189 
3190  aip = &Ai_info[Ships[objp->instance].ai_index];
3191 
3193  {
3194  aip->ai_flags &= ~AIF_FORMATION_WING;
3195  aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3196  }
3197  else
3198  {
3199  aip->ai_flags |= AIF_FORMATION_WING;
3200  aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3201  }
3202 
3203  aip->mode = AIM_FLY_TO_SHIP;
3205 
3206  aip->target_objnum = Ships[shipnum].objnum;
3207 
3209 }
3210 
3211 
3212 // Cause a ship to fly its waypoints.
3213 // flags tells:
3214 // WPF_REPEAT Set -> repeat waypoints.
3215 void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags)
3216 {
3217  ai_info *aip;
3218  Assert(wp_list != NULL);
3219 
3220  aip = &Ai_info[Ships[objp->instance].ai_index];
3221 
3222  if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_list == wp_list) )
3223  {
3224  if (aip->wp_index == INVALID_WAYPOINT_POSITION)
3225  {
3226  Warning(LOCATION, "aip->wp_index should have been assigned already!");
3227  aip->wp_index = 0;
3228  }
3229  return;
3230  }
3231 
3233  {
3234  aip->ai_flags &= ~AIF_FORMATION_WING;
3235  aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3236  }
3237  else
3238  {
3239  aip->ai_flags |= AIF_FORMATION_WING;
3240  aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3241  }
3242 
3243  aip->wp_list = wp_list;
3244  aip->wp_index = 0;
3245  aip->wp_flags = wp_flags;
3246  aip->mode = AIM_WAYPOINTS;
3247 
3249 }
3250 
3254 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3255 {
3256  ai_info *aip;
3257 
3258  Assert(objp != other_objp); // Bogus! Told to stay near self.
3259  Assert(objp->type == OBJ_SHIP);
3260  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3261 
3262  aip = &Ai_info[Ships[objp->instance].ai_index];
3263 
3264  aip->mode = AIM_STAY_NEAR;
3265  aip->submode = -1;
3267  aip->stay_near_distance = dist;
3268  aip->goal_objnum = OBJ_INDEX(other_objp);
3269  aip->goal_signature = other_objp->signature;
3270 }
3271 
3272 // Goober5000 - enter safety mode (used by support ships, now possibly by fighters too)
3273 void ai_do_safety(object *objp)
3274 {
3275  ai_info *aip;
3276 
3277  Assert(objp->type == OBJ_SHIP);
3278  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3279 
3280  aip = &Ai_info[Ships[objp->instance].ai_index];
3281 
3282  aip->mode = AIM_SAFETY;
3283  aip->submode = AISS_1;
3285 }
3286 
3290 void ai_form_on_wing(object *objp, object *goal_objp)
3291 {
3292  ai_info *aip;
3293  ship *shipp;
3294  ship_info *sip;
3295 
3296  // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3297  // out for this case.
3298  if ( Game_mode & GM_MULTIPLAYER ) {
3299  if ( objp == goal_objp ) {
3300  return;
3301  }
3302  }
3303 
3304  Assert(objp != goal_objp); // Bogus! Told to form on own's wing!
3305 
3306  shipp = &Ships[objp->instance];
3307  sip = &Ship_info[shipp->ship_info_index];
3308 
3309  // Only fighters or bombers allowed to form on wing.
3310  if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3311  nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3312  return;
3313  }
3314 
3315 
3316  aip = &Ai_info[Ships[objp->instance].ai_index];
3317 
3318  aip->ai_flags &= ~AIF_FORMATION_WING;
3320 
3321  aip->goal_objnum = OBJ_INDEX(goal_objp);
3322  ai_set_goal_maybe_abort_dock(objp, aip);
3323  aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4); // Super extra long time until can target another ship.
3324 
3325 }
3326 
3332 int ai_formation_object_get_slotnum(int objnum, object *objp)
3333 {
3334  int slotnum = 1; // Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3335 
3336  object *o;
3337  for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3338  if (objp == o)
3339  break;
3340  else if (o->type == OBJ_SHIP)
3342  if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3343  slotnum++;
3344  }
3345 
3346  Assert(o != END_OF_LIST(&obj_used_list)); // Didn't find objp in list of used ships. Impossible!
3347 
3348  return slotnum;
3349 }
3350 
3351 #define BIGNUM 100000.0f
3352 
3353 int Debug_k = 0;
3354 
3361 float compute_collision_time(vec3d *targpos, vec3d *targvel, vec3d *attackpos, float weapon_speed)
3362 {
3363  vec3d vec_to_target;
3364  float pos_dot_vel;
3365  float vel_sqr;
3366  float discrim;
3367 
3368  vm_vec_sub(&vec_to_target, targpos, attackpos);
3369  pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3370  vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3371  discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3372 
3373  if (discrim > 0.0f) {
3374  float t1, t2, t_solve;
3375 
3376  t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3377  t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3378 
3379  t_solve = BIGNUM;
3380 
3381  if (t1 > 0.0f)
3382  t_solve = t1;
3383  if ((t2 > 0.0f) && (t2 < t_solve))
3384  t_solve = t2;
3385 
3386  if (t_solve < BIGNUM-1.0f) {
3387  return t_solve + Debug_k * flFrametime;
3388  }
3389  }
3390 
3391  return 0.0f;
3392 }
3393 
3394 
3395 // --------------------------------------------------------------------------
3396 // If far away, use player's speed.
3397 // If in between, lerp between player and laser speed
3398 // If close, use laser speed.
3399 // Want to know how much time it will take to get to the enemy.
3400 // This function doesn't account for the fact that by the time the player
3401 // (or his laser) gets to the current enemy position, the enemy will have moved.
3402 // This is dealt with in polish_predicted_enemy_pos.
3403 float compute_time_to_enemy(float dist_to_enemy, object *pobjp)
3404 {
3405  float time_to_enemy;
3406  float pl_speed = pobjp->phys_info.speed;
3407  float max_laser_distance, max_laser_speed;
3408  int bank_num, weapon_num;
3409  ship *shipp = &Ships[pobjp->instance];
3410 
3411  bank_num = shipp->weapons.current_primary_bank;
3412  weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3413  max_laser_speed = Weapon_info[weapon_num].max_speed;
3414  max_laser_distance = MIN((max_laser_speed * Weapon_info[weapon_num].lifetime), Weapon_info[weapon_num].weapon_range);
3415 
3416  // If pretty far away, use player's speed to predict position, else
3417  // use laser's speed because when close, we care more about hitting
3418  // with a laser than about causing ship:ship rendezvous.
3419  if (dist_to_enemy > 1.5 * max_laser_distance) {
3420  if (pl_speed > 0.0f)
3421  time_to_enemy = dist_to_enemy/pl_speed;
3422  else
3423  time_to_enemy = 1.0f;
3424  } else if (dist_to_enemy > 1.1*max_laser_distance) {
3425  if (pl_speed > 0.1f) {
3426  float scale;
3427 
3428  scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3429 
3430  time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3431  } else
3432  time_to_enemy = 2.0f;
3433  } else
3434  time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3435 
3436  return time_to_enemy + flFrametime;
3437 }
3438 
3439 // Stuff *dot and *tts.
3440 // *dot is always computed. If dot is less than zero, the magnitude is
3441 // incorrect, not having been divided by distance.
3442 // If *dot is > 0.0f, then tts is computed. This is the time it will take object
3443 // *objp to get to *pos, assuming it moves right at it.
3444 void fds_aux(float *dot, float *tts, vec3d *pos, float dtime, object *objp)
3445 {
3446  vec3d v2s;
3447 
3448  vm_vec_sub(&v2s, pos, &objp->pos);
3449  *dot = vm_vec_dot(&v2s, &objp->orient.vec.fvec);
3450 
3451  if (*dot > 0.0f) {
3452  float dist;
3453 
3454  dist = vm_vec_dist(&objp->pos, pos);
3455 
3456  if (dist > 0.1f)
3457  *dot /= dist;
3458  else
3459  *dot = 1.0f;
3460 
3461  if (objp->phys_info.speed > 0.1f)
3462  *tts = dist / objp->phys_info.speed;
3463  else
3464  *tts = dist * 100.0f;
3465  }
3466 }
3467 
3468 // --------------------------------------------------------------------------
3469 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vec3d *player_pos, vec3d *enemy_pos)
3470 {
3471  *player_pos = pl_objp->pos;
3472 
3473  if (aip->next_predict_pos_time > Missiontime) {
3474  *enemy_pos = aip->last_predicted_enemy_pos;
3475  } else {
3476  *enemy_pos = en_objp->pos;
3477 
3479  aip->last_predicted_enemy_pos = *enemy_pos;
3480  }
3481 
3482 
3483 }
3484 
3491 {
3492  if (Missiontime >= aip->next_aim_pos_time)
3493  {
3494  aip->last_aim_enemy_pos = En_objp->pos;
3495  aip->last_aim_enemy_vel = En_objp->phys_info.vel;
3497  }
3498  else
3499  {
3500  //Update the position based on the velocity (assume no velocity vector change)
3502  }
3503 }
3504 
3505 // Given an ai_info struct, by reading current goal and path information,
3506 // extract base path information and return in pmp and pmpv.
3507 // Return true if found, else return false.
3508 // false means the current point is not on the original path.
3509 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
3510 {
3511  pnode *pn = &Path_points[path_cur];
3512  ship *shipp = &Ships[Objects[goal_objnum].instance];
3513  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
3514 
3515  *pmpv = NULL;
3516  *pmp = NULL;
3517 
3518  if (pn->path_num != -1) {
3519  *pmp = &pm->paths[pn->path_num];
3520  if (pn->path_index != -1)
3521  *pmpv = &(*pmp)->verts[pn->path_index];
3522  else
3523  return 0;
3524  } else
3525  return 0;
3526 
3527  return 1;
3528 }
3529 
3530 // Modify, in place, the points in a global model path.
3531 // Only modify those points that are defined in the model path. Don't modify the
3532 // leadin points, such as those that are necessary to get the model on the path.
3534 {
3535  ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
3536  object *mobjp = &Objects[aip->path_objnum];
3537  polymodel *pm = model_get(Ship_info[Ships[mobjp->instance].ship_info_index].model_num);
3538  pnode *pnp;
3539  int path_num, dir;
3540 
3541  Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
3542 
3543  pnp = &Path_points[aip->path_start];
3544  while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
3545  pnp++;
3546 
3547  path_num = pnp->path_num;
3548  Assert((path_num >= 0) && (path_num < pm->n_paths));
3549 
3550  Assert(pnp->path_index != -1); // If this is -1, that means we never found the model path points
3551 
3552  dir = 1;
3553  if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
3554  dir = -1;
3555  }
3556 
3557  copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
3558 }
3559 
3560 // Return an indication of the distance between two matrices.
3561 // This is the sum of the distances of their dot products from 1.0f.
3562 float ai_matrix_dist(matrix *mat1, matrix *mat2)
3563 {
3564  float t;
3565 
3566  t = 1.0f - vm_vec_dot(&mat1->vec.fvec, &mat2->vec.fvec);
3567  t += 1.0f - vm_vec_dot(&mat1->vec.uvec, &mat2->vec.uvec);
3568  t += 1.0f - vm_vec_dot(&mat1->vec.rvec, &mat2->vec.rvec);
3569 
3570  return t;
3571 }
3572 
3573 
3574 // Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
3575 // This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
3576 // prevents this from happening too often.
3577 // force_recreate_flag TRUE means to recreate regardless of timestamp.
3578 // Returns TRUE if path recreated.
3579 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag, int override_hash = 0)
3580 {
3581  int hashval;
3582 
3583  Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
3584 
3585  if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
3586  if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
3587  force_recreate_flag = 1;
3588 
3589  // If no path, that means we don't need one.
3590  if (aip->path_start == -1)
3591  return 0.0f;
3592 
3593  // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate. This is needed when ships
3594  // emerge from fighter bays. We don't need to recreate the path.. and in case the
3595  // parent ship dies, we still want to be able to continue on the path
3596  if ( aip->ai_flags & AIF_USE_STATIC_PATH )
3597  return 0.0f;
3598 
3599  if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
3600  object *path_objp;
3601 
3602  path_objp = &Objects[aip->path_objnum];
3603  hashval = create_object_hash(path_objp);
3604 
3605  if (override_hash || (hashval != aip->path_goal_obj_hash)) {
3606  float dist;
3607 
3608  dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
3609  dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
3610 
3611  if (force_recreate_flag || (dist > 2.0f)) {
3612  aip->path_next_create_time = timestamp(1000); // Update again in as little as 1000 milliseconds, ie 1 second.
3613  aip->path_goal_obj_hash = hashval;
3615 
3616  aip->path_create_pos = path_objp->pos;
3617  aip->path_create_orient = path_objp->orient;
3618 
3619  //Get path departure orientation from ships.tbl if it exists, otherwise zero it
3620  ship_info *osip = &Ship_info[Ships[path_objp->instance].ship_info_index];
3621  model_path *mp = &model_get(osip->model_num)->paths[aip->mp_index];
3622  SCP_string pathName(mp->name);
3623  if (osip->pathMetadata.find(pathName) != osip->pathMetadata.end() && !IS_VEC_NULL(&osip->pathMetadata[pathName].departure_rvec))
3624  {
3625  vm_vec_copy_normalize(&aip->path_depart_orient, &osip->pathMetadata[pathName].departure_rvec);
3626  }
3627  else
3628  {
3630  }
3631 
3632  return dist;
3633  }
3634  }
3635  }
3636 
3637  return 0.0f;
3638 }
3639 
3643 void set_accel_for_docking(object *objp, ai_info *aip, float dot, float dot_to_next, float dist_to_next, float dist_to_goal, ship_info *sip, float max_allowed_speed, object *gobjp)
3644 {
3645  float prev_dot_to_goal = aip->prev_dot_to_goal;
3646 
3647  aip->prev_dot_to_goal = dot;
3648 
3649  if (objp->phys_info.speed < 0.0f) {
3650  accelerate_ship(aip, 1.0f/32.0f);
3651  } else if ((prev_dot_to_goal-dot) > 0.01) {
3652  if (prev_dot_to_goal > dot + 0.05f) {
3653  accelerate_ship(aip, 0.0f);
3654  } else {
3655  change_acceleration(aip, -1.0f); // -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
3656  }
3657  } else {
3658  float max_bay_speed = sip->max_speed;
3659 
3660  // Maybe gradually ramp up/down the speed of a ship flying a fighterbay path
3661  if (aip->mode == AIM_BAY_EMERGE || (aip->mode == AIM_BAY_DEPART && aip->path_cur != aip->path_start)) {
3662  ship_info *gsip = &Ship_info[Ships[gobjp->instance].ship_info_index];
3663  polymodel *pm = model_get(gsip->model_num);
3664  SCP_string pathName(pm->paths[Path_points[aip->path_start].path_num].name);
3665  float speed_mult = FLT_MIN;
3666 
3667  if (aip->mode == AIM_BAY_EMERGE) { // Arriving
3668  if (gsip->pathMetadata.find(pathName) != gsip->pathMetadata.end()) {
3669  speed_mult = gsip->pathMetadata[pathName].arrive_speed_mult;
3670  }
3671 
3672  if (speed_mult == FLT_MIN) {
3674  }
3675  } else { // Departing
3676  if (gsip->pathMetadata.find(pathName) != gsip->pathMetadata.end()) {
3677  speed_mult = gsip->pathMetadata[pathName].depart_speed_mult;
3678  }
3679 
3680  if (speed_mult == FLT_MIN) {
3682  }
3683  }
3684 
3685  if (speed_mult != FLT_MIN && speed_mult != 1.0f) {
3686  // We use the distance between the first and last point on the path here; it's not accurate
3687  // if the path is not straight, but should be good enough usually; can be changed if necessary.
3688  float total_path_length = vm_vec_dist_quick(&Path_points[aip->path_start].pos, &Path_points[aip->path_start + aip->path_length - 1].pos);
3689  float dist_to_end;
3690 
3691  if (aip->mode == AIM_BAY_EMERGE) { // Arriving
3692  dist_to_end = vm_vec_dist_quick(&Pl_objp->pos, &Path_points[aip->path_start].pos);
3693  } else { // Departing
3694  dist_to_end = vm_vec_dist_quick(&Pl_objp->pos, &Path_points[aip->path_start + aip->path_length - 1].pos);
3695  }
3696 
3697  // Calculate max speed, but respect the waypoint speed cap if it's lower
3698  max_bay_speed = sip->max_speed * (speed_mult + (1.0f - speed_mult) * (dist_to_end / total_path_length));
3699  }
3700  }
3701 
3702  // Cap speed to max_allowed_speed, if it's set
3703  if (max_allowed_speed <= 0)
3704  max_allowed_speed = max_bay_speed;
3705  else
3706  max_allowed_speed = MIN(max_allowed_speed, max_bay_speed);
3707 
3708  if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
3709  set_accel_for_target_speed(objp, max_allowed_speed * MAX(dist_to_next/500.0f, 1.0f));
3710  } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
3711  if (dist_to_goal > 200.0f) {
3712  set_accel_for_target_speed(objp, max_allowed_speed * (dot + 1.0f) / 2.0f);
3713  }
3714  else {
3715  float xdot;
3716 
3717  xdot = (dot_to_next + dot)/2.0f;
3718  if (xdot < 0.0f)
3719  xdot = 0.0f;
3720 
3721  // AL: if following a path not in dock mode, move full speed
3722  if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
3723  set_accel_for_target_speed(objp, max_allowed_speed*dot*dot*dot);
3724  } else {
3725  if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
3726  set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
3727  } else {
3728  set_accel_for_target_speed(objp, max_allowed_speed * (2*xdot + 0.25f)/4.0f);
3729  }
3730  }
3731  }
3732  } else {
3733  float xdot;
3734 
3735  xdot = MAX(dot_to_next, 0.1f);
3736  if ( aip->mode != AIM_DOCK ) {
3737  set_accel_for_target_speed(objp, max_allowed_speed);
3738  } else {
3739  float speed;
3740  if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
3741  speed = dist_to_goal/8.0f + 2.0f;
3742  } else if (dist_to_goal < 4*objp->radius + 50.0f) {
3743  speed = dist_to_goal/4.0f + 4.0f;
3744  } else {
3745  speed = max_allowed_speed * (3*xdot + 1.0f)/4.0f;
3746  }
3747  if (aip->mode == AIM_DOCK) {
3748  speed = speed * 2.0f + 1.0f;
3749  if (aip->goal_objnum != -1) {
3750  speed += Objects[aip->goal_objnum].phys_info.speed;
3751  }
3752  }
3753 
3754  set_accel_for_target_speed(objp, speed);
3755  }
3756  }
3757  }
3758 }
3759 
3760 // --------------------------------------------------------------------------
3761 // Follow a path associated with a large object, such as a capital ship.
3762 // The points defined on the path are in the object's reference frame.
3763 // The object of interest is goal_objnum.
3764 // The paths are defined in the model. The path of interest is wp_list.
3765 // The next goal point in the path is wp_index.
3766 // wp_flags contain special information specific to the path.
3767 
3768 // The path vertices are defined by model_path structs:
3769 // typedef struct model_path {
3770 // char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
3771 // int nverts;
3772 // vec3d *verts;
3773 // } model_path;
3774 
3775 // The polymodel struct for the object contains the following:
3776 // int n_paths;
3777 // model_path *paths;
3778 
3779 // Returns distance to goal point.
3780 float ai_path_0()
3781 {
3782  int num_points;
3783  float dot, dist_to_goal, dist_to_next, dot_to_next;
3784  ship *shipp = &Ships[Pl_objp->instance];
3785  ship_info *sip = &Ship_info[shipp->ship_info_index];
3786  ai_info *aip;
3787  vec3d nvel_vec;
3788  float mag;
3789  vec3d temp_vec, *slop_vec;
3790  object *gobjp;
3791  vec3d *cvp, *nvp, next_vec, gcvp, gnvp; // current and next vertices in global coordinates.
3792 
3793  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
3794 
3795  Assert(aip->goal_objnum != -1);
3797 
3798  gobjp = &Objects[aip->goal_objnum];
3799 
3800  if (aip->path_start == -1) {
3801  Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
3802  int path_num;
3803  Assert(aip->active_goal >= 0);
3804  ai_goal *aigp = &aip->goals[aip->active_goal];
3806 
3808  ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
3809  }
3810 
3811  maybe_recreate_path(Pl_objp, aip, 0);
3812 
3813  num_points = aip->path_length;
3814 
3815  // Set cvp and nvp as pointers to current and next vertices of interest on path.
3816  cvp = &Path_points[aip->path_cur].pos;
3817  if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
3818  nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
3819  else {
3820  // If this is 0, then path length must be 1 which means we have no direction!
3821  Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
3822  // Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
3823  if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
3824  if (aip->path_dir == 1)
3825  aip->path_cur = aip->path_start;
3826  else
3827  aip->path_cur = aip->path_start + num_points - 1;
3828  }
3829 
3830  vec3d delvec;
3831  vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
3832  vm_vec_normalize(&delvec);
3833  vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
3834  nvp = &next_vec;
3835  }
3836 
3837  // See if can reach next point (as opposed to current point)
3838  // However, don't do this if docking and next point is last point.
3839  // That is, we don't want to pursue the last point under control of the
3840  // path code. In docking, this is a special hack.
3841  if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
3842  if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
3844  aip->path_next_check_time = timestamp( 3000 );
3845  if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
3846  cvp = nvp;
3847  aip->path_cur += aip->path_dir;
3848  nvp = &Path_points[aip->path_cur].pos;
3849  }
3850  }
3851  }
3852  }
3853 
3854  gcvp = *cvp;
3855  gnvp = *nvp;
3856 
3857  dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
3858  dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
3859  // Can't use fvec, need to use velocity vector because we aren't necessarily
3860  // moving in the direction we're facing.
3861 
3863  mag = 0.0f;
3864  vm_vec_zero(&nvel_vec);
3865  } else
3866  mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
3867 
3868  // If moving not-very-slowly and sliding, then try to slide at goal, rather than
3869  // point at goal.
3870  slop_vec = NULL;
3871  if (mag < 1.0f)
3872  nvel_vec = Pl_objp->orient.vec.fvec;
3873  else if (mag > 5.0f) {
3874  float nv_dot;
3875  nv_dot = vm_vec_dot(&Pl_objp->orient.vec.fvec, &nvel_vec);
3876  if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
3877  slop_vec = &temp_vec;
3878  vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.vec.fvec);
3879  }
3880  }
3881 
3882  if (dist_to_goal > 0.1f)
3883  ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
3884 
3885  // Code to control speed is MUCH less forgiving in path following than in waypoint
3886  // following. Must be very close to path or might hit objects.
3887  dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
3888  dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
3889 
3890  set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip, 0, gobjp);
3891  aip->prev_dot_to_goal = dot;
3892 
3893  // If moving at a non-tiny velocity, detect attaining path point by its being close to
3894  // line between previous and current object location.
3895  if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
3896  vec3d nearest_point;
3897  float r, min_dist_to_goal;
3898 
3899  r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
3900 
3901  // Set min_dist_to_goal = how close must be to waypoint to pick next one.
3902  // If docking and this is the second last waypoint, must be very close.
3903  if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
3904  min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
3905  else
3906  min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
3907 
3908  if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
3909  (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius)))) {
3910  aip->path_cur += aip->path_dir;
3911  if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
3912  Assert(aip->mode != AIM_DOCK); // If docking, should never get this far, getting to last point handled outside ai_path()
3913  aip->path_dir = -aip->path_dir;
3914  }
3915  }
3916  }
3917 
3918  return dist_to_goal;
3919 }
3920 
3921 // --------------------------------------------------------------------------
3922 // Alternate version of ai_path
3923 // 1.
3924 float ai_path_1()
3925 {
3926  int num_points;
3927  float dot, dist_to_goal, dist_to_next, dot_to_next;
3928  ship *shipp = &Ships[Pl_objp->instance];
3929  ship_info *sip = &Ship_info[shipp->ship_info_index];
3930  ai_info *aip;
3931  vec3d nvel_vec;
3932  float mag;
3933  object *gobjp;
3934  vec3d *pvp, *cvp, *nvp, next_vec, gpvp, gcvp, gnvp; // previous, current and next vertices in global coordinates.
3935 
3936  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
3937 
3938  Assert(aip->goal_objnum != -1);
3940 
3941  gobjp = &Objects[aip->goal_objnum];
3942 
3943  if (aip->path_start == -1) {
3944  Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
3945  int path_num;
3946  Assert(aip->active_goal >= 0);
3947  ai_goal *aigp = &aip->goals[aip->active_goal];
3949 
3951  ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
3952  }
3953 
3954  maybe_recreate_path(Pl_objp, aip, 0);
3955 
3956  num_points = aip->path_length;
3957 
3958  // Set cvp and nvp as pointers to current and next vertices of interest on path.
3959  cvp = &Path_points[aip->path_cur].pos;
3960  if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
3961  nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
3962  else {
3963  // If this is 0, then path length must be 1 which means we have no direction!
3964  Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
3965  // Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
3966  if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
3967  if (aip->path_dir == 1)
3968  aip->path_cur = aip->path_start;
3969  else
3970  aip->path_cur = aip->path_start + num_points - 1;
3971  }
3972 
3973  vec3d delvec;
3974  vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
3975  vm_vec_normalize(&delvec);
3976  vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
3977  nvp = &next_vec;
3978  }
3979  // Set pvp to the previous vertex of interest on the path (if there is one)
3980  int prev_point = aip->path_cur - aip->path_dir;
3981  if (prev_point >= aip->path_start && prev_point <= aip->path_start + num_points)
3982  pvp = &Path_points[prev_point].pos;
3983  else
3984  pvp = NULL;
3985 
3986  gpvp = (pvp != NULL) ? *pvp : *cvp;
3987  gcvp = *cvp;
3988  gnvp = *nvp;
3989 
3990  dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
3991  dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
3992 
3993  // Can't use fvec, need to use velocity vector because we aren't necessarily
3994  // moving in the direction we're facing.
3996  mag = 0.0f;
3997  vm_vec_zero(&nvel_vec);
3998  } else
3999  mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4000 
4001  if (mag < 1.0f)
4002  nvel_vec = Pl_objp->orient.vec.fvec;
4003 
4004  // For departures, optionally rotate so the ship is pointing the correct direction
4005  // (so you can always have "wheels-down" landings)
4006  vec3d rvec;
4007  vec3d *prvec = NULL;
4008  if ( aip->mode == AIM_BAY_DEPART && !IS_VEC_NULL(&aip->path_depart_orient) ) {
4010  prvec = &rvec;
4011  }
4012 
4013  // Turn toward a point between where we are on the path and the goal. This helps keep the ship flying
4014  // "down the line"
4015  if (dist_to_goal > 0.1f) {
4016  //Get nearest point on line between current and previous path points
4017  vec3d closest_point;
4018  float r = find_nearest_point_on_line(&closest_point, &gpvp, &gcvp, &Pl_objp->pos);
4019 
4020  //Turn towards the 1/3 point between the closest point on the line and the current goal point
4021  //(unless we are already past the current goal)
4022  if (r <= 1.0f) {
4023  vec3d midpoint;
4024  vm_vec_avg3(&midpoint, &gcvp, &closest_point, &closest_point);
4025  ai_turn_towards_vector(&midpoint, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, prvec);
4026  }
4027  else {
4028  ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, prvec);
4029  }
4030  }
4031 
4032  // Code to control speed is MUCH less forgiving in path following than in waypoint
4033  // following. Must be very close to path or might hit objects.
4034  dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4035  dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4036 
4037  // This path mode respects the "cap-waypoint-speed" SEXP
4038  float max_allowed_speed = 0.0f;
4039  if ( aip->waypoint_speed_cap > 0) {
4040  max_allowed_speed = (float) aip->waypoint_speed_cap;
4041  }
4042 
4043  set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip, max_allowed_speed, gobjp);
4044  aip->prev_dot_to_goal = dot;
4045 
4046  // If moving at a non-tiny velocity, detect attaining path point by its being close to
4047  // line between previous and current object location.
4048  if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4049  vec3d nearest_point;
4050  float r, min_dist_to_goal;
4051 
4052  r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4053 
4054  // Set min_dist_to_goal = how close must be to waypoint to pick next one.
4055  // If docking and this is the second last waypoint, must be very close.
4056  if ((aip->mode == AIM_DOCK) && (aip->path_cur - aip->path_start >= aip->path_length-1)) {
4057  min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4058  }
4059  // If this is not the last waypoint, include a term to compensate for inertia
4060  else if (aip->path_cur - aip->path_start < aip->path_length - (aip->mode == AIM_DOCK ? 0 : 1)) {
4061  vec3d cp_rel;
4062  vm_vec_sub(&cp_rel, &gcvp, &Pl_objp->pos);
4063  float goal_mag = 0.0f;
4064  if (vm_vec_mag(&cp_rel) > 0.0f) {
4065  vm_vec_normalize(&cp_rel);
4066  goal_mag = vm_vec_dot(&Pl_objp->phys_info.vel, &cp_rel);
4067  }
4068  min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius + (goal_mag * sip->damp);
4069  }
4070  else {
4071  min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4072  }
4073 
4074  if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4075  (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius)))) {
4076  aip->path_cur += aip->path_dir;
4077  if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4078  Assert(aip->mode != AIM_DOCK); // If docking, should never get this far, getting to last point handled outside ai_path()
4079  aip->path_dir = -aip->path_dir;
4080  }
4081  }
4082  }
4083 
4084  return dist_to_goal;
4085 }
4086 
4087 float ai_path()
4088 {
4090  {
4091  case AI_PATH_MODE_NORMAL:
4092  return ai_path_0();
4093  break;
4094  case AI_PATH_MODE_ALT1:
4095  return ai_path_1();
4096  break;
4097  default:
4098  Error(LOCATION, "Invalid path mode found: %d\n", The_mission.ai_profile->ai_path_mode);
4099  return ai_path_0();
4100  }
4101 }
4102 
4103 // Only needed because CLAMP() doesn't handle pointers
4104 void update_min_max(float val, float *min, float *max)
4105 {
4106  if (val < *min)
4107  *min = val;
4108  else if (val > *max)
4109  *max = val;
4110 }
4111 
4112 // Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4113 // Stuff ni min_vec and max_vec.
4114 // Return value: Number of enemy objects in bounding box.
4115 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vec3d *min_vec, vec3d *max_vec)
4116 {
4117  object *objp;
4118  ship_obj *so;
4119  int count = 0;
4120 
4121  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4122  objp = &Objects[so->objnum];
4123  if (iff_matches_mask(Ships[objp->instance].team, enemy_team_mask)) {
4125  if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4126  if (count == 0) {
4127  *min_vec = objp->pos;
4128  *max_vec = objp->pos;
4129  count++;
4130  } else {
4131  update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4132  update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4133  update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4134  }
4135  }
4136 
4137  }
4138  }
4139 
4140  return count;
4141 }
4142 
4143 // Pick a relatively safe spot for objp to fly to.
4144 // Problem:
4145 // Finds a spot away from any enemy within a bounding box.
4146 // Doesn't verify that "safe spot" is not near some other enemy.
4148 {
4149  int objnum;
4150  int enemy_team_mask;
4151  vec3d min_vec, max_vec;
4152  vec3d vec_to_center, center;
4153  vec3d goal_pos;
4154 
4155  objnum = OBJ_INDEX(objp);
4156 
4157  enemy_team_mask = iff_get_attacker_mask(obj_team(&Objects[objnum]));
4158 
4159  if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4160  vm_vec_avg(&center, &min_vec, &max_vec);
4161  vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4162 
4163  vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4164  } else
4165  vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.vec.fvec, 100.0f);
4166 
4167  Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4168 }
4169 
4170 // Fly to desired safe point.
4171 // Returns distance to that point.
4173 {
4174  float dot, dist;
4175  ai_info *aip;
4176  vec3d vec_to_goal;
4177  ship_info *sip;
4178  float dot_val;
4179 
4180  sip = &Ship_info[Ships[objp->instance].ship_info_index];
4181 
4182  aip = &Ai_info[Ships[objp->instance].ai_index];
4183  dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4184  dot = vm_vec_dot(&vec_to_goal, &objp->orient.vec.fvec);
4185 
4186  dot_val = (1.1f + dot) / 2.0f;
4187  if (dist > 200.0f) {
4188  set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4189  } else
4190  set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4191 
4192  return dist;
4193 }
4194 
4196 {
4197  vec3d goal_point;
4198  ship_info *sip;
4199  float dot;
4200 
4201  sip = &Ship_info[Ships[objp->instance].ship_info_index];
4202 
4203  goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4204  dot = turn_towards_tangent(objp, &goal_point, 250.0f); // Increased from 50 to 250 to make circling not look so wacky.
4205 
4206  set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4207 }
4208 
4209 // --------------------------------------------------------------------------
4211 {
4212  ai_info *aip;
4213 
4214  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4215 
4216  switch (aip->submode) {
4217  case AISS_1:
4218  ai_safety_pick_spot(Pl_objp);
4219  aip->submode = AISS_2;
4221  break;
4222  case AISS_1a: // Pick a safe point because we just got whacked!
4223  Int3();
4224  break;
4225  case AISS_2:
4226  if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4227  aip->submode = AISS_3;
4229  }
4230  break;
4231  case AISS_3:
4232  ai_safety_circle_spot(Pl_objp);
4233  break;
4234  default:
4235  Int3(); // Illegal submode for ai_safety();
4236  break;
4237  }
4238 }
4239 
4240 
4241 // --------------------------------------------------------------------------
4242 // make Pl_objp fly to its target's position
4243 // optionally returns true if Pl_objp (pl_done_p) has reached the target position (can be NULL)
4244 // optionally returns true if Pl_objp's (pl_treat_as_ship_p) fly to order was issued to him directly,
4245 // or if the order was issued to his wing (returns false) (can be NULL, and result is only
4246 // valid if Pl_done_p was not NULL and was set true by function.
4247 void ai_fly_to_target_position(vec3d* target_pos, bool* pl_done_p=NULL, bool* pl_treat_as_ship_p=NULL)
4248 {
4249  float dot, dist_to_goal;
4250  ship *shipp = &Ships[Pl_objp->instance];
4251  ship_info *sip = &Ship_info[shipp->ship_info_index];
4252  ai_info *aip;
4253  vec3d nvel_vec;
4254  float mag;
4255  float prev_dot_to_goal;
4256  vec3d temp_vec;
4257  vec3d *slop_vec;
4258  int j;
4259  bool ab_allowed = true;
4260 
4261  Assert( target_pos != NULL );
4262 
4263  if ( pl_done_p != NULL ) {
4264  // initialize this to false now incase we leave early so that that
4265  // the caller of this function doesn't get any funny ideas about
4266  // the status of the waypoint
4267  *pl_done_p = false;
4268  }
4269 
4270  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4271 
4272  /* I shouldn't be flying to position for what ever called me any more.
4273  Set mode to none so that default dynamic behaviour gets started up again. */
4274  if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4275  aip->mode = AIM_NONE;
4276  }
4277 
4278  dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, target_pos);
4279 
4280  // Can't use fvec, need to use velocity vector because we aren't necessarily
4281  // moving in the direction we're facing.
4282  // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4283  // If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4285  mag = 0.0f;
4286  vm_vec_zero(&nvel_vec);
4287  } else {
4288  mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4289  }
4290 
4291  // If moving not-very-slowly and sliding, then try to slide at goal, rather than
4292  // point at goal.
4293  slop_vec = NULL;
4294  if (mag < 1.0f) {
4295  nvel_vec = Pl_objp->orient.vec.fvec;
4296  } else if (mag > 5.0f) {
4297  float nv_dot;
4298  nv_dot = vm_vec_dot(&Pl_objp->orient.vec.fvec, &nvel_vec);
4299  if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4300  slop_vec = &temp_vec;
4301  vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.vec.fvec);
4302  }
4303  }
4304 
4305  // If a wing leader, take turns more slowly, based on size of wing.
4306  int scale;
4307 
4308  if (aip->wing >= 0) {
4309  scale = Wings[aip->wing].current_count;
4310  scale = (int) ((scale+1)/2);
4311  } else {
4312  scale = 1;
4313  }
4314 
4315  // ----------------------------------------------
4316  // if in autopilot mode make sure to not collide
4317  // and "keep reasonable distance"
4318  // this needs to be done for ALL SHIPS not just capships STOP CHANGING THIS
4319  // ----------------------------------------------
4320 
4321  vec3d perp, goal_point;
4322 
4323  bool carry_flag = ((shipp->flags2 & SF2_NAVPOINT_CARRY) || ((shipp->wingnum >= 0) && (Wings[shipp->wingnum].flags & WF_NAV_CARRY)));
4324 
4325  if (AutoPilotEngaged)
4326  ab_allowed = false;
4327 
4328  if (AutoPilotEngaged
4330  && carry_flag
4331  && ((The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) || (Pl_objp != Autopilot_flight_leader)) )
4332  {
4333  Assertion( Autopilot_flight_leader != NULL, "When under autopilot there must be a flight leader" );
4334  // snap wings into formation them into formation
4336  if (aip->wing != -1) {
4337  int wing_index = get_wing_index(Pl_objp, aip->wing);
4338  object *wing_leader = get_wing_leader(aip->wing);
4339 
4340  if (wing_leader != Pl_objp) {
4341  // not wing leader.. get my position relative to wing leader
4342  get_absolute_wing_pos_autopilot(&goal_point, wing_leader, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
4343  } else {
4344  // Am wing leader.. get the wings position relative to the flight leader
4345  j = 1+int( (float)floor(double(autopilot_wings[aip->wing]-1)/2.0) );
4346 
4347  switch (autopilot_wings[aip->wing] % 2) {
4348  case 1: // back-left
4349  vm_vec_copy_normalize(&perp, &Autopilot_flight_leader->orient.vec.rvec);
4350  vm_vec_scale(&perp, -166.0f*j); // 166m is supposedly the optimal range according to tolwyn
4351  vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp);
4352  break;
4353 
4354  default: //back-right
4355  case 0:
4356  vm_vec_copy_normalize(&perp, &Autopilot_flight_leader->orient.vec.rvec);
4357  vm_vec_scale(&perp, 166.0f*j);
4358  vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp);
4359  break;
4360  }
4361 
4362  }
4363 
4364  Pl_objp->pos = goal_point;
4365  }
4366 
4367  vm_vec_sub(&perp, Navs[CurrentNav].GetPosition(), &Autopilot_flight_leader->pos);
4368  vm_vector_2_matrix(&Pl_objp->orient, &perp, NULL, NULL);
4369  } else {
4370  vm_vec_scale_add(&perp, &Pl_objp->pos, &Autopilot_flight_leader->phys_info.vel, 1000.0f);
4371  ai_turn_towards_vector(&perp, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4372  }
4373  } else {
4374  if (dist_to_goal > 0.1f) {
4375  ai_turn_towards_vector(target_pos, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4376  }
4377  }
4378 
4379  // ----------------------------------------------
4380 
4381  prev_dot_to_goal = aip->prev_dot_to_goal;
4382  dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, target_pos);
4383  aip->prev_dot_to_goal = dot;
4384 
4385  if (Pl_objp->phys_info.speed < 0.0f) {
4386  accelerate_ship(aip, 1.0f/32);
4387  ab_allowed = false;
4388  } else if (prev_dot_to_goal > dot+0.01f) {
4389  // We are further from pointing at our goal this frame than last frame, so slow down.
4390  set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4391  ab_allowed = false;
4392  } else if (dist_to_goal < 100.0f) {
4393  float slew_dot = vm_vec_dot(&Pl_objp->orient.vec.fvec, &nvel_vec);
4394  if (fl_abs(slew_dot) < 0.9f) {
4395  accelerate_ship(aip, 0.0f);
4396  } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4397  accelerate_ship(aip, 0.0f);
4398  } else {
4399  accelerate_ship(aip, 0.5f * dot * dot);
4400  }
4401  ab_allowed = false;
4402  } else {
4403  float dot1;
4404  if (dist_to_goal < 250.0f) {
4405  dot1 = dot*dot*dot; // Very important to be pointing towards goal when nearby. Note, cubing preserves sign.
4406  } else {
4407  if (dot > 0.0f) {
4408  dot1 = dot*dot;
4409  } else {
4410  dot1 = dot;
4411  }
4412  }
4413 
4414  if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4415  if (dot < 0.2f) {
4416  dot1 = 0.2f;
4417  }
4418  }
4419 
4420  if (sip->flags & SIF_SMALL_SHIP) {
4421  set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4422  } else {
4423  set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4424  }
4425  if (dot1 < 0.8f)
4426  ab_allowed = false;
4427  }
4428 
4429  if (!(sip->flags & SIF_AFTERBURNER) || (shipp->flags2 & SF2_AFTERBURNER_LOCKED) || !(aip->ai_flags & AIF_FREE_AFTERBURNER_USE)) {
4430  ab_allowed = false;
4431  }
4432 
4433  // Make sure not travelling too fast for someone to keep up.
4434  float max_allowed_speed = 9999.9f;
4435  float max_allowed_ab_speed = 10000.1f;
4436  float self_ab_speed = 10000.0f;
4437 
4438  if (shipp->wingnum != -1) {
4439  max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4440  max_allowed_ab_speed = 0.95f * get_wing_lowest_av_ab_speed(Pl_objp);
4441  if (ab_allowed) {
4443  self_ab_scale = sip->afterburner_recover_rate * self_ab_scale / (sip->afterburner_burn_rate + sip->afterburner_recover_rate * self_ab_scale);
4444  self_ab_speed = 0.95f * (self_ab_scale * (Pl_objp->phys_info.afterburner_max_vel.xyz.z - shipp->current_max_speed) + shipp->current_max_speed);
4445  }
4446  }
4447 
4448  // check if waypoint speed cap is set and adjust max speed
4449  if (aip->waypoint_speed_cap > 0) {
4450  max_allowed_speed = (float) aip->waypoint_speed_cap;
4451  max_allowed_ab_speed = max_allowed_speed;
4452  }
4453 
4454  if ((self_ab_speed < 5.0f) || (max_allowed_ab_speed < 5.0f)){ // || ((shipp->wingnum != -1) && (dist_to_goal / max_allowed_ab_speed < 5.0f))){
4455  ab_allowed = false;
4456  } else if (max_allowed_ab_speed < shipp->current_max_speed) {
4457  if (max_allowed_speed < max_allowed_ab_speed) {
4458  max_allowed_speed = max_allowed_ab_speed;
4459  } else {
4460  ab_allowed = false;
4461  }
4462  }
4463 
4464 
4465  if (ab_allowed) {
4466  if (self_ab_speed <= (1.001 * max_allowed_ab_speed)){
4467  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
4468  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
4469  if (percent_left > 30.0f) {
4470  afterburners_start(Pl_objp);
4471  aip->afterburner_stop_time = Missiontime + 5 * F1_0;
4472  }
4473  }
4474  } else {
4475  float switch_value;
4476  if ((self_ab_speed - max_allowed_ab_speed) > (0.2f * max_allowed_ab_speed)) {
4477  switch_value = 0.2f * max_allowed_ab_speed;
4478  } else {
4479  switch_value = self_ab_speed - max_allowed_ab_speed;
4480  }
4481  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
4482  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
4483  if ((Pl_objp->phys_info.speed < (max_allowed_ab_speed - switch_value)) && (percent_left > 30.0f)){
4484  afterburners_start(Pl_objp);
4485  aip->afterburner_stop_time = Missiontime + 5 * F1_0;
4486  }
4487  } else {
4488  if (Pl_objp->phys_info.speed > (max_allowed_ab_speed + 0.9 * switch_value)){
4489  afterburners_stop(Pl_objp);
4490  }
4491  }
4492  }
4493 
4494  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
4495  if (aip->prev_accel * shipp->current_max_speed > max_allowed_ab_speed) {
4496  accelerate_ship(aip, max_allowed_ab_speed / shipp->current_max_speed);
4497  }
4498  }
4499  } else {
4500  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
4501  afterburners_stop(Pl_objp);
4502  }
4503  if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4504  accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4505  }
4506  }
4507 
4508  if ( (dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) ) {
4509  vec3d nearest_point;
4510  float r;
4511 
4512  r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, target_pos);
4513 
4514  if ( (dist_to_goal < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius) + vm_vec_dist_quick(&Pl_objp->pos, &Pl_objp->last_pos)))
4515  || (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, target_pos) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))))
4516  {
4517  int treat_as_ship;
4518 
4519  // we must be careful when dealing with wings. A ship in a wing might be completing
4520  // a waypoint for for the entire wing, or it might be completing a goal for itself. If
4521  // for itself and in a wing, treat the completion as we would a ship
4522  treat_as_ship = 1;
4523  if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4524  int type;
4525 
4526  // protect array access from invalid indexes
4527  if ((aip->active_goal >= 0) && (aip->active_goal < MAX_AI_GOALS)) {
4528  type = aip->goals[aip->active_goal].type;
4529  if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4530  treat_as_ship = 0;
4531  } else {
4532  treat_as_ship = 1;
4533  }
4534  }
4535  }
4536  // setup out parameters
4537  if ( pl_done_p != NULL ) {
4538  *pl_done_p = true;
4539  if ( pl_treat_as_ship_p != NULL ) {
4540  if ( treat_as_ship ) {
4541  *pl_treat_as_ship_p = true;
4542  } else {
4543  *pl_treat_as_ship_p = false;
4544  }
4545  }
4546  } else {
4547  Assertion( pl_treat_as_ship_p != NULL,
4548  "pl_done_p cannot be NULL while pl_treat_as_ship_p is not NULL" );
4549  }
4550  }
4551  }
4552 }
4553 
4554 // --------------------------------------------------------------------------
4555 // make Pl_objp fly waypoints.
4557 {
4558  ai_info *aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4559 
4560  // sanity checking for stuff that should never happen
4561  if (aip->wp_index == INVALID_WAYPOINT_POSITION) {
4562  if (aip->wp_list == NULL) {
4563  Warning(LOCATION, "Waypoints should have been assigned already!");
4564  ai_start_waypoints(Pl_objp, &Waypoint_lists.front(), WPF_REPEAT);
4565  } else {
4566  Warning(LOCATION, "Waypoints should have been started already!");
4567  ai_start_waypoints(Pl_objp, aip->wp_list, WPF_REPEAT);
4568  }
4569  }
4570 
4571  Assert(!aip->wp_list->get_waypoints().empty()); // What? Is this zero? Probably never got initialized!
4572 
4573  bool done, treat_as_ship;
4574  ai_fly_to_target_position(aip->wp_list->get_waypoints()[aip->wp_index].get_pos(), &done, &treat_as_ship);
4575 
4576  if ( done ) {
4577  // go on to next waypoint in path
4578  ++aip->wp_index;
4579 
4580  if ( aip->wp_index == aip->wp_list->get_waypoints().size() ) {
4581  // have reached the last waypoint. Do I repeat?
4582  if ( aip->wp_flags & WPF_REPEAT ) {
4583  // go back to the start.
4584  aip->wp_index = 0;
4585  } else {
4586  // stay on the last waypoint.
4587  --aip->wp_index;
4588 
4589  // Log a message that the wing or ship reached his waypoint and
4590  // remove the goal from the AI goals of the ship pr wing, respectively.
4591  // Whether we should treat this as a ship or a wing is determined by
4592  // ai_fly_to_target_position when it marks the AI directive as complete
4593  if ( treat_as_ship ) {
4594  ai_mission_goal_complete( aip ); // this call should reset the AI mode
4595  mission_log_add_entry( LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, aip->wp_list->get_name(), -1 );
4596  } else {
4597  ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4598  mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, aip->wp_list->get_name(), -1 );
4599  }
4600  }
4601  }
4602  }
4603 }
4604 
4605 // --------------------------------------------------------------------------
4606 // make Pl_objp fly toward a ship
4608 {
4609  ai_info *aip;
4610  object* target_p = NULL;
4611 
4612  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4613 
4614  if ( aip->mode != AIM_FLY_TO_SHIP ) {
4615  Warning(LOCATION,
4616  "ai_fly_to_ship called for '%s' when ai_info.mode not equal to AIM_FLY_TO_SHIP. Is actually '%d'",
4617  Ships[Pl_objp->instance].ship_name,
4618  aip->mode);
4619  aip->mode = AIM_NONE;
4620  return;
4621  }
4622  if ( aip->active_goal < 0 || aip->active_goal >= MAX_AI_GOALS ) {
4623  Warning(LOCATION,
4624  "'%s' is trying to fly-to a ship without an active AI_GOAL\n\n"
4625  "Active ai mode is '%d'",
4626  Ships[Pl_objp->instance].ship_name,
4627  aip->active_goal);
4628  aip->mode = AIM_NONE;
4629  return;
4630  }
4631  Assert( aip->goals[aip->active_goal].target_name != NULL );
4632  if ( aip->goals[aip->active_goal].target_name[0] == '\0' ) {
4633  Warning(LOCATION, "'%s' is trying to fly-to-ship without a name for the ship", Ships[Pl_objp->instance].ship_name);
4634  aip->mode = AIM_NONE;
4635  ai_remove_ship_goal( aip, aip->active_goal ); // function sets aip->active_goal to NONE for me
4636  return;
4637  }
4638 
4639  for (int j = 0; j < MAX_SHIPS; j++)
4640  {
4641  if (Ships[j].objnum != -1 && !stricmp(aip->goals[aip->active_goal].target_name, Ships[j].ship_name))
4642  {
4643  target_p = &Objects[Ships[j].objnum];
4644  break;
4645  }
4646  }
4647 
4648  if ( target_p == NULL ) {
4649  #ifndef NDEBUG
4650  for (int i = 0; i < MAX_AI_GOALS; i++)
4651  {
4652  if (aip->mode == AIM_FLY_TO_SHIP || aip->goals[i].ai_mode == AI_GOAL_FLY_TO_SHIP)
4653  {
4654  mprintf(("Ship '%s' told to fly to target ship '%s'",
4655  Ships[Pl_objp->instance].ship_name,
4656  aip->goals[i].target_name));
4657  }
4658  }
4659  #endif
4660  Warning(LOCATION, "Ship '%s' told to fly to a ship but none of the ships it was told to fly to exist.\n"
4661  "See log before this message for list of ships set as fly-to tagets",
4662  Ships[Pl_objp->instance].ship_name);
4663  aip->mode = AIM_NONE;
4664  ai_remove_ship_goal( aip, aip->active_goal ); // function sets aip->active_goal to NONE for me
4665  return;
4666  } else {
4667  bool done, treat_as_ship;
4668  ai_fly_to_target_position(&(target_p->pos), &done, &treat_as_ship);
4669 
4670  if ( done ) {
4671  // remove the goal from the AI goals of the ship pr wing, respectively.
4672  // Wether or not we should treat this as a ship or a wing is determined by
4673  // ai_fly_to_target when it marks the AI directive as complete
4674  if ( treat_as_ship ) {
4675  ai_mission_goal_complete( aip ); // this call should reset the AI mode
4676  } else {
4677  ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4678  }
4679  }
4680  }
4681 }
4682 
4683 // Make Pl_objp avoid En_objp
4684 // Not like evading. This is for avoiding a collision!
4685 // Note, use sliding if available.
4687 {
4688  // To avoid an object, turn towards right or left vector until facing away from object.
4689  // To choose right vs. left, pick one that is further from center of avoid object.
4690  // Keep turning away from until pointing away from ship.
4691  // Stay in avoid mode until at least 3 enemy ship radii away.
4692 
4693  // Speed setting:
4694  // If inside sphere, zero speed and turn towards outside.
4695  // If outside sphere, inside 2x sphere, set speed percent of max to:
4696  // max(away_dot, (dist-rad)/rad)
4697  // where away_dot is dot(Pl_objp->fvec, vec_En_objp_to_Pl_objp)
4698 
4699  vec3d vec_to_enemy;
4700  float away_dot;
4701  float dist;
4702  ship *shipp = &Ships[Pl_objp->instance];
4703  ship_info *sip = &Ship_info[shipp->ship_info_index];
4704  ai_info *aip = &Ai_info[shipp->ai_index];
4705  vec3d player_pos, enemy_pos;
4706 
4707  // if we're avoiding a stealth ship, then we know where he is, update with no error
4708  if ( is_object_stealth_ship(En_objp) ) {
4710  }
4711 
4712  ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4713  vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4714 
4715  dist = vm_vec_normalize(&vec_to_enemy);
4716  away_dot = -vm_vec_dot(&Pl_objp->orient.vec.fvec, &vec_to_enemy);
4717 
4718  if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4719  if (vm_vec_dot(&Pl_objp->orient.vec.rvec, &vec_to_enemy) > 0.0f) {
4720  AI_ci.sideways = -1.0f;
4721  } else {
4722  AI_ci.sideways = 1.0f;
4723  }
4724  if (vm_vec_dot(&Pl_objp->orient.vec.uvec, &vec_to_enemy) > 0.0f) {
4725  AI_ci.vertical = -1.0f;
4726  } else {
4727  AI_ci.vertical = 1.0f;
4728  }
4729  }
4730 
4731  // If in front of enemy, turn away from it.
4732  // If behind enemy, try to get fully behind it.
4733  if (away_dot < 0.0f) {
4734  turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4735  } else {
4736  vec3d goal_pos;
4737 
4738  vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.vec.fvec, -100.0f);
4739  turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4740  }
4741 
4742  // Set speed.
4743  float radsum = Pl_objp->radius + En_objp->radius;
4744 
4745  if (dist < radsum)
4746  accelerate_ship(aip, MAX(away_dot, 0.2f));
4747  else if (dist < 2*radsum)
4748  accelerate_ship(aip, MAX(away_dot, (dist - radsum) / radsum)+0.2f);
4749  else
4750  accelerate_ship(aip, 1.0f);
4751 
4752 }
4753 
4754 // Maybe it's time to resume the previous AI mode in aip->previous_mode.
4755 // Each type of previous_mode has its own criteria on when to resume.
4756 // Return true if previous mode was resumed.
4758 {
4759  // Only (maybe) resume previous goal if current goal is dynamic.
4760  if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4761  return 0;
4762 
4763  if (aip->mode == AIM_EVADE_WEAPON) {
4764  if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4766  aip->mode = aip->previous_mode;
4767  aip->submode = aip->previous_submode;
4769  aip->active_goal = AI_GOAL_NONE;
4770  aip->mode_time = -1; // Means do forever.
4771  return 1;
4772  }
4773  } else if ( aip->previous_mode == AIM_GUARD) {
4774  if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4775  object *guard_objp;
4776  float dist;
4777 
4778  guard_objp = &Objects[aip->guard_objnum];
4779  dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4780 
4781  // If guarding ship is far away from guardee and enemy is far away from guardee,
4782  // then stop chasing and resume guarding.
4783  if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4784  if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4785  if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4786  Assert(aip->previous_mode == AIM_GUARD);
4787  aip->mode = aip->previous_mode;
4788  aip->submode = AIS_GUARD_PATROL;
4790  aip->active_goal = AI_GOAL_NONE;
4791  return 1;
4792  }
4793  }
4794  }
4795  }
4796  }
4797 
4798  return 0;
4799 
4800 }
4801 
4802 // Call this function if you want something to happen on average every N quarters of a second.
4803 // The truth value returned by this function will be the same for any given quarter second interval.
4804 // The value "num" is only passed in to get asynchronous behavior for different objects.
4805 // modulus == 1 will always return true.
4806 // modulus == 2 will return true half the time.
4807 // modulus == 16 will return true for one quarter second interval every four seconds.
4808 int static_rand_timed(int num, int modulus)
4809 {
4810  if (modulus < 2)
4811  return 1;
4812  else {
4813  int t;
4814 
4815  t = Missiontime >> 18; // Get time in quarters of a second (SUSHI: this comment is wrong! It's actually every 4 seconds!)
4816  t += num;
4817 
4818  return !(t % modulus);
4819  }
4820 }
4821 
4826 {
4827  // bail if the ship doesn't even have an afterburner
4828  if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_AFTERBURNER)) {
4829  return 0;
4830  }
4831  if (aip->ai_aburn_use_factor == INT_MIN && aip->ai_class == 0) {
4832  return 0; // Lowest level never aburners away (unless ai_aburn_use_factor is specified)
4833  }
4834  else {
4835  // Maybe don't afterburner because of a potential collision with the player.
4836  // If not multiplayer, near player and player in front, probably don't afterburner.
4837  if (!(Game_mode & GM_MULTIPLAYER)) {
4838  if (Ships[objp->instance].team == Player_ship->team) {
4839  float dist;
4840 
4841  dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
4842  if (dist < 150.0f) {
4843  vec3d v2p;
4844  float dot;
4845 
4846  vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
4847  dot = vm_vec_dot(&v2p, &objp->orient.vec.fvec);
4848 
4849  if (dot > 0.0f) {
4850  if (dot * dist > 50.0f)
4851  return 0;
4852  }
4853  }
4854  }
4855  }
4856 
4857  if (aip->ai_aburn_use_factor == INT_MIN && aip->ai_class >= Num_ai_classes-2)
4858  return 1; // Highest two levels always aburner away (unless ai_aburn_use_factor is specified).
4859  else {
4860  //If ai_aburn_use_factor is not specified, calculate a number based on the AI class. Otherwise, use that value.
4861  if (aip->ai_aburn_use_factor == INT_MIN)
4862  return static_rand_timed(OBJ_INDEX(objp), Num_ai_classes - aip->ai_class);
4863  else
4864  return static_rand_timed(OBJ_INDEX(objp), aip->ai_aburn_use_factor);
4865  }
4866  }
4867 }
4868 
4872 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
4873 {
4874  // Only do if facing a little away.
4875  if (en_objp != NULL) {
4876  vec3d v2e;
4877 
4878  vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
4879  if (vm_vec_dot(&v2e, &objp->orient.vec.fvec) > -0.5f)
4880  return;
4881  }
4882 
4883  if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
4884  if (ai_maybe_fire_afterburner(objp, aip)) {
4885  afterburners_start(objp);
4887  }
4888  }
4889 }
4890 
4895 int is_instructor(object *objp)
4896 {
4898 }
4899 
4900 // Evade the weapon aip->danger_weapon_objnum
4901 // If it's not valid, do a quick out.
4902 // Evade by accelerating hard.
4903 // If necessary, turn hard left or hard right.
4905 {
4906  object *weapon_objp = NULL;
4907  object *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
4908  vec3d weapon_pos, player_pos, goal_point;
4909  vec3d vec_from_enemy;
4910  float dot_from_enemy, dot_to_enemy;
4911  float dist;
4912  ship *shipp = &Ships[Pl_objp->instance];
4913  ai_info *aip = &Ai_info[shipp->ai_index];
4914 
4915  if (is_instructor(Pl_objp))
4916  return;
4917 
4918  // Make sure we're actually being attacked.
4919  // Favor locked objects.
4920  if (aip->nearest_locked_object != -1) {
4922  locked_weapon_objp = &Objects[aip->nearest_locked_object];
4923  }
4924 
4925  if (aip->danger_weapon_objnum != -1) {
4927  unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
4928  else
4929  aip->danger_weapon_objnum = -1; // Signatures don't match, so no longer endangered.
4930  }
4931 
4932  if (locked_weapon_objp != NULL) {
4933  if (unlocked_weapon_objp != NULL) {
4934  if (vm_vec_dist_quick(&locked_weapon_objp->pos, &Pl_objp->pos) < 1.5f * vm_vec_dist_quick(&unlocked_weapon_objp->pos, &Pl_objp->pos))
4935  weapon_objp = locked_weapon_objp;
4936  else
4937  weapon_objp = unlocked_weapon_objp;
4938  } else
4939  weapon_objp = locked_weapon_objp;
4940  } else if (unlocked_weapon_objp != NULL)
4941  weapon_objp = unlocked_weapon_objp;
4942  else {
4943  if (aip->mode == AIM_EVADE_WEAPON)
4944  maybe_resume_previous_mode(Pl_objp, aip);
4945  return;
4946  }
4947 
4948  Assert(weapon_objp != NULL);
4949 
4950  if (weapon_objp->type != OBJ_WEAPON) {
4951  if (aip->mode == AIM_EVADE_WEAPON)
4952  maybe_resume_previous_mode(Pl_objp, aip);
4953  return;
4954  }
4955 
4956  weapon_pos = weapon_objp->pos;
4957  player_pos = Pl_objp->pos;
4958 
4959  // Make speed based on skill level, varying at highest skill level, which is harder to hit.
4960  accelerate_ship(aip, 1.0f);
4961 
4962  dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
4963 
4964  dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.vec.fvec, &vec_from_enemy);
4965  dot_from_enemy = vm_vec_dot(&weapon_objp->orient.vec.fvec, &vec_from_enemy);
4966 
4967  // If shot is incoming...
4968  if (dot_from_enemy < 0.3f) {
4969  if (weapon_objp == unlocked_weapon_objp)
4970  aip->danger_weapon_objnum = -1;
4971  return;
4972  } else if (dot_from_enemy > 0.7f) {
4973  if (dist < 200.0f) {
4974  if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
4975  if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
4976  afterburners_start(Pl_objp);
4978  }
4979  }
4980  }
4981 
4982  // If we're sort of pointing towards it...
4983  if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
4984  float rdot;
4985  float udot;
4986 
4987  // Turn hard left or right, depending on which gets out of way quicker.
4988  //SUSHI: Also possibly turn up or down.
4989  rdot = vm_vec_dot(&Pl_objp->orient.vec.rvec, &vec_from_enemy);
4990  udot = vm_vec_dot(&Pl_objp->orient.vec.uvec, &vec_from_enemy);
4991 
4992  if (aip->ai_profile_flags & AIPF_ALLOW_VERTICAL_DODGE && fl_abs(udot) > fl_abs(rdot))
4993  {
4994  if ((udot < -0.5f) || (udot > 0.5f))
4995  vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.uvec, -200.0f);
4996  else
4997  vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.uvec, 200.0f);
4998  }
4999  else
5000  {
5001  if ((rdot < -0.5f) || (rdot > 0.5f))
5002  vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, -200.0f);
5003  else
5004  vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, 200.0f);
5005  }
5006 
5007  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5008  }
5009  }
5010 
5011 }
5012 
5013 // Use sliding and backwards moving to face enemy.
5014 // (Coded 2/20/98. Works fine, but it's hard to see how to integrate it into the AI system.
5015 // Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5016 // It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5017 // would be frustrating, I think.
5018 // This function is currently not called.)
5020 {
5021  ship_info *sip;
5022 
5023  sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5024 
5025  // If can't slide, return.
5026  if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5027  return;
5028 
5029  vec3d goal_pos;
5030  float dot_from_enemy;
5031  vec3d vec_from_enemy, vec_to_goal;
5032  float dist;
5033  float up, right;
5034  ai_info *aip;
5035 
5036  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5037 
5038  dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5039 
5040  ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5041 
5042  dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.vec.fvec);
5043 
5044  if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.vec.rvec) > 0.0f)
5045  right = 1.0f;
5046  else
5047  right = -1.0f;
5048 
5049  if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.vec.uvec) > 0.0f)
5050  up = 1.0f;
5051  else
5052  up = -1.0f;
5053 
5054  vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.vec.rvec, right * 200.0f);
5055  vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.vec.uvec, up * 200.0f);
5056 
5057  vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5058 
5059  if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.vec.rvec) > 0.0f)
5060  AI_ci.sideways = 1.0f;
5061  else
5062  AI_ci.sideways = -1.0f;
5063 
5064  if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.vec.uvec) > 0.0f)
5065  AI_ci.vertical = 1.0f;
5066  else
5067  AI_ci.vertical = -1.0f;
5068 
5069  if (dist < 200.0f) {
5070  if (dot_from_enemy < 0.7f)
5071  accelerate_ship(aip, -1.0f);
5072  else
5073  accelerate_ship(aip, dot_from_enemy + 0.5f);
5074  } else {
5075  if (dot_from_enemy < 0.7f) {
5076  accelerate_ship(aip, 0.2f);
5077  } else {
5078  accelerate_ship(aip, 1.0f);
5079  }
5080  }
5081 }
5082 
5083 // General code for handling one ship evading another.
5084 // Problem: This code is also used for avoiding an impending collision.
5085 // In such a case, it is not good to go to max speed, which is often good
5086 // for a certain kind of evasion.
5088 {
5089  vec3d player_pos, enemy_pos, goal_point;
5090  vec3d vec_from_enemy;
5091  float dot_from_enemy;
5092  float dist;
5093  ship *shipp = &Ships[Pl_objp->instance];
5094  ship_info *sip = &Ship_info[shipp->ship_info_index];
5095  ai_info *aip = &Ai_info[shipp->ai_index];
5096  float bank_override = 0.0f;
5097 
5098  ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5099 
5100  // Make speed based on skill level, varying at highest skill level, which is harder to hit.
5102  int rand_int;
5103  float accel_val;
5104 
5105  rand_int = static_rand(OBJ_INDEX(Pl_objp));
5106  accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5107  accelerate_ship(aip, accel_val);
5108  } else
5109  accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5110 
5111  if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5112  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5113  if (percent_left > 30.0f + ((OBJ_INDEX(Pl_objp)) & 0x0f)) {
5114  afterburners_start(Pl_objp);
5115 
5117  aip->afterburner_stop_time = (fix) (Missiontime + F1_0 + static_randf(OBJ_INDEX(Pl_objp)) * F1_0 / 4);
5118  } else {
5120  }
5121  }
5122  }
5123 
5124  vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5125 
5126  dist = vm_vec_normalize(&vec_from_enemy);
5127  dot_from_enemy = vm_vec_dot(&En_objp->orient.vec.fvec, &vec_from_enemy);
5128 
5129  if (dist > 250.0f) {
5130  vec3d gp1, gp2;
5131  // If far away from enemy, circle, going to nearer of point far off left or right wing
5132  vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.vec.rvec, 250.0f);
5133  vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.vec.rvec, -250.0f);
5134  if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5135  goal_point = gp1;
5136  else
5137  goal_point = gp2;
5138  } else if (dot_from_enemy < 0.1f) {
5139  // If already close to behind, goal is to get completely behind.
5140  vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.vec.fvec, -1000.0f);
5141  } else if (dot_from_enemy > 0.9f) {
5142  // If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5143  vec3d vec_to_enemy;
5144  float dot_to_enemy;
5145 
5146  vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5147 
5148  vm_vec_normalize(&vec_to_enemy);
5149  dot_to_enemy = vm_vec_dot(&Pl_objp->orient.vec.fvec, &vec_to_enemy);
5150  if (dot_to_enemy > 0.75f) {
5151  // Used to go to En_objp's right vector, but due to banking while turning, that
5152  // caused flying in an odd spiral.
5153  vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.vec.rvec, 1000.0f);
5154  if (dist < 100.0f)
5155  bank_override = Pl_objp->phys_info.speed;
5156  } else {
5157  bank_override = Pl_objp->phys_info.speed; // In enemy's sights, not pointing at him, twirl away.
5158  goto evade_ship_l1;
5159  }
5160  } else {
5161 evade_ship_l1: ;
5162  if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5163  int temp;
5164  float scale;
5165  float psrandval; // some value close to zero to choose whether to turn right or left.
5166 
5167  psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); // Value between -8 and 7
5168  psrandval = psrandval/16.0f; // Value between -1/2 and 1/2 (approx)
5169 
5170  // If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5171  if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.vec.rvec) > psrandval) {
5172  scale = 1000.0f;
5173  } else {
5174  scale = -1000.0f;
5175  }
5176 
5177  vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.vec.rvec, scale);
5178 
5179  temp = ((Missiontime >> 16) & 0x07);
5180  temp = ((temp * (temp+1)) % 16)/2 - 4;
5181  if ((psrandval == 0) && (temp == 0))
5182  temp = 3;
5183 
5184  scale = 200.0f * temp;
5185 
5186  vm_vec_scale_add2(&goal_point, &En_objp->orient.vec.uvec, scale);
5187  } else {
5188  // No evasion this frame, but continue with previous turn.
5189  // Reason: If you don't, you lose rotational momentum. Turning every other frame,