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,
5190  // and not in between results in a very slow turn because of loss of momentum.
5191  if ((aip->prev_goal_point.xyz.x != 0.0f) || (aip->prev_goal_point.xyz.y != 0.0f) || (aip->prev_goal_point.xyz.z != 0.0f))
5192  goal_point = aip->prev_goal_point;
5193  else
5194  vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.vec.rvec, 100.0f);
5195  }
5196  }
5197 
5198  turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5199 
5200  aip->prev_goal_point = goal_point;
5201 }
5202 
5203 // --------------------------------------------------------------------------
5204 // Fly in a manner making it difficult for opponent to attack.
5205 void ai_evade()
5206 {
5207  evade_ship();
5208 }
5209 
5210 
5213 
5214 
5215 //old version of this fuction, this will be useful for playing old missions and not having the new primary
5216 //selection code throw off the balance of the mission.
5217 // If:
5218 // flags & WIF_PUNCTURE
5219 // Then Select a Puncture weapon.
5220 // Else
5221 // Select Any ol' weapon.
5222 // Returns primary_bank index.
5223 int ai_select_primary_weapon_OLD(object *objp, object *other_objp, int flags)
5224 {
5225  ship *shipp = &Ships[objp->instance];
5226  ship_weapon *swp = &shipp->weapons;
5227 
5228  Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < static_cast<int>(Ship_info.size()));
5229 
5230  if (flags & WIF_PUNCTURE) {
5231  if (swp->current_primary_bank >= 0) {
5232  int bank_index;
5233 
5234  bank_index = swp->current_primary_bank;
5235 
5236  if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5237  return swp->current_primary_bank;
5238  }
5239  }
5240  for (int i=0; i<swp->num_primary_banks; i++) {
5241  int weapon_info_index;
5242 
5243  weapon_info_index = swp->primary_bank_weapons[i];
5244 
5245  if (weapon_info_index > -1){
5246  if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5247  swp->current_primary_bank = i;
5248  return i;
5249  }
5250  }
5251  }
5252 
5253  // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5254  if ( swp->current_primary_bank < 0 ) {
5255  if ( swp->num_primary_banks > 0 ) {
5256  swp->current_primary_bank = 0;
5257  }
5258  }
5259 
5260  } else { // Don't need to be using a puncture weapon.
5261  if (swp->current_primary_bank >= 0) {
5262  if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5263  return swp->current_primary_bank;
5264  }
5265  }
5266  for (int i=0; i<swp->num_primary_banks; i++) {
5267  if (swp->primary_bank_weapons[i] > -1) {
5268  if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5269  swp->current_primary_bank = i;
5270  nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5271  return i;
5272  }
5273  }
5274  }
5275  // Wasn't able to find a non-puncture weapon. Stick with what we have.
5276  }
5277 
5278  Assert( swp->current_primary_bank != -1 ); // get Alan or Allender
5279 
5280  return swp->current_primary_bank;
5281 }
5282 
5283 // If:
5284 // flags & WIF_PUNCTURE
5285 // Then Select a Puncture weapon.
5286 // Else
5287 // Select Any ol' weapon.
5288 // Returns primary_bank index.
5296 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5297 {
5298  // Pointer Set Up
5299  ship *shipp = &Ships[objp->instance];
5300  ship_weapon *swp = &shipp->weapons;
5301 
5302  // Debugging
5303  if (other_objp==NULL)
5304  {
5305  // this can be NULL in the case of a target death and attacker weapon
5306  // change. using notification message instead of a fault
5307  mprintf(("'other_objpp == NULL' in ai_select_primary_weapon()\n"));
5308  return -1;
5309  }
5310 
5311  //not using the new AI, use the old version of this function instead.
5313  {
5314  return ai_select_primary_weapon_OLD(objp, other_objp, flags);
5315  }
5316 
5317  Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < static_cast<int>(Ship_info.size()));
5318 
5319  //made it so it only selects puncture weapons if the active goal is to disable something -Bobboau
5320  if ((flags & WIF_PUNCTURE) && (Ai_info[shipp->ai_index].goals[0].ai_mode & (AI_GOAL_DISARM_SHIP | AI_GOAL_DISABLE_SHIP)))
5321  {
5322  if (swp->current_primary_bank >= 0)
5323  {
5324  int bank_index;
5325 
5326  bank_index = swp->current_primary_bank;
5327 
5328  if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE)
5329  {
5330  return swp->current_primary_bank;
5331  }
5332  }
5333  for (int i=0; i<swp->num_primary_banks; i++)
5334  {
5335  int weapon_info_index;
5336 
5337  weapon_info_index = swp->primary_bank_weapons[i];
5338 
5339  if (weapon_info_index > -1)
5340  {
5341  if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE)
5342  {
5343  swp->current_primary_bank = i;
5344  return i;
5345  }
5346  }
5347  }
5348  }
5349 
5350  if ( (other_objp->type == OBJ_SHIP) && (Ship_info[Ships[other_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP) ) )
5351  {
5352  //Check if we have a capital+ weapon on board
5353  for (int i = 0; i < swp->num_primary_banks; i++)
5354  {
5355  if (swp->primary_bank_weapons[i] > -1) // Make sure there is a weapon in the bank
5356  {
5358  {
5359  swp->current_primary_bank = i;
5360  nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5361  return i;
5362  }
5363  }
5364  }
5365  }
5366 
5367  float enemy_remaining_shield = get_shield_pct(other_objp);
5368 
5369  // Is the target shielded by say only 5%?
5370  if (enemy_remaining_shield <= 0.05f)
5371  {
5372  // Then it is safe to start using a heavy hull damage weapon such as the maxim
5373  int i;
5374  float i_hullfactor_prev = 0; // Previous weapon bank hull factor (this is the safe way to do it)
5375  int i_hullfactor_prev_bank = -1; // Bank which gave us this hull factor
5376 
5377  // Find the weapon with the highest hull * damage / fire delay factor
5378  for (i = 0; i < swp->num_primary_banks; i++)
5379  {
5380  if (swp->primary_bank_weapons[i] > -1) // Make sure there is a weapon in the bank
5381  {
5382  if ((((Weapon_info[swp->primary_bank_weapons[i]].armor_factor) * (Weapon_info[swp->primary_bank_weapons[i]].damage)) / Weapon_info[swp->primary_bank_weapons[i]].fire_wait) > i_hullfactor_prev)
5383  {
5385  {
5386  // This weapon is the new candidate
5388  i_hullfactor_prev_bank = i;
5389  }
5390  }
5391  }
5392  }
5393  if (i_hullfactor_prev_bank == -1) // In the unlikely instance we don't find at least 1 candidate weapon
5394  {
5395  i_hullfactor_prev_bank = 0; // Just switch to the first one
5396  }
5397  swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
5398  return i_hullfactor_prev_bank; // Return
5399  }
5400 
5401  //if the shields are above lets say 10% definitely use a pierceing weapon if there are any-Bobboau
5402  if (enemy_remaining_shield >= 0.10f)
5403  {
5404  if (swp->current_primary_bank >= 0)
5405  {
5406  int bank_index;
5407 
5408  bank_index = swp->current_primary_bank;
5409 
5411  {
5412  return swp->current_primary_bank;
5413  }
5414  }
5415  for (int i=0; i<swp->num_primary_banks; i++)
5416  {
5417  int weapon_info_index;
5418 
5419  weapon_info_index = swp->primary_bank_weapons[i];
5420 
5421  if (weapon_info_index > -1)
5422  {
5423  if ((Weapon_info[weapon_info_index].wi_flags2 & WIF2_PIERCE_SHIELDS) && !(Weapon_info[swp->primary_bank_weapons[i]].wi_flags2 & WIF2_CAPITAL_PLUS))
5424  {
5425  swp->current_primary_bank = i;
5426  return i;
5427  }
5428  }
5429  }
5430  }
5431 
5432  // Is the target shielded by less then 50%?
5433  if (enemy_remaining_shield <= 0.50f)
5434  {
5435  // Should be using best balanced shield gun
5436  int i;
5437  float i_hullfactor_prev = 0; // Previous weapon bank hull factor (this is the safe way to do it)
5438  int i_hullfactor_prev_bank = -1; // Bank which gave us this hull factor
5439  // Find the weapon with the highest average hull and shield * damage / fire delay factor
5440  for (i = 0; i < swp->num_primary_banks; i++)
5441  {
5442  if (swp->primary_bank_weapons[i] > -1) // Make sure there is a weapon in the bank
5443  {
5445  {
5447  {
5448  // This weapon is the new candidate
5450  i_hullfactor_prev_bank = i;
5451  }
5452  }
5453  }
5454  }
5455  if (i_hullfactor_prev_bank == -1) // In the unlikely instance we don't find at least 1 candidate weapon
5456  {
5457  i_hullfactor_prev_bank = 0; // Just switch to the first one
5458  }
5459  swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
5460  return i_hullfactor_prev_bank; // Return
5461  }
5462  else
5463  {
5464  // Should be using best shield destroying gun
5465  int i;
5466  float i_hullfactor_prev = 0; // Previous weapon bank hull factor (this is the safe way to do it)
5467  int i_hullfactor_prev_bank = -1; // Bank which gave us this hull factor
5468  // Find the weapon with the highest average hull and shield * damage / fire delay factor
5469  for (i = 0; i < swp->num_primary_banks; i++)
5470  {
5471  if (swp->primary_bank_weapons[i] > -1) // Make sure there is a weapon in the bank
5472  {
5474  {
5476  {
5477  // This weapon is the new candidate
5479  i_hullfactor_prev_bank = i;
5480  }
5481  }
5482  }
5483  }
5484  if (i_hullfactor_prev_bank == -1) // In the unlikely instance we don't find at least 1 candidate weapon
5485  {
5486  i_hullfactor_prev_bank = 0; // Just switch to the first one
5487  }
5488  swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
5489  return i_hullfactor_prev_bank; // Return
5490  }
5491 }
5492 
5497 {
5498  ship *shipp;
5499  ship_info *sip;
5500  ai_info *aip;
5501  ship_weapon *swp;
5502  weapon_info *wip;
5503 
5504  int total_ammo;
5505  int current_ammo;
5506  float ammo_pct;
5507  int i;
5508 
5509  shipp = &Ships[objp->instance];
5510  sip = &Ship_info[shipp->ship_info_index];
5511  aip = &Ai_info[shipp->ai_index];
5512  swp = &shipp->weapons;
5513 
5514  shipp->flags &= ~SF_PRIMARY_LINKED;
5515 
5516  // AL: ensure target is a ship!
5517  if ( (aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP) ) {
5518  // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5520  if ( aip->targeted_subsys == NULL ) {
5521  shipp->flags |= SF_PRIMARY_LINKED;
5522  shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5523  return;
5524  }
5525  }
5526  }
5527 
5528  if (Num_weapons > (int) (MAX_WEAPONS * 0.75f) || sip->flags2 & SIF2_NO_PRIMARY_LINKING) {
5529  if (shipp->flags & SF_PRIMARY_LINKED)
5530  nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5531  shipp->flags &= ~SF_PRIMARY_LINKED;
5532  return; // If low on slots or primary linking disallowed, don't link.
5533  }
5534 
5535  // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5536  // puncture weapons
5537  if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) )
5538  {
5540  {
5541  // only continue if both primaries are puncture weapons
5542  if ( swp->num_primary_banks == 2 ) {
5544  return;
5545  if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) )
5546  return;
5547  }
5548  }
5549  }
5550 
5551  // Don't want all ships always linking weapons at start, so asynchronize.
5553  {
5554  if (Missiontime < i2f(30))
5555  return;
5556  else if (Missiontime < i2f(120))
5557  {
5558  int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5559  if ( (r&3) != 0)
5560  return;
5561  }
5562  }
5563 
5564  // get energy level
5565  float energy;
5567  energy = shipp->weapon_energy / sip->max_weapon_reserve * 100.0f;
5568  } else {
5569  energy = shipp->weapon_energy;
5570  }
5571 
5572  // make linking decision based on weapon energy
5573  if (energy > aip->ai_link_energy_levels_always) {
5574  shipp->flags |= SF_PRIMARY_LINKED;
5575  } else if (energy > aip->ai_link_ammo_levels_maybe) {
5576  if (objp->hull_strength < shipp->ship_max_hull_strength/3.0f) {
5577  shipp->flags |= SF_PRIMARY_LINKED;
5578  }
5579  }
5580 
5581  // also check ballistics - Goober5000
5582  if (sip->flags & SIF_BALLISTIC_PRIMARIES)
5583  {
5584  total_ammo = 0;
5585  current_ammo = 0;
5586 
5587  // count ammo, and do not continue unless all weapons are ballistic
5588  for (i = 0; i < swp->num_primary_banks; i++)
5589  {
5590  wip = &Weapon_info[swp->primary_bank_weapons[i]];
5591 
5592  if (wip->wi_flags2 & WIF2_BALLISTIC)
5593  {
5594  total_ammo += swp->primary_bank_start_ammo[i];
5595  current_ammo += swp->primary_bank_ammo[i];
5596  }
5597  else
5598  {
5599  return;
5600  }
5601  }
5602 
5603  Assert(total_ammo); // Goober5000: div-0 check
5604  ammo_pct = float (current_ammo) / float (total_ammo) * 100.0f;
5605 
5606  // link according to defined levels
5607  if (ammo_pct > aip->ai_link_ammo_levels_always)
5608  {
5609  shipp->flags |= SF_PRIMARY_LINKED;
5610  }
5611  else if (ammo_pct > aip->ai_link_ammo_levels_maybe)
5612  {
5613  if (objp->hull_strength < shipp->ship_max_hull_strength/3.0f)
5614  {
5615  shipp->flags |= SF_PRIMARY_LINKED;
5616  }
5617  }
5618  }
5619 }
5620 
5621 // --------------------------------------------------------------------------
5622 // Fire the current primary weapon.
5623 // *objp is the object to fire from.
5624 //I changed this to return a true false flag on weather
5625 //it did or did not fire the weapon sorry if this screws you up-Bobboau
5627 {
5628  ship *shipp = &Ships[objp->instance];
5629  ship_weapon *swp = &shipp->weapons;
5630  ship_info *enemy_sip;
5631  ai_info *aip;
5632  object *enemy_objp;
5633 
5634  Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < static_cast<int>(Ship_info.size()));
5635 
5636  aip = &Ai_info[shipp->ai_index];
5637 
5638  // If low on slots, fire a little less often.
5639  if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5640  if (frand() > 0.5f) {
5641  nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5642  return 0;
5643  }
5644  }
5645 
5646  if (!Ai_firing_enabled){
5647  return 0;
5648  }
5649 
5650  if (aip->target_objnum != -1){
5651  enemy_objp = &Objects[aip->target_objnum];
5652  enemy_sip = (enemy_objp->type == OBJ_SHIP) ? &Ship_info[Ships[enemy_objp->instance].ship_info_index] : NULL;
5653  } else {
5654  enemy_objp = NULL;
5655  enemy_sip = NULL;
5656  }
5657 
5659  int flags = 0;
5660  if ( aip->targeted_subsys != NULL ) {
5661  flags = WIF_PUNCTURE;
5662  }
5663  ai_select_primary_weapon(objp, enemy_objp, flags);
5664  ship_primary_changed(shipp); // AL: maybe send multiplayer information when AI ship changes primaries
5665  aip->primary_select_timestamp = timestamp(5 * 1000); // Maybe change primary weapon five seconds from now.
5666  }
5667 
5668  // If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5669  float dot;
5670  vec3d v2t;
5671 
5672  if ( !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE ) && !(aip->submode == SM_AVOID) ) {
5673  if ( vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5674  vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5675  dot = vm_vec_dot(&v2t, &objp->orient.vec.fvec);
5676  if (dot > .998629534f){ // if within 3.0 degrees of desired heading, bash
5677  vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.vec.uvec, NULL);
5678  }
5679  }
5680  }
5681 
5682  //SUSHI: Burst-fire for ballistic primaries.
5683  if (The_mission.ai_profile->primary_ammo_burst_mult[Game_skill_level] > 0 && //Make sure we are using burst fire
5684  enemy_objp != NULL && enemy_sip != NULL && //We need a target, obviously
5685  (enemy_objp->phys_info.speed >= 1.0f) && //Only burst for moving ships
5686  (enemy_sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)) && //Only burst for small ships (transports count)
5687  swp->primary_bank_start_ammo[swp->current_primary_bank] > 0 && //Prevent div by 0
5688  Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags2 & WIF2_BALLISTIC) //Current weapon must be ballistic
5689  {
5690  float percentAmmoLeft = ((float)swp->primary_bank_ammo[swp->current_primary_bank] / (float)swp->primary_bank_start_ammo[swp->current_primary_bank]);
5691  float distToTarget = vm_vec_dist(&enemy_objp->pos, &objp->pos);
5692  float weaponRange = Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].weapon_range;
5693  float distanceFactor = 1.0f - distToTarget/weaponRange;
5694  vec3d vecToTarget;
5695  vm_vec_normalized_dir(&vecToTarget, &enemy_objp->pos, &objp->pos);
5696  float dotToTarget = vm_vec_dot(&vecToTarget, &objp->orient.vec.fvec);
5697  dotToTarget = pow(dotToTarget, 4); //This makes the dot a tiny bit more impactful (otherwise nearly always over 0.98 or so)
5698  float fof_spread_cooldown_factor = 1.0f - swp->primary_bank_fof_cooldown[swp->current_primary_bank];
5699 
5700  //Combine factors
5701  float burstFireProb = ((0.6f * percentAmmoLeft) + (0.4f * distanceFactor)) * dotToTarget * aip->ai_primary_ammo_burst_mult * fof_spread_cooldown_factor;
5702 
5703  //Possibly change values every half-second
5704  if (static_randf((Missiontime + static_rand(aip->shipnum)) >> 15) > burstFireProb)
5705  return 0;
5706  }
5707 
5708  // Make sure not firing at a protected ship unless firing at a live subsystem.
5709  // Note: This happens every time the ship tries to fire, perhaps every frame.
5710  // Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5711  // by multiple banks it can fire from.
5712  if (aip->target_objnum != -1) {
5713  object *tobjp = &Objects[aip->target_objnum];
5714  if (tobjp->flags & OF_PROTECTED) {
5715  if (aip->targeted_subsys != NULL) {
5716  int type;
5717 
5718  type = aip->targeted_subsys->system_info->type;
5719  if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5720  aip->target_objnum = -1;
5721  return 0;
5722  }
5723  } else {
5724  aip->target_objnum = -1;
5725  return 0;
5726  }
5727  }
5728  }
5729 
5730  // If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5731  if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5732  // AL: 3-6-98: Check if current_primary_bank is valid
5733  if ((enemy_objp->hull_strength < 750.0f) &&
5734  ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5735  (swp->current_primary_bank >= 0) ) {
5738  return 0;
5739  }
5740  }
5741  }
5742 
5744 
5745  // I think this will properly solve the problem
5746  // fire non-streaming weapons
5747  ship_fire_primary(objp, 0);
5748 
5749  // fire streaming weapons
5750  shipp->flags |= SF_TRIGGER_DOWN;
5751  ship_fire_primary(objp, 1);
5752  shipp->flags &= ~SF_TRIGGER_DOWN;
5753  return 1;//if it got down to here then it tryed to fire
5754 }
5755 
5756 // --------------------------------------------------------------------------
5757 // Return number of nearby enemy fighters.
5758 // threshold is the distance within which a ship is considered near.
5759 //
5760 // input: enemy_team_mask => teams that are considered as an enemy
5761 // pos => world position to measure ship distances from
5762 // threshold => max distance from pos to be considered "near"
5763 //
5764 // exit: number of ships within threshold units of pos
5765 int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold)
5766 {
5767  ship_obj *so;
5768  object *ship_objp;
5769  int count = 0;
5770 
5771  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5772 
5773  ship_objp = &Objects[so->objnum];
5774 
5775  if (iff_matches_mask(Ships[ship_objp->instance].team, enemy_team_mask)) {
5776  if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5777  if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5778  count++;
5779  }
5780  }
5781  }
5782 
5783  return count;
5784 }
5785 
5786 // --------------------------------------------------------------------------
5787 // Select secondary weapon to fire.
5788 // Currently, 1/16/98:
5789 // If 0 secondary weapons available, return -1
5790 // If 1 available, use it.
5791 // If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5792 // priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT. If any weapon has any bit in priority1
5793 // set, that weapon will be selected. If not, apply to priority2. If neither, return -1, meaning no weapon selected.
5794 // Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5795 // Return value:
5796 // bank index
5797 // Should do this:
5798 // Favor aspect seekers when attacking small ships faraway.
5799 // Favor rapid fire dumbfire when attacking a large ship.
5800 // Ignore heat seekers because we're not sure how they'll work.
5801 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1, int wif2_priority1 = -1, int wif2_priority2 = -1)
5802 {
5803  int num_weapon_types;
5804  int weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5805  int i;
5806  int ignore_mask, ignore_mask_without_huge;
5807  int initial_bank;
5808  ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5809 
5810  initial_bank = swp->current_secondary_bank;
5811 
5812  // set up ignore masks
5813  ignore_mask = 0;
5814  ignore_mask_without_huge = 0;
5815 
5816  // Ignore bombs unless one of the priorities asks for them to be selected.
5817  if (!(WIF_HUGE & (priority1 | priority2))) {
5818  ignore_mask |= WIF_HUGE;
5819  }
5820 
5821  // Ignore capital+ unless one of the priorities asks for it.
5822  if (!(WIF2_CAPITAL_PLUS & (wif2_priority1 | wif2_priority2))) {
5823  ignore_mask |= WIF2_CAPITAL_PLUS;
5824  }
5825 
5826  // Ignore bomber+ unless one of the priorities asks for them to be selected
5827  if (!(WIF_BOMBER_PLUS & (priority1 | priority2))) {
5828  ignore_mask |= WIF_BOMBER_PLUS;
5829  ignore_mask_without_huge |= WIF_BOMBER_PLUS;
5830  }
5831 
5832 #ifndef NDEBUG
5833  for (i=0; i<MAX_WEAPON_TYPES; i++) {
5834  weapon_id_list[i] = -1;
5835  weapon_bank_list[i] = -1;
5836  }
5837 #endif
5838 
5839  // Stuff weapon_bank_list with bank index of available weapons.
5840  num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5841 
5842  // Ignore homing weapons if we didn't specify a flag - for priority 1
5843  if ((aip->ai_profile_flags & AIPF_SMART_SECONDARY_WEAPON_SELECTION) && (priority1 == 0)) {
5844  ignore_mask |= WIF_HOMING;
5845  ignore_mask_without_huge |= WIF_HOMING;
5846  }
5847 
5848  int priority2_index = -1;
5849 
5850  for (i=0; i<num_weapon_types; i++) {
5851  int wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5852  int wi_flags2 = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags2;
5853  int ignore_mask_to_use = ((aip->ai_profile_flags & AIPF_SMART_SECONDARY_WEAPON_SELECTION) && (wi_flags & WIF_BOMBER_PLUS)) ? ignore_mask_without_huge : ignore_mask;
5854 
5855  if (!(wi_flags & ignore_mask_to_use)) { // Maybe bombs are illegal.
5856  if (wi_flags & priority1) {
5857  swp->current_secondary_bank = weapon_bank_list[i]; // Found first priority, return it.
5858  break;
5859  } else if (wi_flags & priority2)
5860  priority2_index = weapon_bank_list[i]; // Found second priority, but might still find first priority.
5861  }
5862  if (!(wi_flags2 & ignore_mask_to_use)) { // Maybe bombs are illegal.
5863  if (wi_flags2 & wif2_priority1) {
5864  swp->current_secondary_bank = weapon_bank_list[i]; // Found first priority, return it.
5865  break;
5866  } else if (wi_flags2 & wif2_priority2)
5867  priority2_index = weapon_bank_list[i]; // Found second priority, but might still find first priority.
5868  }
5869  }
5870 
5871  // Ignore homing weapons if we didn't specify a flag - for priority 2
5872  if ((aip->ai_profile_flags & AIPF_SMART_SECONDARY_WEAPON_SELECTION) && (priority2 == 0)) {
5873  ignore_mask |= WIF_HOMING;
5874  ignore_mask_without_huge |= WIF_HOMING;
5875  }
5876 
5877  // If didn't find anything above, then pick any secondary weapon.
5878  if (i == num_weapon_types) {
5879  swp->current_secondary_bank = priority2_index; // Assume we won't find anything.
5880  if (priority2_index == -1) {
5881  for (i=0; i<num_weapon_types; i++) {
5882  int wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5883  int ignore_mask_to_use = ((aip->ai_profile_flags & AIPF_SMART_SECONDARY_WEAPON_SELECTION) && (wi_flags & WIF_BOMBER_PLUS)) ? ignore_mask_without_huge : ignore_mask;
5884 
5885  if (!(wi_flags & ignore_mask_to_use)) { // Maybe bombs are illegal.
5886  if (swp->secondary_bank_ammo[weapon_bank_list[i]] > 0) {
5887  swp->current_secondary_bank = weapon_bank_list[i];
5888  break;
5889  }
5890  }
5891  }
5892  }
5893  }
5894 
5895 
5896  // If switched banks, force reacquisition of aspect lock.
5897  if (swp->current_secondary_bank != initial_bank) {
5898  aip->aspect_locked_time = 0.0f;
5899  aip->current_target_is_locked = 0;
5900  }
5901 
5903  {
5905 
5906  // phreak -- rapid dumbfire? let it rip!
5907  if ((aip->ai_profile_flags & AIPF_ALLOW_RAPID_SECONDARY_DUMBFIRE) && !(wip->wi_flags & WIF_HOMING) && (wip->fire_wait < .5f))
5908  {
5910  }
5911  }
5912 
5913  ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5914 }
5915 
5919 int compute_num_homing_objects(object *target_objp)
5920 {
5921  object *objp;
5922  int count = 0;
5923 
5924  for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5925  if (objp->type == OBJ_WEAPON) {
5927  if (Weapons[objp->instance].homing_object == target_objp) {
5928  count++;
5929  }
5930  }
5931  }
5932  }
5933 
5934  return count;
5935 }
5936 
5937 // Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5938 // If it's a shockwave weapon, tell your team about it!
5939 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5940 {
5941  if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave.speed > 0.0f)) {
5942  ship_obj *so;
5943  int firing_ship_team;
5944 
5945  firing_ship_team = Ships[firing_objp->instance].team;
5946 
5947  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5948  object *A = &Objects[so->objnum];
5949  Assert(A->type == OBJ_SHIP);
5950 
5951  if (Ships[A->instance].team == firing_ship_team) {
5952  ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5953 
5954  // AL 1-5-98: only avoid shockwave if not docked or repairing
5955  if ( !object_is_docked(A) && !(aip->ai_flags & (AIF_REPAIRING|AIF_BEING_REPAIRED)) ) {
5957  }
5958  }
5959  }
5960  }
5961 }
5962 
5966 float compute_incoming_payload(object *target_objp)
5967 {
5968  missile_obj *mo;
5969  float payload = 0.0f;
5970 
5971  for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5972  object *objp;
5973 
5974  objp = &Objects[mo->objnum];
5975  Assert(objp->type == OBJ_WEAPON);
5976  if (Weapons[objp->instance].homing_object == target_objp) {
5978  }
5979  }
5980 
5981  return payload;
5982 }
5983 
5984 // --------------------------------------------------------------------------
5985 // Return true if OK for *aip to fire its current weapon at its current target.
5986 // Only reason this function returns false is:
5987 // weapon is a homer
5988 // targeted at player
5989 // OR: player has too many homers targeted at him
5990 // Missiontime in that dead zone in which can't fire at this player
5991 // Note: If player is attacking a ship, that ship is allowed to fire at player. Otherwise, we get in a situation in which
5992 // player is attacking a large ship, but that large ship is not defending itself with missiles.
5993 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5994 {
5995  int num_homers = 0;
5996 
5997  if (target_objnum >= 0) {
5998  object *tobjp = &Objects[target_objnum];
5999 
6000  // AL 3-4-98: Ensure objp target is a ship first
6001  if ( tobjp->type == OBJ_SHIP ) {
6002  if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
6003  num_homers = compute_num_homing_objects(&Objects[target_objnum]);
6004  }
6005  }
6006 
6007  // If player, maybe fire based on Skill_level and number of incoming weapons.
6008  // If non-player, maybe fire based on payload of incoming weapons.
6009  if (wip->wi_flags & WIF_HOMING) {
6010  if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
6011  if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
6012  // Don't allow AI ships to fire at player for fixed periods of time based on skill level.
6013  // With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
6014  // At Easy, 2/7...at Expert, 5/7
6015  int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
6017  return 0;
6018  }
6019  }
6020  int swarmers = 0;
6021  if (wip->wi_flags & WIF_SWARM)
6022  swarmers = 2; // Note, always want to be able to fire swarmers if no currently incident homers.
6023  if (The_mission.ai_profile->max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
6024  return 0;
6025  }
6026  } else if (num_homers > 3) {
6027  float incoming_payload;
6028 
6029  incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
6030 
6031  if (incoming_payload > tobjp->hull_strength) {
6032  return 0;
6033  }
6034  }
6035  }
6036  }
6037  else
6038  {
6039  // We have no valid target object, we should not fire at it...
6040  return 0;
6041  }
6042 
6043  return 1;
6044 }
6045 
6046 // --------------------------------------------------------------------------
6047 // Fire a secondary weapon.
6048 // Maybe choose to fire a different one.
6049 // priority1 and priority2 are optional parameters with defaults = -1
6050 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
6051 {
6052  ship_weapon *swp;
6053  ship *shipp;
6054  int current_bank;
6055  int rval = 0;
6056 
6057 #ifndef NDEBUG
6058  if (!Ai_firing_enabled)
6059  return rval;
6060 #endif
6061 
6062  Assert( objp != NULL );
6063  Assert(objp->type == OBJ_SHIP);
6064  shipp = &Ships[objp->instance];
6065  swp = &shipp->weapons;
6066 
6067  Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < static_cast<int>(Ship_info.size()));
6068 
6069  // Select secondary weapon.
6070  current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
6071 
6072  if (current_bank == -1) {
6073  return rval;
6074  }
6075 
6076  Assert(current_bank < shipp->weapons.num_secondary_banks);
6077 
6078  weapon_info *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
6079 
6081  swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
6082  } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
6083  // This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
6084  // bombs, delivering them is probably more important than surviving.
6085  ai_info *aip;
6086 
6087  aip = &Ai_info[shipp->ai_index];
6088 
6089  // Note, maybe don't fire if firing at player and any homers yet fired.
6090  // Decreasing chance to fire the more homers are incoming on player.
6091  if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
6092  if (ship_fire_secondary(objp)) {
6093  rval = 1;
6094  swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6095  }
6096 
6097  } else {
6098  swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6099  }
6100  }
6101 
6102  return rval;
6103 }
6104 
6108 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6109 {
6110  if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6111  if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6112  return objects_will_collide(obj1, obj2, duration, 2.0f);
6113 
6114  return 0;
6115 }
6116 
6120 int might_hit_teammate(object *firing_objp)
6121 {
6122  int team;
6123  object *objp;
6124  ship_obj *so;
6125 
6126  team = Ships[firing_objp->instance].team;
6127 
6128  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6129  objp = &Objects[so->objnum];
6130  if (Ships[objp->instance].team == team) {
6131  float dist, dot;
6132  vec3d vec_to_objp;
6133 
6134  vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6135  dist = vm_vec_mag_quick(&vec_to_objp);
6136  dot = vm_vec_dot(&firing_objp->orient.vec.fvec, &vec_to_objp)/dist;
6137  if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6138  return 1;
6139  }
6140  }
6141 
6142  return 0;
6143 }
6144 
6146 {
6147  int i,j,clr;
6148  polymodel *pm;
6149  model_path *mp;
6150 
6151  pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
6152  vec3d global_path_point;
6153  vertex v, prev_vertex;
6154 
6155  if ( pm->ship_bay == NULL )
6156  return;
6157 
6158  memset(&v, 0, sizeof(v));
6159 
6160  for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6161  mp = &pm->paths[pm->ship_bay->path_indexes[i]];
6162 
6163  for ( j = 0; j < mp->nverts; j++ ) {
6164  vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6165  vm_vec_add2(&global_path_point, &objp->pos);
6166  g3_rotate_vertex(&v, &global_path_point);
6167  clr = 255 - j*50;
6168  if ( clr < 50 )
6169  clr = 100;
6170  gr_set_color(0, clr, 0);
6171 
6172  if ( j == mp->nverts-1 ) {
6173  gr_set_color(255, 0, 0);
6174  }
6175 
6176  g3_draw_sphere( &v, 1.5f);
6177 
6178  if ( j > 0 )
6179  g3_draw_line(&v, &prev_vertex);
6180 
6181  prev_vertex = v;
6182 
6183  }
6184  }
6185 }
6186 
6191 {
6192  int i,j,clr;
6193  polymodel *pm;
6194  model_path *mp;
6195 
6196  pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
6197  vec3d global_path_point;
6198  vertex v, prev_vertex;
6199 
6200  if ( pm->ship_bay == NULL )
6201  return;
6202 
6203  memset(&v, 0, sizeof(v));
6204 
6205  for ( i = 0; i < pm->n_paths; i++ ) {
6206  mp = &pm->paths[i];
6207  for ( j = 0; j < mp->nverts; j++ ) {
6208  vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6209  vm_vec_add2(&global_path_point, &objp->pos);
6210  g3_rotate_vertex(&v, &global_path_point);
6211  clr = 255 - j*50;
6212  if ( clr < 50 )
6213  clr = 100;
6214  gr_set_color(0, clr, 0);
6215 
6216  if ( j == mp->nverts-1 ) {
6217  gr_set_color(255, 0, 0);
6218  }
6219 
6220  g3_draw_sphere( &v, 1.5f);
6221 
6222  if ( j > 0 )
6223  g3_draw_line(&v, &prev_vertex);
6224 
6225  prev_vertex = v;
6226  }
6227  }
6228 }
6229 
6231 {
6232  ship *shipp = &Ships[objp->instance];
6233  ai_info *aip = &Ai_info[shipp->ai_index];
6234  object *dobjp;
6235  polymodel *pm;
6236 
6239 
6240  if (aip->goal_objnum < 0)
6241  return;
6242 
6243  dobjp = &Objects[aip->goal_objnum];
6244  pm = model_get(Ship_info[Ships[dobjp->instance].ship_info_index].model_num);
6245  vec3d dock_point, global_dock_point;
6246  vertex v;
6247 
6248  if (pm->n_docks) {
6249  dock_point = pm->docking_bays[0].pnt[0];
6250  model_instance_find_world_point(&global_dock_point, &dock_point, shipp->model_instance_num, 0, &dobjp->orient, &dobjp->pos );
6251  g3_rotate_vertex(&v, &global_dock_point);
6252  gr_set_color(255, 255, 255);
6253  g3_draw_sphere( &v, 1.5f);
6254  }
6255 
6256  if (aip->path_start != -1) {
6257  vertex prev_vertex;
6258  pnode *pp = &Path_points[aip->path_start];
6259  int num_points = aip->path_length;
6260  int i;
6261 
6262  for (i=0; i<num_points; i++) {
6263  vertex v0;
6264 
6265  memset(&v0, 0, sizeof(v0));
6266 
6267  g3_rotate_vertex( &v0, &pp->pos );
6268 
6269  gr_set_color(0, 128, 96);
6270  if (i != 0)
6271  g3_draw_line(&v0, &prev_vertex);
6272 
6273  if (pp-Path_points == aip->path_cur)
6274  gr_set_color(255,255,0);
6275 
6276  g3_draw_sphere( &v0, 4.5f);
6277 
6278  prev_vertex = v0;
6279 
6280  pp++;
6281  }
6282  }
6283 }
6284 
6289 {
6290  int bank_num, weapon_num;
6291 
6292  bank_num = swp->current_primary_bank;
6293  weapon_num = swp->primary_bank_weapons[bank_num];
6294 
6295  // If weapon_num is illegal, return a reasonable value. A valid weapon
6296  // will get selected when this ship tries to fire.
6297  if (weapon_num == -1) {
6298  return 1000.0f;
6299  }
6300 
6301  return MIN((Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime), Weapon_info[weapon_num].weapon_range);
6302 }
6303 
6305 {
6306  int bank_num, weapon_num;
6307 
6308  bank_num = swp->current_primary_bank;
6309  if (bank_num < 0)
6310  return 100.0f;
6311 
6312  weapon_num = swp->primary_bank_weapons[bank_num];
6313 
6314  if (weapon_num == -1) {
6315  return 100.0f;
6316  }
6317 
6318  return Weapon_info[weapon_num].max_speed;
6319 }
6320 
6322 {
6323  int bank_num, weapon_num;
6324 
6325  bank_num = swp->current_primary_bank;
6326  if (bank_num < 0)
6327  return NULL;
6328 
6329  weapon_num = swp->primary_bank_weapons[bank_num];
6330 
6331  if (weapon_num == -1) {
6332  return NULL;
6333  }
6334 
6335  return &Weapon_info[weapon_num];
6336 }
6337 
6338 // Compute the predicted position of a ship to be fired upon from a turret.
6339 // This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6340 // Return value in *predicted_enemy_pos.
6341 // Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6342 // *pobjp object firing the weapon
6343 // *eobjp object being fired upon
6344 void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range)
6345 {
6346  ship *shipp = &Ships[pobjp->instance];
6347  float range_time;
6348 
6349  if (weapon_speed < 1.0f)
6350  weapon_speed = 1.0f;
6351 
6352  range_time = 2.0f;
6353 
6354  // Make it take longer for enemies to get player's allies in range based on skill level.
6356  range_time += Ai_info[shipp->ai_index].ai_in_range_time;
6357 
6358  if (time_enemy_in_range < range_time) {
6359  float dist;
6360 
6361  dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6362  vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6363  } else {
6364  float collision_time, scale;
6365  vec3d rand_vec;
6366  ai_info *aip = &Ai_info[shipp->ai_index];
6367 
6368  collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6369 
6370  if (collision_time == 0.0f){
6371  collision_time = 100.0f;
6372  }
6373 
6374  vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6375  if (time_enemy_in_range > 2*range_time){
6376  scale = (1.0f - aip->ai_accuracy) * 4.0f;
6377  } else {
6378  scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6379  }
6380 
6381  static_randvec(((OBJ_INDEX(pobjp)) ^ (Missiontime >> 16)) & 7, &rand_vec);
6382 
6383  vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6384  G_collision_time = collision_time;
6385  G_fire_pos = *gun_pos;
6386  }
6387 
6388  G_predicted_pos = *predicted_enemy_pos;
6389 }
6390 
6391 // Compute the predicted position of a ship to be fired upon.
6392 // This is based on current position of firing object, enemy object, relative position of gun on firing object,
6393 // weapon speed and skill level constraints.
6394 // Return value in *predicted_enemy_pos.
6395 // Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6396 //SUSHI: Modified to take in a position and accel value instead of reading it directly from the enemy object
6397 void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, ai_info *aip)
6398 {
6399  float weapon_speed, range_time;
6400  ship *shipp = &Ships[pobjp->instance];
6401  weapon_info *wip;
6402  vec3d target_moving_direction;
6403 
6404  Assert( enemy_pos != NULL );
6405  Assert( enemy_vel != NULL );
6406 
6407  wip = ai_get_weapon(&shipp->weapons);
6408  target_moving_direction = *enemy_vel;
6409 
6411  vm_vec_scale_sub2(&target_moving_direction, &pobjp->phys_info.vel, wip->vel_inherit_amount);
6412 
6413  if (wip != NULL)
6414  weapon_speed = wip->max_speed;
6415  else
6416  weapon_speed = 100.0f;
6417 
6418  weapon_speed = MAX(weapon_speed, 1.0f); // set not less than 1
6419 
6420  range_time = 2.0f;
6421 
6422  // Make it take longer for enemies to get player's allies in range based on skill level.
6423  // but don't bias team v. team missions
6424  if ( !(MULTI_TEAM) )
6425  {
6426  if (iff_x_attacks_y(shipp->team, Player_ship->team))
6427  range_time += aip->ai_in_range_time;
6428  }
6429 
6430  if (aip->time_enemy_in_range < range_time) {
6431  float dist;
6432 
6433  dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6434  vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &target_moving_direction, aip->time_enemy_in_range * dist/weapon_speed);
6435  } else {
6436  float collision_time;
6437  vec3d gun_pos, pnt;
6438  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
6439 
6440  // Compute position of gun in absolute space and use that as fire position
6441  // ...unless we want to just use the ship center
6443  pnt = pm->gun_banks[0].pnt[0];
6444  } else {
6445  //Use the convergence offset, if there is one
6446  vm_vec_copy_scale(&pnt, &Ship_info[shipp->ship_info_index].convergence_offset, 1.0f);
6447  }
6448  vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6449  vm_vec_add2(&gun_pos, &pobjp->pos);
6450 
6451  collision_time = compute_collision_time(enemy_pos, &target_moving_direction, &gun_pos, weapon_speed);
6452 
6453  if (collision_time == 0.0f) {
6454  collision_time = 100.0f;
6455  }
6456 
6457  vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &target_moving_direction, collision_time);
6458 
6459  // set globals
6460  G_collision_time = collision_time;
6461  G_fire_pos = gun_pos;
6462  }
6463 
6464  // Now add error terms (1) regular aim (2) EMP (3) stealth
6465  float scale = 0.0f;
6466  vec3d rand_vec;
6467 
6468  // regular skill level error in aim
6469  if (aip->time_enemy_in_range > 2*range_time) {
6470  scale = (1.0f - aip->ai_accuracy) * 4.0f;
6471  } else {
6472  scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6473  }
6474 
6475  // if this ship is under the effect of an EMP blast, throw his aim off a bit
6476  if (shipp->emp_intensity > 0.0f) {
6477  // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6478  scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6479  mprintf(("AI miss scale factor (EMP) %f\n",scale));
6480  }
6481 
6482  // if stealthy ship, throw his aim off, more when farther away and when dot is small
6483  if ( aip->ai_flags & AIF_STEALTH_PURSUIT ) {
6484  float dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6485  vec3d temp;
6486  vm_vec_sub(&temp, enemy_pos, &pobjp->pos);
6487  vm_vec_normalize_quick(&temp);
6488  float dot = vm_vec_dot(&temp, &pobjp->orient.vec.fvec);
6489  float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6490  scale += st_err;
6491  }
6492 
6493  // get a random vector that changes slowly over time (1x / sec)
6494  static_randvec(((OBJ_INDEX(pobjp)) ^ (Missiontime >> 16)) & 7, &rand_vec);
6495 
6496  vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6497 
6498  // set global
6499  G_predicted_pos = *predicted_enemy_pos;
6500 }
6501 
6506 {
6507  vec3d tvec;
6508  ship_info *sip;
6509  ai_info *aip;
6510 
6511  Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6512  sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6513  Assert(Ships[Pl_objp->instance].ai_index >= 0);
6514  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6515 
6516  // Make a continuous turn towards any combination of possibly negated
6517  // up and right vectors.
6518  tvec = Pl_objp->pos;
6519 
6520  if (aip->submode_parm0 & 0x01)
6521  vm_vec_add2(&tvec, &Pl_objp->orient.vec.rvec);
6522  if (aip->submode_parm0 & 0x02)
6523  vm_vec_sub2(&tvec, &Pl_objp->orient.vec.rvec);
6524  if (aip->submode_parm0 & 0x04)
6525  vm_vec_add2(&tvec, &Pl_objp->orient.vec.uvec);
6526  if (aip->submode_parm0 & 0x08)
6527  vm_vec_sub2(&tvec, &Pl_objp->orient.vec.uvec);
6528 
6529  // Detect degenerate cases that cause tvec to be same as player pos.
6530  if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6531  aip->submode_parm0 &= 0x05;
6532  if (aip->submode_parm0 == 0)
6533  aip->submode_parm0 = 1;
6534  vm_vec_add2(&tvec, &Pl_objp->orient.vec.rvec);
6535  }
6536 
6537  ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6538  accelerate_ship(aip, 1.0f);
6539 }
6540 
6544 void ai_chase_eb(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, float dist_to_enemy)
6545 {
6546  vec3d _pep;
6547  float dot_to_enemy, dot_from_enemy;
6548 
6549  compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6550 
6551  // If we're trying to slow down to get behind, then point to turn towards is different.
6552  _pep = *predicted_enemy_pos;
6553  if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6554  vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.vec.fvec, 100.0f);
6555 
6556  ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6557 
6558  accelerate_ship(aip, 0.0f);
6559 }
6560 
6561 // Return time until weapon_objp might hit ship_objp.
6562 // Assumes ship_objp is not moving.
6563 // Returns negative time if not going to hit.
6564 // This is a very approximate function, but is pretty fast.
6565 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6566 {
6567  float to_dot, from_dot, dist;
6568 
6569  dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6570 
6571  // Note, this is bogus. It assumes only the weapon is moving.
6572  // Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6573  // (Ie, if object moving at right angle to weapon, just continue for now...)
6574  if (weapon_objp->phys_info.speed < 1.0f)
6575  return dist + 1.0f;
6576  else if ((from_dot > 0.1f) && (dist/(from_dot*from_dot) < 48*ship_objp->radius)) //: don't require them to see it, they have instruments!: && (fl_abs(to_dot) > 0.5f))
6577  return dist / weapon_objp->phys_info.speed;
6578  else
6579  return -1.0f;
6580 }
6581 
6582 // Return time until danger weapon could hit this ai object.
6583 // Return negative time if not endangered.
6585 {
6586  object *weapon_objp;
6587 
6588  if (aip->danger_weapon_objnum == -1) {
6589  return -1.0f;
6590  }
6591 
6592  weapon_objp = &Objects[aip->danger_weapon_objnum];
6593 
6594  if (weapon_objp->signature != aip->danger_weapon_signature) {
6595  aip->danger_weapon_objnum = -1;
6596  return -1.0f;
6597  }
6598 
6599  return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6600 }
6601 
6602 // Return true if this ship is near full strength.
6603 // Goober5000: simplified and accounted for shields being 0
6605 {
6606  return (get_hull_pct(objp) > 0.9f) || (get_shield_pct(objp) > 0.8f);
6607 }
6608 
6610 {
6611  //Sidethrust vector is initially based on the velocity vector representing the ship's current sideways motion.
6612  vec2d side_vec;
6613  //Length to hold circle strafe is random (1-3) + slide accel time, changes every 4 seconds
6614  int strafeHoldDirAmount = (int)(sip->slide_accel) + static_rand_range((Missiontime + static_rand(aip->shipnum)) >> 18, 1, 3);
6615  //Get a random float using some of the more significant chunks of the missiontime as a seed (>>16 means it changes every second)
6616  //This means that we get the same random values for a little bit.
6617  //Using static_rand(shipnum) as a crude hash function to make sure that the seed is different for each ship and direction
6618  //The *2 ensures that y and x stay separate.
6619  if (strafeHoldDirAmount > 0) { //This may look unnecessary, but we're apparently still getting div by zero errors on Macs here.
6620  side_vec.x = static_randf_range((((Missiontime + static_rand(aip->shipnum)) >> 16) / strafeHoldDirAmount) , -1.0f, 1.0f);
6621  side_vec.y = static_randf_range((((Missiontime + static_rand(aip->shipnum)) >> 16) / strafeHoldDirAmount) * 2, -1.0f, 1.0f);
6622  } else {
6623  Warning(LOCATION, "Division by zero in do_random_sidethrust averted. Please tell a coder.\n");
6624  side_vec.x = 1.0f;
6625  side_vec.y = 1.0f;
6626  }
6627  //Scale it up so that the longest dimension is length 1.0. This ensures we are always getting as much use out of sidethrust as possible.
6628  vm_vec_boxscale(&side_vec, 1.0f);
6629 
6630  AI_ci.sideways = side_vec.x;
6631  AI_ci.vertical = side_vec.y;
6632 }
6633 
6637 void attack_set_accel(ai_info *aip, ship_info *sip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6638 {
6639  float speed_ratio;
6640 
6641  if (En_objp->phys_info.speed > 1.0f)
6642  speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6643  else
6644  speed_ratio = 5.0f;
6645 
6646  // Sometimes, told to attack slowly. Allows to get in more hits.
6647  if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6648  if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6649  if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp)) {
6650  accelerate_ship(aip, MAX(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6651  return;
6652  }
6653  } else
6654  aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6655  }
6656 
6657  //Glide attack: we turn on glide to maintain current vector while aiming at the enemy
6658  //The aiming part should already be taken care of.
6659  if (aip->submode == AIS_CHASE_GLIDEATTACK) {
6660  Pl_objp->phys_info.flags |= PF_GLIDING;
6661  accelerate_ship(aip, 0.0f);
6662  return;
6663  }
6664 
6665  //Circle Strafe: We try to maintain a constant distance from the target while using sidethrust to move in a circle
6666  //around the target
6667  if (aip->submode == AIS_CHASE_CIRCLESTRAFE) {
6668  //If glide is available, use it (smooths things out a bit)
6669  if (sip->can_glide == true)
6670  Pl_objp->phys_info.flags |= PF_GLIDING;
6671 
6672  //Try to maintain a distance between 50% and 75% of maximum circle strafe distance
6673  if (dist_to_enemy <= CIRCLE_STRAFE_MAX_DIST * .5)
6674  accelerate_ship(aip, -1.0f);
6675  else if (dist_to_enemy >= CIRCLE_STRAFE_MAX_DIST * 0.75)
6676  accelerate_ship(aip, 1.0f);
6677  else
6678  accelerate_ship(aip, 0.0f);
6679 
6680  do_random_sidethrust(aip, sip);
6681  return;
6682  }
6683 
6684 
6685  if (dist_to_enemy > 200.0f + vm_vec_mag_quick(&En_objp->phys_info.vel) * dot_from_enemy + Pl_objp->phys_info.speed * speed_ratio) {
6686  if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6687  if (dist_to_enemy > 800.0f) {
6688  if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6689  float percent_left;
6690  ship *shipp;
6691  ship_info *sip_local;
6692 
6693  shipp = &Ships[Pl_objp->instance];
6694  sip_local = &Ship_info[shipp->ship_info_index];
6695 
6696  if (sip_local->afterburner_fuel_capacity > 0.0f) {
6697  percent_left = 100.0f * shipp->afterburner_fuel / sip_local->afterburner_fuel_capacity;
6698  if (percent_left > 30.0f + ((OBJ_INDEX(Pl_objp)) & 0x0f)) {
6699  afterburners_start(Pl_objp);
6701  float max_ab_vel;
6702  float time_to_exhaust_25pct_fuel;
6703  float time_to_fly_75pct_of_distance;
6704  float ab_time;
6705 
6706  // Max afterburner speed - make sure we don't devide by 0 later
6707  max_ab_vel = sip_local->afterburner_max_vel.xyz.z > 0.0f ? sip_local->afterburner_max_vel.xyz.z : sip_local->max_vel.xyz.z;
6708  max_ab_vel = max_ab_vel > 0.0f ? max_ab_vel : 0.0001f;
6709 
6710  // Time to exhaust 25% of the remaining fuel
6711  time_to_exhaust_25pct_fuel = shipp->afterburner_fuel * 0.25f / sip_local->afterburner_burn_rate;
6712 
6713  // Time to fly 75% of the distance to the target
6714  time_to_fly_75pct_of_distance = dist_to_enemy * 0.75f / max_ab_vel;
6715 
6716  // Get minimum
6717  ab_time = MIN(time_to_exhaust_25pct_fuel, time_to_fly_75pct_of_distance);
6718 
6719  aip->afterburner_stop_time = (fix) (Missiontime + F1_0 * ab_time);
6720  } else {
6722  }
6723  }
6724  }
6725  }
6726  }
6727  }
6728 
6729  accelerate_ship(aip, 1.0f);
6730  } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6731  && (En_objp->phys_info.speed < 10.0f)
6732  && (dist_to_enemy > 25.0f)
6733  && (dot_to_enemy > 0.8f)
6734  && (dot_from_enemy < 0.8f)) {
6735  accelerate_ship(aip, 0.0f); // No one attacking us, so don't need to move.
6736  } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6737  set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6738  } else if (Pl_objp->phys_info.speed < 15.0f) {
6739  accelerate_ship(aip, 1.0f);
6740  } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6741  if (dot_from_enemy > 0.75f)
6742  accelerate_ship(aip, 1.0f);
6743  else
6744  set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6745  } else {
6746  change_acceleration(aip, 0.5f);
6747  }
6748 }
6749 
6750 // Pl_objp (aip) tries to get behind En_objp.
6751 // New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6752 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6753 {
6754  vec3d new_pos;
6755  float dot;
6756  vec3d vec_from_enemy;
6757 
6758  vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6759 
6760  vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.vec.fvec, -100.0f); // Pick point 100 units behind.
6761  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6762 
6763  dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.vec.fvec);
6764 
6765  if (dot > 0.25f) {
6766  accelerate_ship(aip, 1.0f);
6767  } else {
6768  accelerate_ship(aip, (dot + 1.0f)/2.0f);
6769  }
6770 }
6771 
6772 int avoid_player(object *objp, vec3d *goal_pos)
6773 {
6774  maybe_avoid_player(Pl_objp, goal_pos);
6775  ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6776 
6777  if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6778  ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6779 
6780  if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6781  ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6782  accelerate_ship(aip, 0.5f);
6783  return 1;
6784  }
6785  }
6786 
6787  return 0;
6788 }
6789 
6790 // Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6791 // If so, stuff *collision_point.
6792 int will_collide_pp(vec3d *p0, vec3d *p1, float radius, object *big_objp, vec3d *collision_point)
6793 {
6794  mc_info mc;
6795  mc_info_init(&mc);
6796 
6797  polymodel *pm = model_get(Ship_info[Ships[big_objp->instance].ship_info_index].model_num);
6798 
6799  mc.model_instance_num = -1;
6800  mc.model_num = pm->id; // Fill in the model to check
6801  mc.orient = &big_objp->orient; // The object's orient
6802  mc.pos = &big_objp->pos; // The object's position
6803  mc.p0 = p0; // Point 1 of ray to check
6804  mc.p1 = p1;
6806 
6807  mc.radius = radius;
6808 
6809  // Only check the 2nd lowest hull object
6810  mc.submodel_num = pm->detail[0];
6811  model_collide(&mc);
6812 
6813  if (mc.num_hits)
6814  *collision_point = mc.hit_point_world;
6815 
6816  return mc.num_hits;
6817 }
6818 
6819 // Return true/false if *objp will collide with *big_objp
6820 // Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6821 // Global collision point stuffed in *collision_point
6822 int will_collide_with_big_ship(object *objp, vec3d *goal_point, object *big_objp, vec3d *collision_point, float delta_time)
6823 {
6824  float radius;
6825  vec3d end_pos;
6826 
6827  radius = big_objp->radius + delta_time * objp->phys_info.speed;
6828 
6829  if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6830  return 0;
6831  }
6832 
6833  if (goal_point == NULL) {
6834  vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time); // Point 2 of ray to check
6835  } else {
6836  end_pos = *goal_point;
6837  }
6838 
6839  return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6840 }
6841 
6842 // Return true if *objp is expected to collide with a large ship.
6843 // Stuff global collision point in *collision_point.
6844 // If *goal_point is not NULL, use that as the point towards which *objp will be flying. Don't use *objp velocity
6845 // *ignore_objp will typically be the target this ship is pursuing, either to attack or guard. We don't want to avoid it.
6846 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vec3d *goal_point, vec3d *collision_point, float *distance, float delta_time)
6847 {
6848  ship_obj *so;
6849  object *big_objp;
6850  int collision_obj_index = -1;
6851  float min_dist = 999999.9f;
6852 
6853  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6854  big_objp = &Objects[so->objnum];
6855 
6856  if (big_objp == ignore_objp)
6857  continue;
6858 
6859  if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6860  vec3d cur_collision_point;
6861  float cur_dist;
6862 
6863  if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6864 
6865  cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6866 
6867  if (cur_dist < min_dist) {
6868  min_dist = cur_dist;
6869  *collision_point = cur_collision_point;
6870  collision_obj_index = OBJ_INDEX(big_objp);
6871  }
6872  }
6873  }
6874  }
6875 
6876  *distance = min_dist;
6877  return collision_obj_index;
6878 
6879 }
6880 
6881 typedef struct {
6882  float dist;
6883  int collide;
6885 } sgoal;
6886 
6887 // Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6888 // Return result in *avoid_pos
6889 void mabs_pick_goal_point(object *objp, object *big_objp, vec3d *collision_point, vec3d *avoid_pos)
6890 {
6891  matrix mat1;
6892  sgoal goals[4];
6893  vec3d v2b;
6894 
6895  vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6896  vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6897 
6898  int found = 0;
6899 
6900  // Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6901  // First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6902  // means less of a turn.
6903  // Try going as far as 1.25f * radius.
6904  float s;
6905  for (s=0.5f; s<1.3f; s += 0.25f) {
6906  int i;
6907  for (i=0; i<4; i++) {
6908  vec3d p = big_objp->pos;
6909  float ku = big_objp->radius*s + objp->radius * (OBJ_INDEX(objp) % 4)/4; // This objp->radius stuff to prevent ships from glomming together at one point
6910  float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6911  if (i&1)
6912  ku = -ku;
6913  if (i&2)
6914  kr = -kr;
6915  vm_vec_scale_add2(&p, &mat1.vec.uvec, ku);
6916  vm_vec_scale_add2(&p, &mat1.vec.rvec, kr);
6917  goals[i].pos = p;
6918  goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6919  goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6920  if (!goals[i].collide)
6921  found = 1;
6922  }
6923 
6924  // If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6925  if (found) {
6926  float min_dist = 9999999.9f;
6927  int min_index = -1;
6928 
6929  for (i=0; i<4; i++) {
6930  if (!goals[i].collide && (goals[i].dist < min_dist)) {
6931  min_dist = goals[i].dist;
6932  min_index = i;
6933  }
6934  }
6935 
6936  Assert(i != -1);
6937  if (i != -1) {
6938  *avoid_pos = goals[min_index].pos;
6939  return;
6940  }
6941  }
6942  }
6943 
6944  // Drat. We tried and tried and could not find a point that did not cause a collision.
6945  // Get this dump pilot far away from the problem ship.
6946  vec3d away_vec;
6947  vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6948  vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6949 
6950 }
6951 
6955 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time)
6956 {
6958  float distance;
6959  vec3d collision_point;
6960  int ship_num;
6961  if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6963  mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6964  float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6965  aip->avoid_check_timestamp = timestamp(2000 + MIN(1000, (int) (dist * 2.0f))); // Delay until check again is based on distance to avoid point.
6966  aip->avoid_ship_num = ship_num;
6967  } else {
6970  aip->avoid_ship_num = -1;
6971  aip->avoid_check_timestamp = timestamp(1500);
6972  }
6973  }
6974 
6975  if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6976  ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6977 
6978  vec3d v2g;
6979 
6980  ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6981  vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6982  float dot = vm_vec_dot(&objp->orient.vec.fvec, &v2g);
6983  float d2 = (1.0f + dot) * (1.0f + dot);
6984  accelerate_ship(aip, d2/4.0f);
6985  return 1;
6986  }
6987 
6988  return 0;
6989 }
6990 
6995 void compute_desired_rvec(vec3d *rvec, vec3d *goal_pos, vec3d *cur_pos)
6996 {
6997  vec3d v2e;
6998 
6999  vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
7000  rvec->xyz.x = v2e.xyz.z;
7001  rvec->xyz.y = 0.0f;
7002  rvec->xyz.z = -v2e.xyz.x;
7003  if (vm_vec_mag_squared(rvec) < 0.001f)
7004  rvec->xyz.y = 1.0f;
7005 }
7006 
7011 {
7012  ai_info *aip;
7013  ship_info *sip;
7014 
7015  vec3d new_pos, vec_to_enemy;
7016  float dist_to_enemy, dot_to_enemy, dot_from_enemy;
7017 
7018  Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
7019  sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7020  Assert(Ships[Pl_objp->instance].ai_index >= 0);
7021  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
7022 
7023  // get time since last seen
7024  int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
7025 
7026  // if delta_time is really big, i'm real confused, start sweep
7027  if (delta_time > 10000) {
7028  aip->submode_parm0 = SM_SF_BAIL;
7029  }
7030 
7031  // guestimate new position
7032  vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
7033 
7034  // if I think he's behind me, go to the goal point
7035  if ( aip->submode_parm0 == SM_SF_BEHIND ) {
7036  new_pos = aip->goal_point;
7037  }
7038 
7039  // check for collision with big ships
7040  if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
7041  // reset ai submode to chase
7042  return;
7043  }
7044 
7045  // if dist is near max and dot is close to 1, accel, afterburn
7046  vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
7047  dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
7048  dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
7049 
7050  // if i think i should see him ahead and i don't, set goal pos and turn around, but only if I haven't seen him for a while
7051  if ( (delta_time > 800) && (aip->submode_parm0 == SM_SF_AHEAD) && (dot_to_enemy > .94) && (dist_to_enemy < get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST + 50) ) {
7052  // do turn around)
7053  vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.fvec, -300.0f);
7054  aip->submode_parm0 = SM_SF_BEHIND;
7055  vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
7056  dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
7057  dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
7058  }
7059 
7060  if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) { // 20 degree half angle
7061  // accelerate ship
7062  accelerate_ship(aip, 1.0f);
7063 
7064  // engage afterburner
7065  if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7066  if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7067  afterburners_start(Pl_objp);
7068  aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7069  }
7070  }
7071 
7072  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7073  return;
7074  }
7075 
7076  // If enemy more than 500 meters away, all ships flying there will tend to match bank.
7077  // They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7078  // to interpolate a matrix rather than just a vector.
7079  if (dist_to_enemy > 500.0f) {
7080  vec3d rvec;
7081  compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
7082  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
7083  } else {
7084  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7085  }
7086 
7087  dot_from_enemy = -vm_vec_dot(&vec_to_enemy, &En_objp->orient.vec.fvec);
7088 
7089  attack_set_accel(aip, sip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7090 }
7091 
7096 {
7097  ai_info *aip;
7098  ship_info *sip;
7099 
7100  Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
7101  sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7102  Assert(Ships[Pl_objp->instance].ai_index >= 0);
7103  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
7104 
7105  vec3d goal_pt;
7106  vec3d forward, right, up;
7107  int lost_time;
7108 
7109  // time since stealth last seen
7110  lost_time = (timestamp() - aip->stealth_last_visible_stamp);
7111 
7112  // determine which pt to fly to in sweep by keeping track of parm0
7113  if (aip->submode_parm0 == SM_SS_SET_GOAL) {
7114 
7115  // don't make goal pt more than 2k from current pos
7116  vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
7117 
7118  // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
7119  float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
7120  box_size = MIN(200.0f, box_size);
7121  box_size = MAX(500.0f, box_size);
7122  aip->stealth_sweep_box_size = box_size;
7123 
7124  aip->goal_point = goal_pt;
7125  aip->submode_parm0 = SM_SS_BOX0;
7126  }
7127 
7128  // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
7129  // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
7130  // if stealth has no velocity make a velocity
7131  if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
7133  }
7134 
7135  // get "right" vector for box
7136  vm_vec_cross(&right, &aip->stealth_velocity, &vmd_y_vector);
7137 
7138  if ( vm_vec_mag_quick(&right) < 0.01 ) {
7139  vm_vec_cross(&right, &aip->stealth_velocity, &vmd_z_vector);
7140  }
7141 
7142  vm_vec_normalize_quick(&right);
7143 
7144  // get "forward" for box
7146 
7147  // get "up" for box
7148  vm_vec_cross(&up, &forward, &right);
7149 
7150  // lost far away ahead (do box)
7151  switch(aip->submode_parm0) {
7152  case SM_SS_BOX0:
7153  goal_pt = aip->goal_point;
7154  break;
7155 
7156  // pt1 -U +R
7157  case SM_SS_LR:
7158  vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7159  vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7160  vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7161  break;
7162 
7163  // pt2 +U -R
7164  case SM_SS_UL:
7165  vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7166  vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7167  vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7168  break;
7169 
7170  // pt3 back
7171  case SM_SS_BOX1:
7172  goal_pt = aip->goal_point;
7173  break;
7174 
7175  // pt4 +U +R
7176  case SM_SS_UR:
7177  vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7178  vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7179  vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7180  break;
7181 
7182  // pt5 -U -R
7183  case SM_SS_LL:
7184  vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7185  vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7186  vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7187  break;
7188 
7189  // pt6 back
7190  case SM_SS_BOX2:
7191  goal_pt = aip->goal_point;
7192  break;
7193 
7194  default:
7195  Int3();
7196 
7197  }
7198 
7199  // when close to goal_pt, update next goal pt
7200  float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7201  if (dist_to_goal < 15) {
7202  aip->submode_parm0++;
7203  }
7204 
7205  // check for collision with big ship
7206  if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7207  // skip to the next pt on box
7208  aip->submode_parm0++;
7209  return;
7210  }
7211 
7212  ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7213 
7214  float dot = 1.0f;
7215  if (dist_to_goal < 100) {
7216  vec3d vec_to_goal;
7217  vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7218  dot = vm_vec_dot(&vec_to_goal, &Pl_objp->orient.vec.fvec);
7219  }
7220 
7221  accelerate_ship(aip, 0.8f*dot);
7222 }
7223 
7227 void ai_chase_attack(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, float dist_to_enemy, int modelnum)
7228 {
7229  int start_bank;
7230  float dot_to_enemy, dot_from_enemy;
7231  float bank_override = 0.0f;
7232 
7233  if (!(aip->ai_profile_flags2 & AIPF2_NO_SPECIAL_PLAYER_AVOID) && avoid_player(Pl_objp, predicted_enemy_pos))
7234  return;
7235 
7236  compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7237 
7238  polymodel *po = model_get( modelnum );
7239 
7240  vec3d *rel_pos;
7241  float scale;
7242  vec3d randvec;
7243  vec3d new_pos;
7244 
7245  start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7246  if (po->n_guns && start_bank != -1 ) {
7247  rel_pos = &po->gun_banks[start_bank].pnt[0];
7248  } else
7249  rel_pos = NULL;
7250 
7251  // If ship moving slowly relative to its size, then don't attack its center point.
7252  // How far from center we attack is based on speed, size and distance to enemy
7253  if (En_objp->radius > En_objp->phys_info.speed) {
7254  static_randvec(OBJ_INDEX(Pl_objp), &randvec);
7255  scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7256  scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius); // scale downward by 1/2 to 1/4
7257  vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7258  } else
7259  new_pos = *predicted_enemy_pos;
7260 
7261  //SUSHI: Don't change bank while circle strafing or glide attacking
7262  if (dist_to_enemy < 250.0f && dot_from_enemy > 0.7f && aip->submode != AIS_CHASE_CIRCLESTRAFE && aip->submode != AIS_CHASE_GLIDEATTACK) {
7263  bank_override = Pl_objp->phys_info.speed;
7264  }
7265 
7266  // If enemy more than 500 meters away, all ships flying there will tend to match bank.
7267  // They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7268  // to interpolate a matrix rather than just a vector.
7269  if (dist_to_enemy > 500.0f) {
7270  vec3d rvec;
7271  compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7272  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7273  } else {
7274  ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7275  }
7276 
7277  attack_set_accel(aip, sip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7278 }
7279 
7280 // EVADE_SQUIGGLE submode handler for chase mode.
7281 // Changed by MK on 5/5/97.
7282 // Used to evade towards a point off the right or up vector.
7283 // Now, evade straight away to try to get far away.
7284 // The squiggling should protect against laser fire.
7286 {
7287  vec3d tvec;
7288  fix timeslice;
7289  fix scale;
7290 
7291  tvec = Pl_objp->pos;
7292 
7293  timeslice = (Missiontime >> 16) & 0x0f;
7294  scale = ((Missiontime >> 16) & 0x0f) << 14;
7295 
7296  if (timeslice & 0x01)
7297  vm_vec_scale_add2(&tvec, &Pl_objp->orient.vec.rvec, f2fl(scale ^ 0x10000));
7298  if (timeslice & 0x02)
7299  vm_vec_scale_sub2(&tvec, &Pl_objp->orient.vec.rvec, f2fl(scale));
7300  if (timeslice & 0x04)
7301  vm_vec_scale_add2(&tvec, &Pl_objp->orient.vec.uvec, f2fl(scale ^ 0x10000));
7302  if (timeslice & 0x08)
7303  vm_vec_scale_sub2(&tvec, &Pl_objp->orient.vec.uvec, f2fl(scale));
7304 
7305  while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7306  tvec.xyz.x += frand();
7307  tvec.xyz.y += frand();
7308  }
7309 
7310  float bank_override = Pl_objp->phys_info.speed;
7311 
7312  ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7313  accelerate_ship(aip, 1.0f);
7314 }
7315 
7320 {
7321  // If not near end of this submode, evade squiggly. If near end, just fly straight for a bit
7322  vec3d tvec;
7323  float bank_override;
7324  vec3d vec_from_enemy;
7325 
7326  if (En_objp != NULL) {
7327  vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7328  } else
7329  vec_from_enemy = Pl_objp->orient.vec.fvec;
7330 
7331  static_randvec(Missiontime >> 15, &tvec);
7332  vm_vec_scale(&tvec, 100.0f);
7333  vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7334  vm_vec_add2(&tvec, &Pl_objp->pos);
7335 
7336  bank_override = Pl_objp->phys_info.speed;
7337 
7338  ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7339 
7340  accelerate_ship(aip, 2.0f);
7341 
7342  if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7343  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7344  float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7345  if (percent_left > 30.0f + ((OBJ_INDEX(Pl_objp)) & 0x0f)) {
7346  afterburners_start(Pl_objp);
7347  aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7348  }
7349  afterburners_start(Pl_objp);
7350  aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7351  }
7352  }
7353 
7354 }
7355 
7356 // Make object *objp attack subsystem with ID = subnum.
7357 // Return true if found a subsystem to attack, else return false.
7358 // Note, can fail if subsystem exists, but has no hits.
7359 int ai_set_attack_subsystem(object *objp, int subnum)
7360 {
7361  int temp;
7362  ship *shipp, *attacker_shipp;
7363  ai_info *aip;
7364  ship_subsys *ssp;
7365  object *attacked_objp;
7366 
7367  Assert(objp->type == OBJ_SHIP);
7368  Assert(objp->instance >= 0);
7369 
7370  attacker_shipp = &Ships[objp->instance];
7371  Assert(attacker_shipp->ai_index >= 0);
7372 
7373  aip = &Ai_info[attacker_shipp->ai_index];
7374 
7375  // MWA -- 2/27/98. Due to AL's changes, target_objnum is now not always valid (at least sometimes
7376  // in terms of goals). So, bail if we don't have a valid target.
7377  if ( aip->target_objnum == -1 )
7378  return 0;
7379 
7380  attacked_objp = &Objects[aip->target_objnum];
7381  shipp = &Ships[attacked_objp->instance]; // need to get our target's ship pointer!!!
7382 
7383  ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7384  if (ssp == NULL)
7385  return 0;
7386 
7387  set_targeted_subsys(aip, ssp, aip->target_objnum);
7388 
7389  if (aip->ignore_objnum == aip->target_objnum)
7391 
7392  // Goober5000
7393  temp = find_ignore_new_object_index(aip, aip->target_objnum);
7394  if (temp >= 0)
7395  {
7397  }
7398  else if (is_ignore_object(aip, aip->target_objnum, 1))
7399  {
7401  }
7402 
7403  // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7404 
7405  ai_set_goal_maybe_abort_dock(objp, aip);
7407 
7408  return 1;
7409 }
7410 
7411 void ai_set_guard_vec(object *objp, object *guard_objp)
7412 {
7413  ai_info *aip;
7414  float radius;
7415 
7416  aip = &Ai_info[Ships[objp->instance].ai_index];
7417 
7418  // Handle case of bogus call in which ship is told to guard self.
7419  Assert(objp != guard_objp);
7420  if (objp == guard_objp) {
7422  vm_vec_scale(&aip->guard_vec, 100.0f);
7423  return;
7424  }
7425 
7426  // check if guard_objp is BIG
7427  radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7428  if (radius > 300.0f) {
7429  radius = guard_objp->radius * 1.25f;
7430  }
7431 
7432  vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7433 
7434  if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7435  // Far away, don't just use vector to object, causes clustering of guard ships.
7436  vec3d tvec, rvec;
7437  float mag;
7438  mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7439  vm_vec_rand_vec_quick(&rvec);
7440  vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7441  vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7442  }
7443 
7445  vm_vec_scale(&aip->guard_vec, radius);
7446 }
7447 
7448 // Make object *objp guard object *other_objp.
7449 // To be called from the goals code.
7450 void ai_set_guard_wing(object *objp, int wingnum)
7451 {
7452  ship *shipp;
7453  ai_info *aip;
7454  int leader_objnum, leader_shipnum;
7455 
7456  Assert(wingnum >= 0);
7457 
7458  Assert(objp->type == OBJ_SHIP);
7459  Assert(objp->instance >= 0);
7460 
7461  // shouldn't set the ai mode for the player
7462  if ( objp == Player_obj ) {
7463  return;
7464  }
7465 
7466  shipp = &Ships[objp->instance];
7467 
7468  Assert(shipp->ai_index >= 0);
7469 
7470  aip = &Ai_info[shipp->ai_index];
7471  force_avoid_player_check(objp, aip);
7472 
7473  ai_set_goal_maybe_abort_dock(objp, aip);
7475 
7476  // This function is called whenever a guarded ship is destroyed, so this code
7477  // prevents a ship from trying to guard a non-existent wing.
7478  if (Wings[wingnum].current_count < 1) {
7479  aip->guard_objnum = -1;
7480  aip->guard_wingnum = -1;
7481  aip->mode = AIM_NONE;
7482  } else {
7483  leader_shipnum = Wings[wingnum].ship_index[0];
7484  leader_objnum = Ships[leader_shipnum].objnum;
7485 
7486  Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7487 
7488  if (leader_objnum == OBJ_INDEX(objp)) {
7489  return;
7490  }
7491 
7492  aip->guard_wingnum = wingnum;
7493  aip->guard_objnum = leader_objnum;
7494  aip->guard_signature = Objects[leader_objnum].signature;
7495  aip->mode = AIM_GUARD;
7496  aip->submode = AIS_GUARD_STATIC;
7498 
7499  ai_set_guard_vec(objp, &Objects[leader_objnum]);
7500  }
7501 }
7502 
7503 // Make object *objp guard object *other_objp.
7504 // To be called from the goals code.
7505 void ai_set_evade_object(object *objp, object *other_objp)
7506 {
7507  ship *shipp;
7508  ai_info *aip;
7509  int other_objnum;
7510 
7511  Assert(objp->type == OBJ_SHIP);
7512  Assert(objp->instance >= 0);
7513 
7514  shipp = &Ships[objp->instance];
7515 
7516  Assert(shipp->ai_index >= 0);
7517 
7518  aip = &Ai_info[shipp->ai_index];
7519 
7520  other_objnum = OBJ_INDEX(other_objp);
7521  Assert(other_objnum >= 0);
7522 
7523  Assert(other_objnum != Ships[aip->shipnum].objnum); // make sure not targeting self
7524  aip->target_objnum = other_objnum;
7525 
7526  aip->mode = AIM_EVADE;
7527 }
7528 
7529 // Make objp guard other_objp
7530 // If other_objp is a member of a wing, objp will guard that whole wing
7531 // UNLESS objp is also a member of the wing!
7532 void ai_set_guard_object(object *objp, object *other_objp)
7533 {
7534  ship *shipp;
7535  ai_info *aip;
7536  int other_objnum;
7537 
7538  Assert(objp->type == OBJ_SHIP);
7539  Assert(objp->instance >= 0);
7540  Assert(objp != other_objp);
7541 
7542  shipp = &Ships[objp->instance];
7543 
7544  Assert(shipp->ai_index >= 0);
7545 
7546  aip = &Ai_info[shipp->ai_index];
7547  aip->avoid_check_timestamp = timestamp(1);
7548 
7549  // If ship to guard is in a wing, guard that whole wing, unless the appropriate flag has been set
7550  ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7551  if ((other_aip->wing != -1) && (other_aip->wing != aip->wing) && !(The_mission.ai_profile->flags2 & AIPF2_AI_GUARDS_SPECIFIC_SHIP_IN_WING)) {
7552  ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7553  } else {
7554 
7555  other_objnum = OBJ_INDEX(other_objp);
7556 
7557  aip->guard_objnum = other_objnum;
7558  aip->guard_signature = other_objp->signature;
7559  aip->guard_wingnum = -1;
7560 
7561  aip->mode = AIM_GUARD;
7562  aip->submode = AIS_GUARD_STATIC;
7564 
7565  Assert(other_objnum >= 0); // Hmm, bogus object and we need its position for guard_vec.
7566 
7567  ai_set_guard_vec(objp, &Objects[other_objnum]);
7568 
7569  ai_set_goal_maybe_abort_dock(objp, aip);
7571  }
7572 }
7573 
7574 // Update the aspect_locked_time field based on whether enemy is in view cone.
7575 // Also set/clear AIF_SEEK_LOCK.
7576 void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7577 {
7578  float dot_to_enemy;
7579  int num_weapon_types;
7580  int weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7581  ship *shipp;
7582  ship *tshpp;
7583  ship_weapon *swp;
7584  weapon_info *wip;
7585  object *tobjp = &Objects[aip->target_objnum];
7586 
7587  shipp = &Ships[aip->shipnum];
7588  tshpp = NULL;
7589  swp = &shipp->weapons;
7590 
7591  object *aiobjp = &Objects[shipp->objnum];
7592 
7593  // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7594  if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7595  return;
7596  }
7597 
7598  if (tobjp->type == OBJ_SHIP) {
7599  tshpp = &Ships[tobjp->instance];
7600  }
7601 
7602  num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7603 
7605 
7606  if (num_weapon_types && (wip->wi_flags & WIF_LOCKED_HOMING) && !(shipp->flags2 & SF2_NO_SECONDARY_LOCKON)) {
7607  if (dist_to_enemy > 300.0f - MIN(enemy_radius, 100.0f))
7608  aip->ai_flags |= AIF_SEEK_LOCK;
7609  else
7610  aip->ai_flags &= ~AIF_SEEK_LOCK;
7611 
7612  // Update locking information for aspect seeking missiles.
7613  aip->current_target_is_locked = 0;
7614  dot_to_enemy = vm_vec_dot(vec_to_enemy, &aiobjp->orient.vec.fvec);
7615 
7616  float needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); // Replaced MIN_TRACKABLE_DOT with 0.9f
7617  if (dot_to_enemy > needed_dot &&
7618  (wip->wi_flags & WIF_HOMING_ASPECT ||
7619  (wip->wi_flags & WIF_HOMING_JAVELIN &&
7620  (tshpp == NULL ||
7621  ship_get_closest_subsys_in_sight(tshpp, SUBSYSTEM_ENGINE, &aiobjp->pos))))) {
7623  if (aip->aspect_locked_time >= wip->min_lock_time) {
7624  aip->aspect_locked_time = wip->min_lock_time;
7625  aip->current_target_is_locked = 1;
7626  }
7627  } else {
7628  aip->aspect_locked_time -= flFrametime*2;
7629  if (aip->aspect_locked_time < 0.0f)
7630  aip->aspect_locked_time = 0.0f;
7631  }
7632 
7633  } else {
7634  aip->current_target_is_locked = 0;
7635  aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7636  aip->ai_flags &= ~AIF_SEEK_LOCK;
7637  }
7638 
7639 }
7640 
7641 // We're in chase mode and we've recently collided with our target.
7642 // Fly away from it!
7643 void ai_chase_fly_away(object *objp, ai_info *aip)
7644 {
7645  int abort_flag = 0;
7646 
7647  if (aip->ai_flags & AIF_TARGET_COLLISION) {
7648  aip->ai_flags &= ~AIF_TARGET_COLLISION; // Don't process this hit again next frame.
7649  aip->submode = SM_FLY_AWAY; // Focus on avoiding target
7651  }
7652 
7653  if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7654  abort_flag = 1;
7655  }
7656 
7657  if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7659  aip->submode = SM_ATTACK;
7661  } else {
7662  vec3d v2e;
7663  float dot;
7664 
7665  vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7666 
7667  dot = vm_vec_dot(&objp->orient.vec.fvec, &v2e);
7668  if (dot < 0.0f)
7669  accelerate_ship(aip, 1.0f);
7670  else
7671  accelerate_ship(aip, 1.0f - dot);
7672  turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7673  }
7674 }
7675 
7676 // Return bank index of favored secondary weapon.
7677 // Return -1 if nothing favored.
7678 // "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7679 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7680 {
7681  int i;
7682 
7683  for (i=0; i<swp->num_secondary_banks; i++) {
7684  if (swp->secondary_bank_capacity[i] > 0) {
7685  if (swp->secondary_bank_ammo[i] > 0) {
7686  if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7687  return i;
7688  }
7689  }
7690  }
7691  }
7692 
7693  return -1;
7694 }
7695 
7696 // Choose which secondary weapon to fire.
7697 // Note, this is not like ai_select_secondary_weapon(). "choose" means make a choice.
7698 // "select" means execute an order. Get it?
7699 // This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7700 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7701 {
7702  float subsystem_strength = 0.0f;
7703  int is_big_ship, wif_priority1, wif_priority2, wif2_priority1, wif2_priority2;
7704  ship_weapon *swp;
7705  ship_info *esip;
7706 
7707  if ( en_objp->type == OBJ_SHIP ) {
7708  esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7709  } else {
7710  esip = NULL;
7711  }
7712 
7713  swp = &Ships[objp->instance].weapons;
7714 
7715  // AL 3-5-98: do a quick out if the ship has no secondaries
7716  if ( swp->num_secondary_banks <= 0 ) {
7717  swp->current_secondary_bank = -1;
7718  return;
7719  }
7720 
7721  int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7722 
7723  if (preferred_secondary != -1) {
7724  if (swp->current_secondary_bank != preferred_secondary) {
7725  aip->current_target_is_locked = 0;
7726  aip->aspect_locked_time = 0.0f;
7727  swp->current_secondary_bank = preferred_secondary;
7728  }
7730  } else {
7732  if (aip->targeted_subsys) {
7733  subsystem_strength = aip->targeted_subsys->current_hits;
7734  }
7735 
7736  if ( esip ) {
7737  is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7738  } else {
7739  is_big_ship=0;
7740  }
7741 
7742  if (is_big_ship)
7743  {
7744  wif_priority1 = WIF_HUGE;
7746  wif2_priority1 = WIF2_CAPITAL_PLUS;
7747  wif2_priority2 = 0;
7748  }
7749  else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) )
7750  {
7751  wif_priority1 = WIF_BOMBER_PLUS;
7752  wif_priority2 = WIF_HOMING;
7753  wif2_priority1 = 0;
7754  wif2_priority2 = 0;
7755  }
7756  else if (subsystem_strength > 100.0f)
7757  {
7758  wif_priority1 = WIF_PUNCTURE;
7759  wif_priority2 = WIF_HOMING;
7760  wif2_priority1 = 0;
7761  wif2_priority2 = 0;
7762  }
7763  else if ((aip->ai_profile_flags & AIPF_SMART_SECONDARY_WEAPON_SELECTION) && (en_objp->type == OBJ_ASTEROID)) //prefer dumbfires if its an asteroid
7764  {
7765  wif_priority1 = 0;
7766  wif_priority2 = 0;
7767  wif2_priority1 = 0;
7768  wif2_priority2 = 0;
7769  }
7770  else
7771  {
7772  wif_priority1 = WIF_HOMING;
7773  wif_priority2 = 0;
7774  wif2_priority1 = 0;
7775  wif2_priority2 = 0;
7776  }
7777 
7778  ai_select_secondary_weapon(objp, swp, wif_priority1, wif_priority2, wif2_priority1, wif2_priority2);
7779  }
7780 }
7781 
7785 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip, bool burst)
7786 {
7787  float t;
7788  if (burst) {
7789  t = swip->burst_delay;
7790  } else {
7791  t = swip->fire_wait; // Base delay for this weapon.
7792  }
7793  if (shipp->team == Player_ship->team) {
7795  } else {
7797  }
7798 
7799  if (aip->ai_class_autoscale)
7800  t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7801 
7802  t *= frand_range(0.8f, 1.2f);
7803 
7804  // For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7805  if (t < 5.0f)
7806  if (frand() < 0.5f)
7807  t = t * 2.0f + 2.0f;
7808 
7809  return t;
7810 }
7811 
7812 
7813 void ai_chase_big_approach_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
7814 {
7815  float dist_to_goal;
7816 
7817  // head straight toward him and maybe circle later
7818  vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7819 
7820  // get distance to goal
7821  dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7822 
7823  // set accel
7824  if (dist_to_goal > 400.0f) {
7825  *accel = 1.0f;
7826  } else {
7827  *accel = dist_to_goal/400.0f;
7828  }
7829 }
7830 
7831 void ai_chase_big_circle_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
7832 {
7833  get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7834 
7835  *accel = 1.0f;
7836 }
7837 
7841 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vec3d *horz_vec_to_target, float *desired_separation, float *cur_separation)
7842 {
7843  float temp, r_target, r_attacker;
7844  float perp_dist;
7845  vec3d vec_to_target;
7846 
7847  // get parameters of ships (as cylinders - radius and height)
7848  polymodel *pm = model_get(Ship_info[Ships[attack_objp->instance].ship_info_index].model_num);
7849 
7850  // get radius of attacker (for rotations about forward)
7851  temp = MAX(pm->maxs.xyz.x, pm->maxs.xyz.y);
7852  r_attacker = MAX(-pm->mins.xyz.x, -pm->mins.xyz.y);
7853  r_attacker = MAX(temp, r_attacker);
7854 
7855  // get radius of target (for rotations about forward)
7856  temp = MAX(pm->maxs.xyz.x, pm->maxs.xyz.y);
7857  r_target = MAX(-pm->mins.xyz.x, -pm->mins.xyz.y);
7858  r_target = MAX(temp, r_target);
7859 
7860  // find separation between cylinders [if parallel]
7861  vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7862 
7863  // find the distance between centers along forward direction of ships
7864  perp_dist = vm_vec_dot(&vec_to_target, &target_objp->orient.vec.fvec);
7865 
7866  // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7867  vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.vec.fvec, -perp_dist);
7868  *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7869 
7870  // choose "optimal" separation of 1000 + r_target + r_attacker
7871  *desired_separation = 1000 + r_target + r_attacker;
7872 }
7873 
7874 void ai_chase_big_parallel_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
7875 {
7876  int opposing;
7877  float temp, r_target, r_attacker;
7878  float separation, optimal_separation;
7879  vec3d horz_vec_to_target;
7880 
7881  // get parameters of ships (as cylinders - radius and height)
7882  polymodel *pm = model_get(Ship_info[Ships[attack_objp->instance].ship_info_index].model_num);
7883 
7884  // get radius of attacker (for rotations about forward)
7885  temp = MAX(pm->maxs.xyz.x, pm->maxs.xyz.y);
7886  r_attacker = MAX(-pm->mins.xyz.x, -pm->mins.xyz.y);
7887  r_attacker = MAX(temp, r_attacker);
7888 
7889  // get radius of target (for rotations about forward)
7890  temp = MAX(pm->maxs.xyz.x, pm->maxs.xyz.y);
7891  r_target = MAX(-pm->mins.xyz.x, -pm->mins.xyz.y);
7892  r_target = MAX(temp, r_target);
7893 
7894  // are we opposing (only when other ship is not moving)
7895  opposing = ( vm_vec_dot(&attack_objp->orient.vec.fvec, &target_objp->orient.vec.fvec) < 0 );
7896 
7897  ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7898 
7899  // choose dist (2000) so that we don't bash
7900  float dist = 2000;
7901  if (opposing) {
7902  dist = - dist;
7903  }
7904 
7905  // set the goal pos as dist forward from target along target forward
7906  vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.vec.fvec, dist);
7907  // then add horizontal separation
7908  vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7909 
7910  // find the distance between centers along forward direction of ships
7911  vec3d vec_to_target;
7912  vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7913  float perp_dist = vm_vec_dot(&vec_to_target, &target_objp->orient.vec.fvec);
7914 
7915  float match_accel = 0.0f;
7916  float length_scale = attack_objp->radius;
7917 
7918  if (Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z != 0.0f) {
7919  match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7920  }
7921 
7922  // if we're heading toward enemy ship, we want to keep going if we're ahead
7923  if (opposing) {
7924  perp_dist = -perp_dist;
7925  }
7926 
7927  if (perp_dist > 0) {
7928  // falling behind, so speed up
7929  *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7930  } else {
7931  // up in front, so slow down
7932  *accel = match_accel - match_accel / length_scale * -perp_dist;
7933  *accel = MAX(0.0f, *accel);
7934  }
7935 
7936 }
7937 
7938 // Return *goal_pos for one cruiser to attack another (big ship).
7939 // Choose point fairly nearby that is not occupied by another cruiser.
7940 void ai_cruiser_chase_set_goal_pos(vec3d *goal_pos, object *pl_objp, object *en_objp)
7941 {
7942  ai_info *aip;
7943 
7944  aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7945  float accel;
7946 
7947  switch (aip->submode) {
7948  case SM_BIG_APPROACH:
7949  // do approach stuff;
7950  ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7951  break;
7952 
7953  case SM_BIG_CIRCLE:
7954  // do circle stuff
7955  ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7956  break;
7957 
7958  case SM_BIG_PARALLEL:
7959  // do parallel stuff
7960  ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7961  break;
7962  }
7963 }
7964 
7966 {
7967  ship *shipp = &Ships[Pl_objp->instance];
7968  ship *eshipp = &Ships[En_objp->instance];
7969  ai_info *aip = &Ai_info[shipp->ai_index];
7970 
7971  // mission sm3-08, sathanos chasing collosus
7972  if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7973  if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7974  // Changed so all big ships attacking the Colossus will not do the chase code.
7975  // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7976  ai_clear_ship_goals( aip );
7977  aip->mode = AIM_NONE;
7978  return 1;
7979  }
7980  }
7981 
7982  return 0;
7983 }
7984 
7985 // Make a big ship pursue another big ship.
7986 // (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7988 {
7989  ship *shipp = &Ships[Pl_objp->instance];
7990  ai_info *aip = &Ai_info[shipp->ai_index];
7991 
7992  if (En_objp->type != OBJ_SHIP) {
7993  Int3();
7994  return;
7995  }
7996 
7997  if (En_objp->instance < 0) {
7998  Int3();
7999  return;
8000  }
8001 
8002  vec3d goal_pos;
8003  float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
8004 
8005  // kamikaze - ram and explode
8006  if (aip->ai_flags & AIF_KAMIKAZE) {
8007  ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8008  accelerate_ship(aip, 1.0f);
8009  }
8010 
8011  // really track down and chase
8012  else {
8013  // check valid submode
8014  Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
8015 
8016  // just entering, approach enemy ship
8017  if (aip->submode == SM_ATTACK) {
8018  aip->submode = SM_BIG_APPROACH;
8020  }
8021 
8022  // desired accel
8023  float accel = 0.0f;
8024  vec3d *rvecp = NULL;
8025 
8026  switch (aip->submode) {
8027  case SM_BIG_APPROACH:
8028  // do approach stuff;
8029  ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
8030  // maybe set rvec
8031  break;
8032 
8033  case SM_BIG_CIRCLE:
8034  // do circle stuff
8035  ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
8036  // maybe set rvec
8037  break;
8038 
8039  case SM_BIG_PARALLEL:
8040  // do parallel stuff
8041  ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
8042  //maybe set rvec
8043  break;
8044  }
8045 
8046  // now move as desired
8047  ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
8048  accelerate_ship(aip, accel);
8049 
8050  // maybe switch to new mode
8051  vec3d vec_to_enemy;
8052  float dist_to_enemy;
8053  int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
8054  vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
8055  dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
8056 
8057  switch (aip->submode) {
8058  case SM_BIG_APPROACH:
8059  if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
8060  // moving
8061  if (moving) {
8062  // if within 90 degrees of en forward, go into parallel, otherwise circle
8063  if ( vm_vec_dot(&En_objp->orient.vec.fvec, &Pl_objp->orient.vec.fvec) > 0 ) {
8064  aip->submode = SM_BIG_PARALLEL;
8066  }
8067  }
8068 
8069  // otherwise cirle
8070  if ( !maybe_hack_cruiser_chase_abort() ) {
8071  aip->submode = SM_BIG_CIRCLE;
8073  }
8074  }
8075  break;
8076 
8077  case SM_BIG_CIRCLE:
8078  // moving
8079  if (moving) {
8080  vec3d temp;
8081  float desired_sep, cur_sep;
8082  // we're behind the enemy ship
8083  if (vm_vec_dot(&vec_to_enemy, &En_objp->orient.vec.fvec) > 0) {
8084  // and we're turning toward the enemy
8085  if (vm_vec_dot(&En_objp->orient.vec.fvec, &Pl_objp->orient.vec.fvec) > 0) {
8086  // get separation
8087  ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
8088  // and the separation is > 0.9 desired
8089  if (cur_sep > (0.9f * desired_sep)) {
8090  aip->submode = SM_BIG_PARALLEL;
8092  }
8093  }
8094  }
8095  } else {
8096  // still
8097  vec3d temp;
8098  float desired_sep, cur_sep;
8099  // we're behind the enemy ship
8100  if (vm_vec_dot(&vec_to_enemy, &En_objp->orient.vec.fvec) > 0) {
8101  // and we're turning toward the enemy
8102  if (vm_vec_dot(&En_objp->orient.vec.fvec, &Pl_objp->orient.vec.fvec) > 0) {
8103  // get separation
8104  ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
8105  // and the separation is > 0.9 desired
8106  if (cur_sep > (0.9f * desired_sep)) {
8107  aip->submode = SM_BIG_PARALLEL;
8109  }
8110  }
8111  }
8112  // in front of ship
8113  else {
8114  // and we're turning toward the enemy
8115  if (vm_vec_dot(&En_objp->orient.vec.fvec, &Pl_objp->orient.vec.fvec) < 0) {
8116  // get separation
8117  ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
8118  // and the separation is > 0.9 desired
8119  if (cur_sep > (0.9f * desired_sep)) {
8120  aip->submode = SM_BIG_PARALLEL;
8122  }
8123  }
8124  }
8125  }
8126  break;
8127 
8128  case SM_BIG_PARALLEL:
8129  // we're opposing
8130  if ( vm_vec_dot(&Pl_objp->orient.vec.fvec, &En_objp->orient.vec.fvec) < 0 ) {
8131  // and the other ship is moving
8132  if (moving) {
8133  // and we no longer overlap
8134  if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
8135  aip->submode = SM_BIG_APPROACH;
8137  }
8138  }
8139  }
8140  break;
8141  }
8142  }
8143 }
8144 
8148 void ai_chase()
8149 {
8150  float dist_to_enemy;
8151  float dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
8152  vec3d player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
8153  ship *shipp = &Ships[Pl_objp->instance];
8154  ship_info *sip = &Ship_info[shipp->ship_info_index];
8155  ship_weapon *swp = &shipp->weapons;
8156  ai_info *aip = &Ai_info[shipp->ai_index];
8157  int enemy_sip_flags, enemy_shipp_flags2;
8158  int has_fired = -1;
8159 
8160  if (aip->mode != AIM_CHASE) {
8161  Int3();
8162  }
8163 
8164  // by default we try to chase anything
8165  bool go_after_it = true;
8166 
8167  if ( (sip->class_type > -1) && (En_objp->type == OBJ_SHIP) )
8168  {
8169  // default to not chasing for ships
8170  go_after_it = false;
8171  ship_info *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8172  if (esip->class_type > -1)
8173  {
8174  ship_type_info *stp = &Ship_types[sip->class_type];
8175  size_t ap_size = stp->ai_actively_pursues.size();
8176  for(size_t i = 0; i < ap_size; i++)
8177  {
8178  if(stp->ai_actively_pursues[i] == esip->class_type) {
8179  go_after_it = true;
8180  break;
8181  }
8182  }
8183  }
8184  else
8185  {
8186  // if there was no class type then assume we can go after it ...
8187  go_after_it = true;
8188  // ... but also log this in debug so it doesn't go unchecked (NOTE that this can completely flood a debug log!)
8189  mprintf(("AI-WARNING: No class_type specified for '%s', assuming that it's ok to chase!\n", esip->name));
8190  }
8191  }
8192 
8193  //WMC - Guess we do need this
8194  if (!go_after_it) {
8195  aip->mode = AIM_NONE;
8196  return;
8197  }
8198 
8199  if (sip->class_type > -1 && (Ship_types[sip->class_type].ai_bools & STI_AI_ATTEMPT_BROADSIDE)) {
8200  ai_cruiser_chase();
8201  return;
8202  }
8203 
8204  Assert( En_objp != NULL );
8205 
8206  if ( En_objp->type == OBJ_SHIP ) {
8207  enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
8208  enemy_shipp_flags2 = Ships[En_objp->instance].flags2;
8209  } else {
8210  enemy_sip_flags = 0;
8211  enemy_shipp_flags2 = 0;
8212  }
8213 
8214  if ( enemy_sip_flags > 0 ) {
8215  if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
8216  ai_big_chase();
8217  return;
8218  }
8219  }
8220 
8221  // If collided with target_objnum last frame, avoid that ship.
8222  // This should prevent the embarrassing behavior of ships getting stuck on each other
8223  // as if they were magnetically attracted. -- MK, 11/13/97.
8224  if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
8225  ai_chase_fly_away(Pl_objp, aip);
8226  return;
8227  }
8228 
8229  ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
8230  dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
8231  vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
8232 
8233  //Enemy position for the purpose of aiming is already calculated differently, do it explicitly here
8234  ai_update_aim(aip);
8235 
8236  vm_vec_normalize(&real_vec_to_enemy);
8237 
8238  real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.vec.fvec);
8239 
8240  int is_stealthy_ship = 0;
8241  if ( (enemy_sip_flags > 0) && (enemy_shipp_flags2 & SF2_STEALTH) ) {
8242  if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
8243  is_stealthy_ship = 1;
8244  }
8245  }
8246 
8247  // Can only acquire lock on a target that isn't hidden from sensors
8248  if ( En_objp->type == OBJ_SHIP && !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
8249  update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
8250  } else {
8251  aip->current_target_is_locked = 0;
8252  aip->ai_flags &= ~AIF_SEEK_LOCK;
8253  }
8254 
8255  // If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8256  // If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8257  if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f)) {
8258  predicted_enemy_pos = enemy_pos;
8259  } else if (aip->ai_flags & AIF_SEEK_LOCK) {
8260  set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip); // Set G_fire_pos
8261  predicted_enemy_pos = enemy_pos;
8262  G_predicted_pos = predicted_enemy_pos;
8263  } else {
8264  // Set predicted_enemy_pos.
8265  // See if attacking a subsystem.
8266  if (aip->targeted_subsys != NULL) {
8267  Assert(En_objp->type == OBJ_SHIP);
8269  if (aip->targeted_subsys != NULL) {
8270  get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8271  predicted_enemy_pos = enemy_pos;
8272  predicted_vec_to_enemy = real_vec_to_enemy;
8273  } else {
8274  set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip);
8275  set_target_objnum(aip, -1);
8276  }
8277 
8278  } else {
8279  set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip);
8280  }
8281  } else {
8282  set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip);
8283  }
8284  }
8285 
8286  vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8287 
8288  vm_vec_normalize(&predicted_vec_to_enemy);
8289 
8290  dot_to_enemy = vm_vec_dot(&Pl_objp->orient.vec.fvec, &predicted_vec_to_enemy);
8291  dot_from_enemy= - vm_vec_dot(&En_objp->orient.vec.fvec, &real_vec_to_enemy);
8292 
8293  // Set turn and acceleration based on submode.
8294  switch (aip->submode) {
8295  case SM_CONTINUOUS_TURN:
8296  ai_chase_ct();
8297  break;
8298 
8299  case SM_STEALTH_FIND:
8300  ai_stealth_find();
8301  break;
8302 
8303  case SM_STEALTH_SWEEP:
8304  ai_stealth_sweep();
8305  break;
8306 
8307  case SM_ATTACK:
8308  case SM_SUPER_ATTACK:
8309  case SM_ATTACK_FOREVER:
8310  case AIS_CHASE_GLIDEATTACK:
8312  if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8313  if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8314  return;
8315  }
8316 
8317  ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy, sip->model_num);
8318  break;
8319 
8320  case SM_EVADE_SQUIGGLE:
8321  ai_chase_es(aip, sip);
8322  break;
8323 
8324  case SM_EVADE_BRAKE:
8325  ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8326  break;
8327 
8328  case SM_EVADE:
8329  evade_ship();
8330  break;
8331 
8332  case SM_AVOID:
8333  avoid_ship();
8334  break;
8335 
8336  case SM_GET_BEHIND:
8337  get_behind_ship(aip, sip, dist_to_enemy);
8338  break;
8339 
8340  case SM_GET_AWAY: // Used to get away from opponent to prevent endless circling.
8341  ai_chase_ga(aip, sip);
8342  break;
8343 
8344  case SM_EVADE_WEAPON:
8345  evade_weapon();
8346  break;
8347 
8348  default:
8350  aip->submode = SM_ATTACK;
8352  }
8353 
8354  //Maybe apply random sidethrust, depending on the current submode
8355  //The following are valid targets for random sidethrust (circle strafe uses it too, but that is handled separately)
8356  if (aip->submode == SM_ATTACK ||
8357  aip->submode == SM_SUPER_ATTACK ||
8358  aip->submode == SM_EVADE_SQUIGGLE ||
8359  aip->submode == SM_EVADE ||
8360  aip->submode == SM_GET_AWAY ||
8362  {
8363  //Re-roll for random sidethrust every 2 seconds
8364  //Also, only do this if we've recently been hit or are in current primary weapon range of target
8365  float current_weapon_range = Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].weapon_range;
8367  ((Missiontime - aip->last_hit_time < i2f(5) && aip->hitter_objnum >= 0) || dist_to_enemy < current_weapon_range))
8368  {
8369  do_random_sidethrust(aip, sip);
8370  }
8371  }
8372 
8373  // Maybe choose a new submode.
8374  if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8375  // If a very long time since attacked, attack no matter what!
8376  if (Missiontime - aip->last_attack_time > i2f(6)) {
8377  if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSUIT) ) {
8378  aip->submode = SM_SUPER_ATTACK;
8381  }
8382  }
8383 
8384  //SUSHI: Alternate stalemate dection method: if nobody has hit each other for a while
8385  //(and we've been near that whole time), shake things up somehow
8386  //Only do this if stalemate time threshold > 0
8387  if (aip->ai_stalemate_time_thresh > 0.0f &&
8391  (dot_to_enemy < 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)))
8392  {
8393  //Every second, evaluate whether or not to break stalemate. The more patient the ship, the less likely this is.
8394  if (static_randf((Missiontime + static_rand(aip->shipnum)) >> 16) > (aip->ai_patience * .01))
8395  {
8396  if ((sip->can_glide == true) && (frand() < aip->ai_glide_attack_percent)) {
8397  //Maybe use glide attack
8401  aip->time_enemy_near = 0.0f;
8402  } else {
8403  //Otherwise, try to get away
8404  aip->submode = SM_GET_AWAY;
8407  aip->time_enemy_near = 0.0f;
8408  }
8409  }
8410  }
8411 
8412  // If a collision is expected, pull out!
8413  // If enemy is pointing away and moving a bit, don't worry about collision detection.
8414  if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8415  //If we're in circle strafe mode, don't worry about colliding with the target
8416  if (aip->submode != AIS_CHASE_CIRCLESTRAFE && might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8417  if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8418  accelerate_ship(aip, -1.0f);
8419  } else {
8420  aip->submode = SM_AVOID;
8422  }
8423  }
8424  }
8425  }
8426 
8427  switch (aip->submode) {
8428  case SM_CONTINUOUS_TURN:
8429  if (Missiontime - aip->submode_start_time > i2f(3)) {
8431  aip->submode = SM_ATTACK;
8433  }
8434  break;
8435 
8436  case SM_ATTACK:
8437  // if target is stealth and stealth not visible, then enter stealth find mode
8438  if ( (aip->ai_flags & AIF_STEALTH_PURSUIT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_NOT_IN_FRUSTUM) ) {
8439  aip->submode = SM_STEALTH_FIND;
8441  aip->submode_parm0 = SM_SF_AHEAD;
8442  } else if (dist_to_enemy < CIRCLE_STRAFE_MAX_DIST + En_objp->radius &&
8443  (En_objp->phys_info.speed < MAX(sip->max_vel.xyz.x, sip->max_vel.xyz.y) * 1.5f) &&
8447  aip->last_attack_time = Missiontime;
8448  } else if (ai_near_full_strength(Pl_objp) && (Missiontime - aip->last_hit_target_time > i2f(3)) && (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.5f)) {
8449  aip->submode = SM_SUPER_ATTACK;
8452  } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8453  (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8454  (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8455  aip->submode = SM_GET_AWAY;
8458  } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8459  && (dot_to_enemy < dot_from_enemy)
8460  && (En_objp->phys_info.speed > 15.0f)
8461  && (dist_to_enemy < 200.0f)
8462  && (dist_to_enemy > 50.0f)
8463  && (dot_to_enemy < 0.1f)
8464  && (Missiontime - aip->submode_start_time > i2f(2))) {
8465  aip->submode = SM_EVADE_BRAKE;
8467  } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8468  aip->submode = SM_GET_BEHIND;
8470  } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (dist_to_enemy < 150.0f) && (dot_from_enemy > dot_to_enemy + 0.5f + aip->ai_courage*.002)) {
8471  float get_away_chance = (aip->ai_get_away_chance == FLT_MIN)
8473  : aip->ai_get_away_chance;
8474  if ((Missiontime - aip->last_hit_target_time > i2f(5)) && (frand() < get_away_chance)) {
8475  aip->submode = SM_GET_AWAY;
8478  } else {
8479  aip->submode = SM_EVADE_SQUIGGLE;
8481  }
8482  } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8483  if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8484  if (frand() > 0.5f) {
8485  aip->submode = SM_CONTINUOUS_TURN;
8487  aip->submode_parm0 = myrand() & 0x0f;
8488  } else {
8489  aip->submode = SM_EVADE;
8491  }
8492  } else {
8494  }
8495  }
8496 
8498 
8499  break;
8500 
8501  case SM_EVADE_SQUIGGLE:
8502  if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8503  if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8504  aip->submode = SM_EVADE_BRAKE;
8505  } else if ((Pl_objp->phys_info.speed >= Pl_objp->phys_info.max_vel.xyz.z / 2.0f) && (sip->can_glide == true) && (frand() < aip->ai_glide_attack_percent)) {
8508  } else {
8510  aip->submode = SM_ATTACK;
8511  }
8513  }
8514  break;
8515 
8516  case SM_EVADE_BRAKE:
8517  if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8518  aip->submode = SM_AVOID;
8520  } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8522  aip->submode = SM_ATTACK;
8524  } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8526  aip->submode = SM_ATTACK;
8528  }
8529  break;
8530 
8531  case SM_EVADE:
8532  // Modified by MK on 5/5/97 to keep trying to regain attack mode. It's what a human would do.
8533  if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8535  aip->submode = SM_EVADE_BRAKE;
8537  } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8538  && (Missiontime > aip->submode_start_time + i2f(1)))
8539  || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8541  aip->submode = SM_ATTACK;
8543  } else if (Missiontime - aip->submode_start_time > i2f(2))
8544  if (dot_from_enemy > 0.8f) {
8545  aip->submode = SM_EVADE_SQUIGGLE;
8547  }
8548 
8549  break;
8550 
8551  case SM_SUPER_ATTACK:
8552  // if stealth and invisible, enter stealth find mode
8553  if ( (aip->ai_flags & AIF_STEALTH_PURSUIT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_NOT_IN_FRUSTUM) ) {
8554  aip->submode = SM_STEALTH_FIND;
8556  aip->submode_parm0 = SM_SF_AHEAD;
8557  } else if (dist_to_enemy < CIRCLE_STRAFE_MAX_DIST + En_objp->radius &&
8558  (En_objp->phys_info.speed < MAX(sip->max_vel.xyz.x, sip->max_vel.xyz.y) * 1.5f) &&
8562  aip->last_attack_time = Missiontime;
8563  } else if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.8f) && (enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > i2f(5) )) {
8564  aip->ai_flags &= ~AIF_ATTACK_SLOWLY; // Just in case, clear here.
8565 
8566  float get_away_chance = (aip->ai_get_away_chance == FLT_MIN)
8568  : aip->ai_get_away_chance;
8569 
8570  switch (myrand() % 5) {
8571  case 0:
8572  aip->submode = SM_CONTINUOUS_TURN;
8574  break;
8575  case 1:
8576  aip->submode_start_time = Missiontime; // Stay in super attack mode
8577  break;
8578  case 2:
8579  case 3:
8580  if (frand() < (float) 0.5f * get_away_chance) {
8581  aip->submode = SM_GET_AWAY;
8583  } else {
8584  aip->submode = SM_EVADE;
8586  }
8587  break;
8588  case 4:
8589  if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) { // Less likely to GET_AWAY at lower skill levels.
8590  aip->submode = SM_EVADE;
8592  } else {
8593  aip->submode = SM_GET_AWAY;
8595  }
8596  break;
8597  default:
8598  Int3(); // Impossible!
8599  }
8600  }
8601 
8603 
8604  break;
8605 
8606  case SM_AVOID:
8607  if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8609  } else if (Missiontime - aip->submode_start_time > i2f(1)/2) {
8610  if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8612  } else {
8613  aip->submode = SM_GET_BEHIND;
8615  }
8616  }
8617 
8618  break;
8619 
8620  case SM_GET_BEHIND:
8621  if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8622  aip->submode = SM_ATTACK;
8625  }
8626  break;
8627 
8628  case SM_GET_AWAY:
8629  if (Missiontime - aip->submode_start_time > i2f(2)) {
8630  float rand_dist;
8631 
8632  rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f; // Some value in 200..500
8633  if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8634  //Sometimes use a glide attack instead (if we can)
8635  if ((sip->can_glide == true) && (frand() < aip->ai_glide_attack_percent)) {
8637  }
8638  else {
8639  aip->ai_flags |= AIF_ATTACK_SLOWLY;
8640  aip->submode = SM_ATTACK;
8641  }
8642 
8644  aip->time_enemy_in_range = 2.0f; // Cheat. Presumably if they were running away from you, they were monitoring you!
8646  }
8647  }
8648  break;
8649 
8650  case SM_EVADE_WEAPON:
8651  if (aip->danger_weapon_objnum == -1) {
8652  aip->submode = SM_ATTACK;
8655  }
8656  break;
8657 
8658  // Either change to SM_ATTACK or AIM_FIND_STEALTH
8659  case SM_STEALTH_FIND:
8660  // if time > 5 sec change mode to sweep
8661  if ( !(aip->ai_flags & AIF_STEALTH_PURSUIT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_IN_FRUSTUM) ) {
8662  aip->submode = SM_ATTACK;
8665  // sweep if I can't find in 5 sec or bail from find
8666  } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8667  // begin sweep mode
8668  aip->submode = SM_STEALTH_SWEEP;
8672  }
8673  break;
8674 
8675  case SM_STEALTH_SWEEP:
8676  if ( !(aip->ai_flags & AIF_STEALTH_PURSUIT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_IN_FRUSTUM) ) {
8677  aip->submode = SM_ATTACK;
8680  } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8681  // go back to find mode
8682  aip->submode = SM_STEALTH_FIND;
8684  aip->submode_parm0 = SM_SF_AHEAD;
8685  } else if ( aip->submode_parm0 == SM_SS_DONE ) {
8686  // set target objnum = -1
8687  set_target_objnum(aip, -1);
8688 
8689  // set submode to attack
8690  aip->submode = SM_ATTACK;
8693  }
8694  break;
8695 
8696  case SM_ATTACK_FOREVER: // Engines blown, just attack.
8697  break;
8698 
8699  case AIS_CHASE_GLIDEATTACK:
8700  //Glide attack lasts at least as long as it takes to turn around and fire for a couple seconds.
8701  if (Missiontime - aip->submode_start_time > i2f((int)(sip->rotation_time.xyz.x) + 4 + static_rand_range(Missiontime >> 19, 0, 3))) {
8702  aip->submode = SM_ATTACK;
8705  }
8707  break;
8708 
8710  //Break out of circle strafe if the target is too far, moving too fast, or we've been doing this for a while
8711  if ((dist_to_enemy > CIRCLE_STRAFE_MAX_DIST + En_objp->radius) ||
8712  (Missiontime - aip->submode_start_time > i2f(8)) ||
8713  (En_objp->phys_info.speed > MAX(sip->max_vel.xyz.x, sip->max_vel.xyz.y) * 1.5)) {
8714  aip->submode = SM_ATTACK;
8717  }
8719  break;
8720 
8721  default:
8722  aip->submode = SM_ATTACK;
8725  }
8726 
8727 
8728  //Update time enemy near
8729  //Ignore for stealth ships that can't be seen
8730  //If we're trying to get away, recent counter
8731  //Only do this if stalemate distance threshold > 0
8732  if (aip->ai_stalemate_dist_thresh > 0.0f &&
8733  dist_to_enemy < aip->ai_stalemate_dist_thresh &&
8734  aip->submode != SM_GET_AWAY && aip->submode != AIS_CHASE_GLIDEATTACK && aip->submode != SM_FLY_AWAY &&
8735  (!(aip->ai_flags & AIF_STEALTH_PURSUIT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_IN_FRUSTUM)))
8736  {
8737  aip->time_enemy_near += flFrametime;
8738  }
8739  else
8740  {
8741  aip->time_enemy_near *= (1.0f - flFrametime);
8742  if (aip->time_enemy_near < 0.0f)
8743  aip->time_enemy_near = 0.0f;
8744  }
8745 
8746  // Maybe fire primary weapon and update time_enemy_in_range
8747  if (aip->mode != AIM_EVADE) {
8748  if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)) {
8750 
8751  // Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8752  // and also the size of the target relative to distance to target.
8753  if (dot_to_enemy > MAX(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/MAX(1.0f,dist_to_enemy))) {
8754 
8755  ship *temp_shipp;
8756  ship_weapon *tswp;
8757 
8758  temp_shipp = &Ships[Pl_objp->instance];
8759  tswp = &temp_shipp->weapons;
8760  if ( tswp->num_primary_banks > 0 ) {
8761  float scale;
8764 
8765  // Less likely to fire if far away and moving.
8766  scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8767  if (scale > 0.6f)
8768  scale = (scale - 0.6f) * 1.5f;
8769  else
8770  scale = 0.0f;
8771  if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8772  if(ai_fire_primary_weapon(Pl_objp) == 1){
8773  has_fired = 1;
8774  }else{
8775  has_fired = -1;
8776  }
8777  }
8778  }
8779 
8780  if ( tswp->num_secondary_banks > 0) {
8781 
8782  // Don't fire secondaries at a protected ship.
8783  if (!(En_objp->flags & OF_PROTECTED)) {
8784  ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8785  int current_bank = tswp->current_secondary_bank;
8786 
8787  if (current_bank > -1) {
8788  weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]];
8789  if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8790  if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8791  swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8792  }
8793  }
8794 
8795  if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8796  if (current_bank >= 0) {
8797  float firing_range;
8798 
8799  if (swip->wi_flags2 & WIF2_LOCAL_SSM)
8800  firing_range=swip->lssm_lock_range; //that should be enough
8801  else if (swip->wi_flags & WIF_BOMB)
8802  firing_range = MIN((swip->max_speed * swip->lifetime * 0.75f), swip->weapon_range);
8803  else
8804  {
8805  float secondary_range_mult = (aip->ai_secondary_range_mult == FLT_MIN)
8806  ? (float)(Game_skill_level + 1 + (3 * aip->ai_class/(Num_ai_classes - 1)))/NUM_SKILL_LEVELS
8807  : aip->ai_secondary_range_mult;
8808 
8809  firing_range = MIN((swip->max_speed * swip->lifetime * secondary_range_mult), swip->weapon_range);
8810  }
8811 
8812 
8813  // reduce firing range in nebula
8814  extern int Nebula_sec_range;
8816  firing_range *= 0.8f;
8817  }
8818 
8819  // If firing a spawn weapon, distance doesn't matter.
8820  int spawn_fire = 0;
8821 
8822  if (swip->wi_flags & WIF_SPAWN) {
8823  int count;
8824 
8825  count = num_nearby_fighters(iff_get_attackee_mask(obj_team(Pl_objp)), &Pl_objp->pos, 1000.0f);
8826 
8827  if (count > 3)
8828  spawn_fire = 1;
8829  else if (count >= 1) {
8830  float hull_percent = Pl_objp->hull_strength/temp_shipp->ship_max_hull_strength;
8831 
8832  if (hull_percent < 0.01f)
8833  hull_percent = 0.01f;
8834 
8835  if (frand() < 0.25f/(30.0f*hull_percent) * count) // With timestamp below, this means could fire in 30 seconds if one enemy.
8836  spawn_fire = 1;
8837  }
8838  }
8839 
8840  if (spawn_fire || (dist_to_enemy < firing_range)) {
8841  if (ai_fire_secondary_weapon(Pl_objp)) {
8842  // Only if weapon was fired do we specify time until next fire. If not fired, done in ai_fire_secondary...
8843  float t;
8844  int current_bank_adjusted = MAX_SHIP_PRIMARY_BANKS + current_bank;
8845 
8846  if ((aip->ai_flags & AIF_UNLOAD_SECONDARIES) || (swip->burst_flags & WBF_FAST_FIRING)) {
8847  if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
8848  t = swip->burst_delay;
8849  swp->burst_counter[current_bank_adjusted]++;
8850  } else {
8851  t = swip->fire_wait;
8852  if ((swip->burst_shots > 0) && (swip->burst_flags & WBF_RANDOM_LENGTH)) {
8853  swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
8854  } else {
8855  swp->burst_counter[current_bank_adjusted] = 0;
8856  }
8857  }
8858  } else {
8859  if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
8860  t = set_secondary_fire_delay(aip, temp_shipp, swip, true);
8861  swp->burst_counter[current_bank_adjusted]++;
8862  } else {
8863  t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
8864  if ((swip->burst_shots > 0) && (swip->burst_flags & WBF_RANDOM_LENGTH)) {
8865  swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
8866  } else {
8867  swp->burst_counter[current_bank_adjusted] = 0;
8868  }
8869  }
8870  }
8871  swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8872  }
8873  } else {
8874  swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8875  }
8876  }
8877  }
8878  }
8879  }
8880  }
8881  }
8882  } else {
8883  if (flFrametime < 1.0f)
8884  aip->time_enemy_in_range *= (1.0f - flFrametime);
8885  else
8886  aip->time_enemy_in_range = 0;
8887  }
8888  } else {
8889  if (flFrametime < 1.0f)
8890  aip->time_enemy_in_range *= (1.0f - flFrametime);
8891  else
8892  aip->time_enemy_in_range = 0;
8893  }
8894 
8895  if(has_fired == -1){
8896  ship_stop_fire_primary(Pl_objp);
8897  }
8898 
8899 }
8900 
8905 float dock_move_towards_point(object *objp, vec3d *start, vec3d *finish, float speed_scale, float other_obj_speed = 0.0f, rotating_dockpoint_info *rdinfo = NULL)
8906 {
8907  physics_info *pi = &objp->phys_info;
8908  float dist; // dist to goal
8909  vec3d v2g; // vector to goal
8910 
8911  dist = vm_vec_dist_quick(start, finish);
8912  if (dist > 0.0f) {
8913  float speed;
8914 
8915  dist = vm_vec_normalized_dir(&v2g, finish, start);
8916  speed = fl_sqrt(dist) * speed_scale;
8917 
8918  // Goober5000 - if we're on a rotating submodel and we're rotating with it, adjust velocity for rotation
8919  if (rdinfo && (rdinfo->submodel >= 0))
8920  {
8921  speed *= 1.25f;
8922 
8923  switch (rdinfo->dock_mode)
8924  {
8925  case DOA_APPROACH:
8926  // ramp submodel's linear velocity according to distance
8927  speed += (rdinfo->submodel_r * rdinfo->submodel_w) / (dist);
8928  break;
8929 
8930  case DOA_DOCK:
8931  case DOA_UNDOCK_1:
8932  // use docker's linear velocity and don't ramp
8933  speed += (vm_vec_dist(&rdinfo->submodel_pos, &objp->pos) * rdinfo->submodel_w);
8934  break;
8935  }
8936  }
8937 
8938  if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8939  speed += other_obj_speed;
8940  else
8941  speed += MAX_REPAIR_SPEED*0.75f;
8942 
8943  vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8944  } else
8945  vm_vec_zero(&pi->desired_vel);
8946 
8947  return dist;
8948 }
8949 
8950 // Set the orientation in the global reference frame for an object to attain
8951 // to dock with another object. Resultant global matrix returned in dom.
8952 // Revised by Goober5000
8953 void set_goal_dock_orient(matrix *dom, vec3d *docker_p0, vec3d *docker_p1, vec3d *docker_p0_norm, matrix *docker_orient, vec3d *dockee_p0, vec3d *dockee_p1, vec3d *dockee_p0_norm, matrix *dockee_orient)
8954 {
8955  vec3d fvec, uvec, temp;
8956  matrix m1, m2, m3;
8957 
8958  // Compute the global orientation of the dockee's docking bay.
8959 
8960  // get the rotated (local) fvec
8961  vm_vec_rotate(&fvec, dockee_p0_norm, dockee_orient);
8962  vm_vec_negate(&fvec);
8963 
8964  // get the rotated (local) uvec
8965  vm_vec_normalized_dir(&temp, dockee_p1, dockee_p0);
8966  vm_vec_rotate(&uvec, &temp, dockee_orient);
8967 
8968  // create a rotation matrix
8969  vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8970 
8971  // get the global orientation
8972  vm_matrix_x_matrix(&m3, dockee_orient, &m1);
8973 
8974  // Compute the matrix given by the docker's docking bay.
8975 
8976  // get the rotated (local) fvec
8977  vm_vec_rotate(&fvec, docker_p0_norm, docker_orient);
8978 
8979  // get the rotated (local) uvec
8980  vm_vec_normalized_dir(&temp, docker_p1, docker_p0);
8981  vm_vec_rotate(&uvec, &temp, docker_orient);
8982 
8983  // create a rotation matrix
8984  vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8985 
8986  // Pre-multiply the orientation of the source object (docker_orient) by the transpose
8987  // of the docking bay's orientation, ie unrotate the source object's matrix.
8988  vm_transpose(&m2);
8989  vm_matrix_x_matrix(dom, &m3, &m2);
8990 }
8991 
8992 // Goober5000
8993 // Return the rotating submodel on which is mounted the specified dockpoint, or -1 for none.
8995 {
8996  int path_num, submodel;
8997 
8998  // make sure we have a spline path to check against before going any further
8999  if (pm->docking_bays[dock_index].num_spline_paths <= 0)
9000  {
9001  return -1;
9002  }
9003 
9004  // find a path for this dockpoint (c.f. ai_return_path_num_from_dockbay)
9005  path_num = pm->docking_bays[dock_index].splines[0];
9006 
9007  // path must exist
9008  if ((path_num >= 0) && (path_num < pm->n_paths))
9009  {
9010  // find the submodel for the path for this dockpoint
9011  submodel = pm->paths[path_num].parent_submodel;
9012 
9013  // submodel must exist and must move
9014  if ((submodel >= 0) && (submodel < pm->n_models) && (pm->submodel[submodel].movement_type >= 0))
9015  {
9016  return submodel;
9017  }
9018  }
9019 
9020  // if path doesn't exist or the submodel doesn't exist or the submodel doesn't move
9021  return -1;
9022 }
9023 
9024 // Goober5000
9025 void find_adjusted_dockpoint_info(vec3d *global_p0, vec3d *global_p1, vec3d *global_p0_norm, object *objp, polymodel *pm, int modelnum, int submodel, int dock_index)
9026 {
9027  // are we basing this off a rotating submodel?
9028  if (submodel >= 0)
9029  {
9030  vec3d submodel_offset;
9031  vec3d local_p0, local_p1;
9032  ship *shipp;
9033 
9034  shipp = &Ships[objp->instance];
9035 
9036  // calculate the dockpoint locations relative to the unrotated submodel
9037  model_find_submodel_offset(&submodel_offset, modelnum, submodel);
9038  vm_vec_sub(&local_p0, &pm->docking_bays[dock_index].pnt[0], &submodel_offset);
9039  vm_vec_sub(&local_p1, &pm->docking_bays[dock_index].pnt[1], &submodel_offset);
9040 
9041  // find the dynamic positions of the dockpoints
9042  model_instance_find_world_point(global_p0, &local_p0, shipp->model_instance_num, submodel, &objp->orient, &objp->pos);
9043  model_instance_find_world_point(global_p1, &local_p1, shipp->model_instance_num, submodel, &objp->orient, &objp->pos);
9044 
9045  // find the normal of the first dockpoint
9046  model_instance_find_world_dir(global_p0_norm, &pm->docking_bays[dock_index].norm[0], shipp->model_instance_num, submodel, &objp->orient);
9047  }
9048  // use the static dockpoints
9049  else
9050  {
9051  vm_vec_unrotate(global_p0, &pm->docking_bays[dock_index].pnt[0], &objp->orient);
9052  vm_vec_add2(global_p0, &objp->pos);
9053 
9054  vm_vec_unrotate(global_p1, &pm->docking_bays[dock_index].pnt[1], &objp->orient);
9055  vm_vec_add2(global_p1, &objp->pos);
9056 
9057  vm_vec_unrotate(global_p0_norm, &pm->docking_bays[dock_index].norm[0], &objp->orient);
9058  }
9059 }
9060 
9061 
9062 // Make docker_objp dock with dockee_objp
9063 // Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
9064 // DOA_APPROACH means approach point aip->path_cur
9065 // DOA_DOCK means dock
9066 // DOA_UNDOCK_1 means undock, moving to point nearest dock bay
9067 // DOA_UNDOCK_2 means undock, moving to point nearest dock bay and facing away from ship
9068 // DOA_UNDOCK_3 means undock, moving directly away from ship
9069 // DOA_DOCK_STAY means rigidly maintain position in dock bay.
9070 float dock_orient_and_approach(object *docker_objp, int docker_index, object *dockee_objp, int dockee_index, int dock_mode, rotating_dockpoint_info *rdinfo)
9071 {
9072  ship_info *sip0, *sip1;
9073  polymodel *pm0, *pm1;
9074  ai_info *aip;
9075  matrix dom, nm;
9076  vec3d docker_p0, docker_p1, docker_p0_norm;
9077  vec3d dockee_p0, dockee_p1, dockee_p0_norm;
9079  float fdist = UNINITIALIZED_VALUE;
9080 
9081  aip = &Ai_info[Ships[docker_objp->instance].ai_index];
9082 
9083  docker_objp->phys_info.forward_thrust = 0.0f; // Kill thrust so we don't have a sputtering thruster.
9084 
9085  // Goober5000 - moved out here to save calculations
9086  if (dock_mode != DOA_DOCK_STAY)
9087  if (ship_get_subsystem_strength(&Ships[docker_objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
9088  return 9999.9f;
9089 
9090  // If dockee has moved much, then path will be recreated.
9091  // Might need to change state if moved too far.
9092  if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
9093  maybe_recreate_path(docker_objp, aip, 0);
9094  }
9095 
9096  sip0 = &Ship_info[Ships[docker_objp->instance].ship_info_index];
9097  sip1 = &Ship_info[Ships[dockee_objp->instance].ship_info_index];
9098  pm0 = model_get( sip0->model_num );
9099  pm1 = model_get( sip1->model_num );
9100 
9101  Assert( docker_index >= 0 );
9102  Assert( dockee_index >= 0 );
9103 
9104  Assert(pm0->docking_bays[docker_index].num_slots == 2);
9105  Assert(pm1->docking_bays[dockee_index].num_slots == 2);
9106 
9107 
9108  // Goober5000 - check if we're attached to a rotating submodel
9109  int dockee_rotating_submodel = find_parent_rotating_submodel(pm1, dockee_index);
9110 
9111  // Goober5000 - move docking points with submodels if necessary, for both docker and dockee
9112  find_adjusted_dockpoint_info(&docker_p0, &docker_p1, &docker_p0_norm, docker_objp, pm0, sip0->model_num, -1, docker_index);
9113  find_adjusted_dockpoint_info(&dockee_p0, &dockee_p1, &dockee_p0_norm, dockee_objp, pm1, sip1->model_num, dockee_rotating_submodel, dockee_index);
9114 
9115  // Goober5000 - find average of point
9116  vm_vec_avg(&docker_point, &docker_p0, &docker_p1);
9117  vm_vec_avg(&dockee_point, &dockee_p0, &dockee_p1);
9118 
9119  // Goober5000
9120  vec3d submodel_pos = ZERO_VECTOR;
9121  float submodel_radius = 0.0f;
9122  float submodel_omega = 0.0f;
9123  if ((dockee_rotating_submodel >= 0) && (dock_mode != DOA_DOCK_STAY))
9124  {
9125  vec3d submodel_offset;
9126  vec3d dockpoint_temp;
9127 
9128  // get submodel center
9129  model_find_submodel_offset(&submodel_offset, sip1->model_num, dockee_rotating_submodel);
9130  vm_vec_add(&submodel_pos, &dockee_objp->pos, &submodel_offset);
9131 
9132  polymodel_instance *pmi1 = model_get_instance(Ships[dockee_objp->instance].model_instance_num);
9133 
9134  // get angular velocity of dockpoint
9135  //WMC - hack(?) to fix bug where sii might not exist
9136  if ( pmi1->submodel[dockee_rotating_submodel].sii != NULL ) {
9137  submodel_omega = pmi1->submodel[dockee_rotating_submodel].sii->cur_turn_rate;
9138  }
9139 
9140  // get radius to dockpoint
9141  vm_vec_avg(&dockpoint_temp, &dockee_p0, &dockee_p1);
9142  submodel_radius = vm_vec_dist(&submodel_pos, &dockpoint_temp);
9143  }
9144 
9145  // Goober5000
9146  rotating_dockpoint_info rdinfo_buf;
9147  if (rdinfo == NULL)
9148  rdinfo = &rdinfo_buf;
9149 
9150  rdinfo->docker_point = docker_point;
9151  rdinfo->dockee_point = dockee_point;
9152  rdinfo->dock_mode = dock_mode;
9153  rdinfo->submodel = dockee_rotating_submodel;
9154  rdinfo->submodel_pos = submodel_pos;
9155  rdinfo->submodel_r = submodel_radius;
9156  rdinfo->submodel_w = submodel_omega;
9157 
9158 
9159  float speed_scale = 1.0f;
9160  if (sip0->flags & SIF_SUPPORT) {
9161  speed_scale = 3.0f;
9162  }
9163 
9164  switch (dock_mode) {
9165  case DOA_APPROACH:
9166  {
9167  vec3d *goal_point;
9168 
9169  // Compute the desired global orientation matrix for the docker's station.
9170  // That is, the normal vector of the docking station must be the same as the
9171  // forward vector and the vector between its two points must be the uvec.
9172  set_goal_dock_orient(&dom, &docker_p0, &docker_p1, &docker_p0_norm, &docker_objp->orient, &dockee_p0, &dockee_p1, &dockee_p0_norm, &dockee_objp->orient);
9173 
9174  // Compute new orientation matrix and update rotational velocity.
9175  vec3d omega_in, omega_out, vel_limit, acc_limit;
9176  float tdist, mdist, ss1;
9177 
9178  omega_in = docker_objp->phys_info.rotvel;
9179  vel_limit = docker_objp->phys_info.max_rotvel;
9180  vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
9181 
9182  if (sip0->flags & SIF_SUPPORT)
9183  vm_vec_scale(&acc_limit, 2.0f);
9184 
9185  // 1 at end of line prevent overshoot
9186  vm_matrix_interpolate(&dom, &docker_objp->orient, &omega_in, flFrametime, &nm, &omega_out, &vel_limit, &acc_limit, 1);
9187  docker_objp->phys_info.rotvel = omega_out;
9188  docker_objp->orient = nm;
9189 
9190  // Translate towards goal and note distance to goal.
9191  goal_point = &Path_points[aip->path_cur].pos;
9192  mdist = ai_matrix_dist(&docker_objp->orient, &dom);
9193  tdist = vm_vec_dist_quick(&docker_objp->pos, goal_point);
9194 
9195  // If translation is badly lagging rotation, speed up translation.
9196  if (mdist > 0.1f) {
9197  ss1 = tdist/(10.0f * mdist);
9198  if (ss1 > 2.0f)
9199  ss1 = 2.0f;
9200  } else
9201  ss1 = 2.0f;
9202 
9203  // if we're docking to a rotating submodel, speed up translation
9204  if (dockee_rotating_submodel >= 0)
9205  ss1 = 2.0f;
9206 
9207  speed_scale *= 1.0f + ss1;
9208 
9209  fdist = dock_move_towards_point(docker_objp, &docker_objp->pos, goal_point, speed_scale, dockee_objp->phys_info.speed, rdinfo);
9210 
9211  // Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
9212  fdist += 2.0f * mdist;
9213 
9214  break;
9215  }
9216  case DOA_DOCK:
9217  case DOA_DOCK_STAY:
9218  {
9219  // Compute the desired global orientation matrix for the docker's station.
9220  // That is, the normal vector of the docking station must be the same as the
9221  // forward vector and the vector between its two points must be the uvec.
9222  set_goal_dock_orient(&dom, &docker_p0, &docker_p1, &docker_p0_norm, &docker_objp->orient, &dockee_p0, &dockee_p1, &dockee_p0_norm, &dockee_objp->orient);
9223 
9224  // Compute distance between dock bay points.
9225  if (dock_mode == DOA_DOCK) {
9226  vec3d omega_in, omega_out, vel_limit, acc_limit;
9227 
9228  // Compute new orientation matrix and update rotational velocity.
9229  omega_in = docker_objp->phys_info.rotvel;
9230  vel_limit = docker_objp->phys_info.max_rotvel;
9231  vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
9232 
9233  if (sip0->flags & SIF_SUPPORT)
9234  vm_vec_scale(&acc_limit, 2.0f);
9235 
9236  vm_matrix_interpolate(&dom, &docker_objp->orient, &omega_in, flFrametime, &nm, &omega_out, &vel_limit, &acc_limit);
9237  docker_objp->phys_info.rotvel = omega_out;
9238  docker_objp->orient = nm;
9239 
9240  fdist = dock_move_towards_point(docker_objp, &docker_point, &dockee_point, speed_scale, dockee_objp->phys_info.speed, rdinfo);
9241 
9242  // Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
9243  fdist += 10.0f * vm_vec_mag_quick(&omega_out);
9244  } else {
9245  Assert(dock_mode == DOA_DOCK_STAY);
9246  matrix temp, m_offset;
9247  vec3d origin_docker_point, adjusted_docker_point, v_offset;
9248 
9249  // find out the rotation matrix that will get us from the old to the new rotation
9250  vm_copy_transpose(&temp, &docker_objp->orient);
9251  vm_matrix_x_matrix(&m_offset, &temp, &dom);
9252 
9253  // now find out the new docker point after being adjusted for the new orientation
9254  vm_vec_sub(&origin_docker_point, &docker_point, &docker_objp->pos);
9255  vm_vec_rotate(&adjusted_docker_point, &origin_docker_point, &m_offset);
9256  vm_vec_add2(&adjusted_docker_point, &docker_objp->pos);
9257 
9258  // find the vector that will get us from the old to the new position
9259  vm_vec_sub(&v_offset, &dockee_point, &adjusted_docker_point);
9260 
9261  // now set the new rotation and move to the new position
9262  docker_objp->orient = dom;
9263  vm_vec_add2(&docker_objp->pos, &v_offset);
9264  }
9265 
9266  break;
9267  }
9268  case DOA_UNDOCK_1:
9269  {
9270  // Undocking.
9271  // Move to point on dock path nearest to dock station.
9272  Assert(aip->path_length >= 2);
9273  fdist = dock_move_towards_point(docker_objp, &docker_objp->pos, &Path_points[aip->path_start + aip->path_length-2].pos, speed_scale, 0.0f, rdinfo);
9274 
9275  break;
9276  }
9277  case DOA_UNDOCK_2:
9278  {
9279  // Undocking.
9280  // Move to point on dock path nearest to dock station and orient away from big ship.
9281  int desired_index;
9282 
9283  Assert(aip->path_length >= 2);
9284 
9285  desired_index = aip->path_length-2;
9286  fdist = dock_move_towards_point(docker_objp, &docker_objp->pos, &Path_points[aip->path_start + desired_index].pos, speed_scale, 0.0f, rdinfo);
9287 
9288  break;
9289  }
9290  case DOA_UNDOCK_3:
9291  {
9292  vec3d goal_point;
9293  float dist, goal_dist;
9294  vec3d away_vec;
9295 
9296  goal_dist = docker_objp->radius + dockee_objp->radius + 25.0f;
9297 
9298  dist = vm_vec_normalized_dir(&away_vec, &docker_objp->pos, &dockee_objp->pos);
9299  vm_vec_scale_add(&goal_point, &dockee_objp->pos, &away_vec, goal_dist);
9300  if (vm_vec_dist_quick(&goal_point, &dockee_objp->pos) < vm_vec_dist_quick(&docker_objp->pos, &dockee_objp->pos))
9301  fdist = 0.0f;
9302  else
9303  {
9304  float dot, accel;
9305  float turn_time = Ship_info[Ships[docker_objp->instance].ship_info_index].srotation_time;
9306  ai_turn_towards_vector(&goal_point, docker_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
9307 
9308  dot = vm_vec_dot(&docker_objp->orient.vec.fvec, &away_vec);
9309  accel = 0.1f;
9310  if (dot > accel)
9311  accel = dot;
9312  if (dist > goal_dist/2)
9313  accel *= 1.2f - 0.5f*goal_dist/dist;
9314 
9315  accelerate_ship(aip, accel);
9316  fdist = vm_vec_dist_quick(&docker_objp->pos, &goal_point);
9317  }
9318 
9319  break;
9320  }
9321  }
9322 
9323  return fdist;
9324 }
9325 
9327 {
9328  ship *shipp = &Ships[Pl_objp->instance];
9329  object *objp;
9330 
9331  for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
9332  if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
9333  if (objp->instance != -1) {
9334  if (Ships[objp->instance].team == shipp->team) {
9335  ai_set_guard_object(Pl_objp, objp);
9336  }
9337  }
9338  }
9339  }
9340 
9341 }
9342 
9346 int num_ships_attacking(int target_objnum)
9347 {
9348  object *attacking_objp;
9349  ai_info *attacking_aip;
9350  ship_obj *so;
9351  int count = 0;
9352  int target_team = obj_team(&Objects[target_objnum]);
9353 
9354  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) )
9355  {
9356  attacking_objp = &Objects[so->objnum];
9357  if (attacking_objp->instance < 0)
9358  continue;
9359 
9360  attacking_aip = &Ai_info[Ships[attacking_objp->instance].ai_index];
9361 
9362  // don't count instructor
9363  if (is_training_mission() && is_instructor(attacking_objp))
9364  continue; // Goober5000 10/06/2005 changed from break
9365 
9366  if (iff_x_attacks_y(Ships[attacking_objp->instance].team, target_team))
9367  {
9368  if (attacking_aip->target_objnum == target_objnum)
9369  {
9370  if ( ((Game_mode & GM_MULTIPLAYER) && (attacking_objp->flags & OF_PLAYER_SHIP))
9371  || (attacking_aip->mode == AIM_CHASE) )
9372  {
9373  count++;
9374  }
9375  }
9376  }
9377  }
9378 
9379  return count;
9380 }
9381 
9382 // For all objects attacking object #objnum, remove the one that is farthest away.
9383 // Do this by resuming previous behavior, if any. If not, set target_objnum to -1.
9385 {
9386  object *objp, *objp2, *farthest_objp;
9387  ship_obj *so;
9388  float farthest_dist;
9389 
9390  objp2 = &Objects[objnum];
9391 
9392  farthest_dist = 9999999.9f;
9393  farthest_objp = NULL;
9394 
9395  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9396  objp = &Objects[so->objnum];
9397  if ( !(objp->flags & OF_PLAYER_SHIP)) {
9398  if (objp->instance != -1) {
9399  ai_info *aip2;
9400 
9401  aip2 = &Ai_info[Ships[objp->instance].ai_index];
9402 
9403  if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
9404  if (iff_x_attacks_y(Ships[objp->instance].team, Ships[objp2->instance].team)) {
9405  float dist;
9406 
9407  dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
9408  if (dist < farthest_dist) {
9409  farthest_dist = dist;
9410  farthest_objp = objp;
9411  }
9412  }
9413  }
9414  }
9415  }
9416  }
9417 
9418  if (farthest_objp != NULL) {
9419  ai_info *aip;
9420  Assert(farthest_objp->type == OBJ_SHIP);
9421  Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
9422  Assert(Ships[farthest_objp->instance].ai_index > -1);
9423 
9424  aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
9425 
9426  if (!maybe_resume_previous_mode(Pl_objp, aip))
9427  {
9428  // If already ignoring something under player's orders, don't ignore current target.
9429  if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE))
9430  {
9431  aip->ignore_objnum = aip->target_objnum;
9434  aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000); // OK to attack again in 20 to 24 seconds.
9435  }
9436  aip->target_objnum = -1;
9437  ai_do_default_behavior(farthest_objp);
9438  }
9439  }
9440 }
9441 
9442 // Maybe limit the number of attackers on attack_objnum. For now, only limit attackers
9443 // in attacked_objnum is the player
9444 // input: attacked_objnum => object index for ship we want to limit attacks on
9445 //
9446 // exit: 1 => num attackers exceeds maximum, abort
9447 // 0 => removed the farthest attacker
9448 // -1 => nothing was done
9449 int ai_maybe_limit_attackers(int attacked_objnum)
9450 {
9451  int rval=-1;
9452 
9453  if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
9454  int num_attacking;
9455  num_attacking = num_ships_attacking(attacked_objnum);
9456 
9457  if (num_attacking == The_mission.ai_profile->max_attackers[Game_skill_level]) {
9458  remove_farthest_attacker(attacked_objnum);
9459  rval=0;
9460  } else if (num_attacking > The_mission.ai_profile->max_attackers[Game_skill_level]) {
9461  rval=1;
9462  }
9463  }
9464 
9465  return rval;
9466 }
9467 
9471 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
9472 {
9473  int hitter_objnum;
9474  ai_info *aip;
9475 
9476  aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9477 
9478  if (guard_objp == hitter_objp) {
9479  return;
9480  }
9481 
9482  if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
9483  return;
9484 
9485  if (aip->ai_flags & AIF_NO_DYNAMIC) // Not allowed to pursue dynamic goals. So, why are we guarding?
9486  return;
9487 
9488  Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
9489 
9490  hitter_objnum = OBJ_INDEX(hitter_objp);
9491 
9492  if ( hitter_objp->type == OBJ_SHIP ) {
9493  // If the hitter object is the ignore object, don't attack it.
9494  if (is_ignore_object(aip, OBJ_INDEX(hitter_objp)))
9495  return;
9496 
9497  // If hitter is on same team as me, don't attack him.
9498  if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
9499  return;
9500 
9501  // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9502  if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9503  return;
9504  }
9505 
9506  // don't attack if you can't see him
9507  if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
9508  // if he's a stealth and visible, but not targetable, ok to attack.
9509  if ( is_object_stealth_ship(hitter_objp) ) {
9510  if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_IN_FRUSTUM ) {
9511  return;
9512  }
9513  }
9514  }
9515  }
9516 
9517  if (aip->target_objnum == -1) {
9519  }
9520 
9521  if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
9522 
9523  if ( hitter_objp->type == OBJ_SHIP ) {
9524  if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
9525  return;
9526  }
9527 
9528  // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9529  if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9530  return;
9531  }
9532  }
9533 
9534  if (aip->target_objnum != hitter_objnum) {
9535  aip->aspect_locked_time = 0.0f;
9536  }
9537 
9539 
9540  set_target_objnum(aip, hitter_objnum);
9541  aip->previous_mode = AIM_GUARD;
9542  aip->previous_submode = aip->submode;
9543  aip->mode = AIM_CHASE;
9544  aip->submode = SM_ATTACK;
9547  } else if (aip->previous_mode == AIM_GUARD) {
9548  if (aip->target_objnum == -1) {
9549 
9550  if ( hitter_objp->type == OBJ_SHIP ) {
9551  // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9552  if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9553  return;
9554  }
9555  }
9556 
9557  set_target_objnum(aip, hitter_objnum);
9558  aip->mode = AIM_CHASE;
9559  aip->submode = SM_ATTACK;
9562  } else {
9563  int num_attacking_cur, num_attacking_new;
9564 
9565  num_attacking_cur = num_ships_attacking(aip->target_objnum);
9566  if (num_attacking_cur > 1) {
9567  num_attacking_new = num_ships_attacking(hitter_objnum);
9568 
9569  if (num_attacking_new < num_attacking_cur) {
9570 
9571  if ( hitter_objp->type == OBJ_SHIP ) {
9572  // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9573  if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9574  return;
9575  }
9576  }
9577  set_target_objnum(aip, OBJ_INDEX(hitter_objp));
9578  aip->mode = AIM_CHASE;
9579  aip->submode = SM_ATTACK;
9582  }
9583  }
9584  }
9585  }
9586 }
9587 
9588 // Ship object *hit_objp was hit by ship object *hitter_objp.
9589 // See if anyone is guarding hit_objp and, if so, do something useful.
9590 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9591 {
9592  object *objp;
9593  ship_obj *so;
9594 
9595  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9596  objp = &Objects[so->objnum];
9597  if (objp->instance != -1) {
9598  ai_info *aip;
9599  aip = &Ai_info[Ships[objp->instance].ai_index];
9600 
9601  if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9602  if (aip->guard_objnum == OBJ_INDEX(hit_objp)) {
9603  guard_object_was_hit(objp, hitter_objp);
9604  } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9605  guard_object_was_hit(objp, hitter_objp);
9606  }
9607  }
9608  }
9609  }
9610 }
9611 
9612 // Scan missile list looking for bombs homing on guarded_objp
9613 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9614 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9615 {
9616  missile_obj *mo;
9617  object *bomb_objp, *closest_bomb_objp=NULL;
9618  float dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9619  weapon *wp;
9620  weapon_info *wip;
9621 
9622  for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9623  Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9624  bomb_objp = &Objects[mo->objnum];
9625 
9626  wp = &Weapons[bomb_objp->instance];
9627  wip = &Weapon_info[wp->weapon_info_index];
9628 
9629  if ( !((wip->wi_flags & WIF_BOMB) || (wip->wi_flags3 & WIF3_FIGHTER_INTERCEPTABLE)) ) {
9630  continue;
9631  }
9632 
9633  if ( wp->homing_object != guarded_objp ) {
9634  continue;
9635  }
9636 
9637  if (wp->lssm_stage==3){
9638  continue;
9639  }
9640 
9641  dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9642 
9643  if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9644  dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9645  if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9646  closest_dist_to_guarding_obj = dist_to_guarding_obj;
9647  closest_bomb_objp = bomb_objp;
9648  }
9649  }
9650  }
9651 
9652  if ( closest_bomb_objp ) {
9653  guard_object_was_hit(guarding_objp, closest_bomb_objp);
9654  return 1;
9655  }
9656 
9657  return 0;
9658 }
9659 
9663 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9664 {
9665  ship *guarding_shipp = &Ships[guarding_objp->instance];
9666  ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9667  ship_obj *so;
9668  object *enemy_objp;
9669  float dist;
9670 
9671  for (so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
9672  {
9673  enemy_objp = &Objects[so->objnum];
9674 
9675  if (enemy_objp->instance < 0)
9676  {
9677  continue;
9678  }
9679 
9680  ship *eshipp = &Ships[enemy_objp->instance];
9681 
9682  if (iff_x_attacks_y(guarding_shipp->team, eshipp->team))
9683  {
9684  // Don't attack a cargo container or other harmless ships
9685  if (Ship_info[eshipp->ship_info_index].class_type >= 0 && (Ship_types[Ship_info[eshipp->ship_info_index].class_type].ai_bools & STI_AI_GUARDS_ATTACK))
9686  {
9687  dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9688  if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3)
9689  {
9690  guard_object_was_hit(guarding_objp, enemy_objp);
9691  }
9692  else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum))
9693  {
9694  guard_object_was_hit(guarding_objp, enemy_objp);
9695  }
9696  }
9697  }
9698  }
9699 }
9700 
9701 // Scan for nearby asteroids. Favor asteroids which have their collide_objnum set to that of the
9702 // guarded ship. Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9703 // when a ship blows up an asteroid then goes after the pieces that break off.
9704 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9705 {
9706  float dist;
9707 
9708  object *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9709  float dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9710 
9711  for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9712  if ( asteroid_objp->type == OBJ_ASTEROID ) {
9713  // Attack asteroid if near guarded ship
9714  dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9715  if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9716  dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9717  if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9718  if( dist_to_self < closest_danger_asteroid_dist ) {
9719  danger_asteroid_objp=asteroid_objp;
9720  closest_danger_asteroid_dist=dist_to_self;
9721  }
9722  }
9723  if ( dist_to_self < closest_asteroid_dist ) {
9724  // only attack if moving slower than own max speed
9725  if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9726  closest_asteroid_dist = dist_to_self;
9727  closest_asteroid_objp = asteroid_objp;
9728  }
9729  }
9730  }
9731  }
9732  }
9733 
9734  if ( danger_asteroid_objp ) {
9735  guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9736  } else if ( closest_asteroid_objp ) {
9737  guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9738  }
9739 }
9740 
9745 {
9746  ship *shipp = &Ships[Pl_objp->instance];
9747  ai_info *aip = &Ai_info[shipp->ai_index];
9748  object *guardobjp;
9749  int bomb_found=0;
9750 
9751  guardobjp = &Objects[aip->guard_objnum];
9752 
9753  // highest priority is a bomb fired on guarded ship
9754  bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9755 
9756  if ( !bomb_found ) {
9757  // check for ships if there are no bombs fired at guarded ship
9758  ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9759 
9760  // if not attacking anything, go for asteroid close to guarded ship
9761  if ( (aip->target_objnum == -1) && asteroid_count() ) {
9762  ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9763  }
9764  }
9765 }
9766 
9771 float get_cylinder_points(object *other_objp, object *cyl_objp, vec3d *axis_pt, vec3d *r_vec, float *radius)
9772 {
9773  Assert(other_objp->type == OBJ_SHIP);
9774  Assert(cyl_objp->type == OBJ_SHIP);
9775 
9776  // get radius of cylinder
9777  polymodel *pm = model_get(Ship_info[Ships[cyl_objp->instance].ship_info_index].model_num);
9778  float tempx, tempy;
9779  tempx = MAX(-pm->mins.xyz.x, pm->maxs.xyz.x);
9780  tempy = MAX(-pm->mins.xyz.y, pm->maxs.xyz.y);
9781  *radius = MAX(tempx, tempy);
9782 
9783  // get vec from cylinder to other_obj
9784  vec3d r_sph;
9785  vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9786 
9787  // get point on axis and on cylinder
9788  // extended_cylinder_z is along extended cylinder
9789  // cylinder_z is capped within cylinder
9790  float extended_cylinder_z = vm_vec_dot(&r_sph, &cyl_objp->orient.vec.fvec);
9791 
9792  // get pt on axis of extended cylinder
9793  vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.vec.fvec, extended_cylinder_z);
9794 
9795  // get r_vec (pos - axis_pt) normalized
9796  vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9797 
9798  return extended_cylinder_z;
9799 }
9800 
9801 // handler for guard behavior when guarding BIG ships
9802 // When someone has attacked guarded ship, then attack that ship.
9803 // To attack another ship, switch out of guard mode into chase mode.
9805 {
9806 
9807  ship *shipp = &Ships[Pl_objp->instance];
9808  ai_info *aip = &Ai_info[shipp->ai_index];
9809  object *guard_objp;
9810 
9811  // sanity checks already done in ai_guard()
9812  guard_objp = &Objects[aip->guard_objnum];
9813 
9814  switch (aip->submode) {
9815  case AIS_GUARD_STATIC:
9816  case AIS_GUARD_PATROL:
9817  {
9818  vec3d axis_pt, r_vec, theta_vec;
9819  float radius, extended_z;
9820 
9821  // get random [0 to 1] based on OBJNUM
9822  float objval = static_randf(OBJ_INDEX(Pl_objp));
9823 
9824  // get position relative to cylinder of guard_objp
9825  extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9826  vm_vec_cross(&theta_vec, &guard_objp->orient.vec.fvec, &r_vec);
9827 
9828  // half ships circle each way
9829  if (objval > 0.5f) {
9830  vm_vec_negate(&theta_vec);
9831  }
9832 
9833  float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9834  float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9835  float max_guard_dist = min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9836 
9837  // get z extents
9838  float min_z, max_z, length;
9839  polymodel *pm = model_get(Ship_info[Ships[guard_objp->instance].ship_info_index].model_num);
9840  min_z = pm->mins.xyz.z;
9841  max_z = pm->maxs.xyz.z;
9842  length = max_z - min_z;
9843 
9844  // get desired z
9845  // how often to choose new desired_z
9846  // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9847  int time_choose = int(floor(log(length * 0.001f) / log(2.0f)));
9848  float desired_z = min_z + length * static_randf( (OBJ_INDEX(Pl_objp)) ^ (Missiontime >> (22 + time_choose)) );
9849 
9850  // get r from guard_ship
9851  float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9852 
9853  // is ship within extents of cylinder of ship it is guarding
9854  int inside = (extended_z > min_z) && (extended_z < min_z + length);
9855 
9856  vec3d goal_pt;
9857  // maybe go into orbit mode
9858  if (cur_guard_rad < max_guard_dist) {
9859  if ( cur_guard_rad > min_guard_dist ) {
9860  if (inside) {
9861  // orbit
9862  vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9863  vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9864  } else {
9865  // move to where I can orbit
9866  if (extended_z < min_z) {
9867  vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.vec.fvec, min_z);
9868  } else {
9869  vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.vec.fvec, max_z);
9870  }
9871  vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9872  vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9873  }
9874  } else {
9875  // too close for orbit mode
9876  if (inside) {
9877  // inside (fly straight out and return circle)
9878  vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9879  } else {
9880  // outside (fly to edge and circle)
9881  if (extended_z < min_z) {
9882  vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.vec.fvec, min_z);
9883  } else {
9884  vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.vec.fvec, max_z);
9885  }
9886  vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9887  vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9888  }
9889  }
9890 
9891  // make sure we have a forward velocity worth calculating (values too low can produce NaN in goal_pt) - taylor
9892  if (Pl_objp->phys_info.fspeed > 0.0001f) {
9893  // modify goal_pt to take account moving guard objp
9894  float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9895  float time = dist / Pl_objp->phys_info.fspeed;
9896  vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9897 
9898  // now modify to move to desired z (at a max of 20 m/s)
9899  float delta_z = desired_z - extended_z;
9900  float v_z = delta_z * 0.2f;
9901  if (v_z < -20) {
9902  v_z = -20.0f;
9903  } else if (v_z > 20) {
9904  v_z = 20.0f;
9905  }
9906 
9907  vm_vec_scale_add2(&goal_pt, &guard_objp->orient.vec.fvec, v_z*time);
9908  }
9909 
9910  } else {
9911  // cast vector to center of guard_ship adjusted by desired_z
9912  float delta_z = desired_z - extended_z;
9913  vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.vec.fvec, delta_z);
9914  }
9915 
9916  // try not to bump into things along the way
9917  if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9918  if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9919  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
9920  afterburners_stop(Pl_objp);
9921  }
9922  return;
9923  }
9924 
9925  if (avoid_player(Pl_objp, &goal_pt)) {
9926  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
9927  afterburners_stop(Pl_objp);
9928  }
9929  return;
9930  }
9931  } else {
9932  if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9933  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
9934  afterburners_stop(Pl_objp);
9935  }
9936  return;
9937  }
9938  }
9939 
9940  // got the point, now let's go there
9941  ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0);
9942  accelerate_ship(aip, 1.0f);
9943 
9944  if ((aip->ai_flags & AIF_FREE_AFTERBURNER_USE) && !(shipp->flags2 & SF2_AFTERBURNER_LOCKED) && (cur_guard_rad > 1.1f * max_guard_dist)) {
9945  vec3d v2g;
9946  float dot_to_goal_point;
9947 
9948  vm_vec_normalized_dir(&v2g, &goal_pt, &Pl_objp->pos);
9949  dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.vec.fvec);
9950 
9951  if (ai_maybe_fire_afterburner(Pl_objp, aip) && dot_to_goal_point > 0.75f) {
9952  afterburners_start(Pl_objp);
9954  }
9955  } else if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
9956  afterburners_stop(Pl_objp);
9957  }
9958 
9959  // Periodically, scan for a nearby ship to attack.
9960  if (((AI_FrameCount ^ (OBJ_INDEX(Pl_objp))) & 0x07) == 0) {
9962  }
9963  }
9964  break;
9965 
9966  case AIS_GUARD_ATTACK:
9967  // The guarded ship has been attacked. Do something useful!
9968  ai_chase();
9969  break;
9970 
9971  default:
9972  Int3(); // Illegal submode for Guard mode.
9973  // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9974  aip->submode = AIS_GUARD_PATROL;
9976  break;
9977  }
9978 }
9979 
9980 // Main handler for guard behavior.
9981 // When someone has attacked guarded ship, then attack that ship.
9982 // To attack another ship, switch out of guard mode into chase mode.
9983 void ai_guard()
9984 {
9985  ship *shipp = &Ships[Pl_objp->instance];
9986  ai_info *aip = &Ai_info[shipp->ai_index];
9987  object *guard_objp;
9988  float dist_to_guardobj;
9989  vec3d vec_to_guardobj;
9990 
9991  if (aip->guard_objnum == -1) {
9992  aip->mode = AIM_NONE;
9993  return;
9994  }
9995 
9996  Assert(aip->guard_objnum != -1);
9997 
9998  guard_objp = &Objects[aip->guard_objnum];
9999 
10000  if (guard_objp == Pl_objp) {
10001  Int3(); // This seems illegal. Why is a ship guarding itself?
10002  aip->guard_objnum = -1;
10003  return;
10004  }
10005 
10006  // check that I have someone to guard
10007  if (guard_objp->instance == -1) {
10008  return;
10009  }
10010 
10011  // Not sure whether this should be impossible, or a reasonable cleanup condition.
10012  // For now (3/31/97), it's getting trapped by an Assert, so clean it up.
10013  if (guard_objp->type != OBJ_SHIP) {
10014  aip->guard_objnum = -1;
10015  return;
10016  }
10017 
10018  // handler for gurad object with BIG radius
10019  if (guard_objp->radius > BIG_GUARD_RADIUS) {
10020  ai_big_guard();
10021  return;
10022  }
10023 
10024  float objval;
10025  vec3d goal_point;
10026  vec3d rel_vec;
10027  float dist_to_goal_point, dot_to_goal_point, accel_scale;
10028  vec3d v2g, rvec;
10029 
10030  // get random [0 to 1] based on OBJNUM
10031  objval = static_randf(OBJ_INDEX(Pl_objp));
10032 
10033  switch (aip->submode) {
10034  case AIS_GUARD_STATIC:
10035  case AIS_GUARD_PATROL:
10036  // Stay near ship
10037  dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
10038 
10039  rel_vec = aip->guard_vec;
10040  vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
10041 
10042  vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
10043  dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
10044  dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.vec.fvec);
10045  accel_scale = (1.0f + dot_to_goal_point)/2.0f;
10046 
10047  // If far away, get closer
10048  if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
10049  if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
10050  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
10051  afterburners_stop(Pl_objp);
10052  }
10053  return;
10054  }
10055 
10056  if (avoid_player(Pl_objp, &goal_point)) {
10057  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
10058  afterburners_stop(Pl_objp);
10059  }
10060  return;
10061  }
10062 
10063  // quite far away, so try to go straight to
10064  compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
10065  ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
10066 
10067  if ((aip->ai_flags & AIF_FREE_AFTERBURNER_USE) && !(shipp->flags2 & SF2_AFTERBURNER_LOCKED) && (accel_scale * (0.25f + dist_to_goal_point/700.0f) > 0.8f)) {
10068  if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
10069  afterburners_start(Pl_objp);
10071  }
10072  accelerate_ship(aip, 1.0f);
10073  } else {
10074  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
10075  afterburners_stop(Pl_objp);
10076  }
10077  accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
10078  }
10079 
10080  } else {
10081  if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
10082  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
10083  afterburners_stop(Pl_objp);
10084  }
10085  return;
10086  }
10087 
10088  // get max of guard_objp (1) normal speed (2) dock speed
10089  float speed = guard_objp->phys_info.speed;
10090 
10091  if (guard_objp->type == OBJ_SHIP) {
10092  if (object_is_docked(guard_objp)) {
10093  speed = dock_calc_docked_speed(guard_objp);
10094  }
10095  }
10096 
10097  // Deal with guarding a small object.
10098  // If going to guard_vec might cause a collision with guarded object, pick a new guard point.
10099  if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
10100  if (dist_to_guardobj < dist_to_goal_point) {
10101  ai_set_guard_vec(Pl_objp, guard_objp); // OK to return here.
10102  return;
10103  }
10104  }
10105 
10106  if (speed > 10.0f) {
10107  // If goal ship is moving more than a tiny bit, don't orbit it, get near it.
10108  if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
10109  if (vm_vec_dot(&Pl_objp->orient.vec.fvec, &v2g) < 0.0f) {
10110  // Just slow down, don't turn.
10111  set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
10112  } else {
10113  // Goal point is in front.
10114  // If close to goal point, don't change direction, just change speed.
10115  if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
10116  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
10117  }
10118 
10119  set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
10120  }
10121  } else {
10122  if (dot_to_goal_point > 0.8f) {
10123  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
10124  set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
10125  } else {
10126  set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
10127  }
10128  }
10129  // consider guard object STILL
10130  } else if (guard_objp->radius < 50.0f) {
10131  if (dist_to_goal_point > 15.0f) {
10132  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
10133  set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
10134  } else if (Pl_objp->phys_info.speed < 1.0f) {
10135  turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
10136  }
10137  // It's a big ship
10138  } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
10139  // Orbiting ship, too far away
10140  float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
10141  accelerate_ship(aip, (1.0f + dot)/2.0f);
10142  } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
10143  // Orbiting ship, got too close
10144  turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
10145  if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
10146  change_acceleration(aip, 0.25f);
10147  else
10148  accelerate_ship(aip, 0.5f + objval/4.0f);
10149  } else {
10150  // Orbiting ship, about the right distance away.
10151  float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
10152  if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
10153  set_accel_for_target_speed(Pl_objp, (0.5f * (1.0f + dot)) * (guard_objp->phys_info.speed + (dist_to_guardobj - guard_objp->radius - Pl_objp->radius)/10.0f));
10154  else
10155  accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
10156  }
10157  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
10158  afterburners_stop(Pl_objp);
10159  }
10160  }
10161 
10162  // Periodically, scan for a nearby ship to attack.
10163  if (((AI_FrameCount ^ (OBJ_INDEX(Pl_objp))) & 0x07) == 0) {
10165  }
10166  break;
10167 
10168  case AIS_GUARD_ATTACK:
10169  // The guarded ship has been attacked. Do something useful!
10170  ai_chase();
10171 
10172  break;
10173  default:
10174  Int3(); // Illegal submode for Guard mode.
10175  // AL 06/03/97 comment out Int3() to allow milestone to get out the door
10176  aip->submode = AIS_GUARD_PATROL;
10178  break;
10179  }
10180 
10181 }
10182 
10183 // function to clean up ai flags, variables, and other interesting information
10184 // for a ship that was getting repaired. The how parameter is useful for multiplayer
10185 // only in that it tells us why the repaired ship is being cleaned up.
10186 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
10187 {
10188  ai_info *aip, *repair_aip;
10189  int stamp = -1;
10190 
10191  int p_index;
10192  int p_team;
10193 
10194  p_index = -1;
10195  p_team = -1;
10196 
10197  // repaired_objp should not be null, but repair_objp will be null when a support ship is just warping in
10198  Assert(repaired_objp != NULL);
10199 
10200  Assert( repaired_objp->type == OBJ_SHIP);
10201  aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
10202 
10203  if(Game_mode & GM_MULTIPLAYER){
10204  p_index = multi_find_player_by_object(repaired_objp);
10205  if (p_index >= 0) {
10206  p_team = Net_players[p_index].p_info.team;
10207  }
10208  } else {
10209  if(repaired_objp == Player_obj){
10210  p_index = Player_num;
10211  }
10212  }
10213 
10214  switch( how ) {
10215  case REPAIR_INFO_BEGIN:
10216  aip->ai_flags |= AIF_BEING_REPAIRED;
10217  aip->ai_flags &= ~AIF_AWAITING_REPAIR;
10218  stamp = timestamp(-1);
10219 
10220  // if this is a player ship, then subtract the repair penalty from this player's score
10221  if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
10222  if ( !(Game_mode & GM_MULTIPLAYER) ) {
10224  }
10225  }
10226  break;
10227 
10228  case REPAIR_INFO_BROKEN:
10229  aip->ai_flags &= ~AIF_BEING_REPAIRED;
10230  aip->ai_flags |= AIF_AWAITING_REPAIR;
10231  stamp = timestamp((int) ((30 + 10*frand()) * 1000));
10232  break;
10233 
10234  case REPAIR_INFO_END:
10235  // when only awaiting repair, and the repair is ended, then set support to -1.
10236  if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
10237  aip->support_ship_objnum = -1;
10238  }
10240  stamp = timestamp((int) ((30 + 10*frand()) * 1000));
10241  break;
10242 
10243  case REPAIR_INFO_QUEUE:
10244  aip->ai_flags |= AIF_AWAITING_REPAIR;
10245  if ( aip == Player_ai ){
10247  }
10248  stamp = timestamp(-1);
10249  break;
10250 
10251  case REPAIR_INFO_ABORT:
10252  case REPAIR_INFO_KILLED:
10253  // undock if necessary (we may be just waiting for a repair in which case we aren't docked)
10254  if ((repair_objp != NULL) && dock_check_find_direct_docked_object(repair_objp, repaired_objp))
10255  {
10256  ai_do_objects_undocked_stuff(repair_objp, repaired_objp);
10257  }
10258 
10259  // 5/4/98 -- MWA -- Need to set support objnum to -1 to let code know this guy who was getting
10260  // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
10261  aip->support_ship_objnum = -1;
10263 
10264  if (repair_objp != NULL) {
10265  repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
10266  repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
10267  }
10268 
10269  if ( p_index >= 0 ) {
10271 
10272  // send appropriate messages here
10274  if ( how == REPAIR_INFO_KILLED ){
10276  } else {
10277  if ( repair_objp ){
10279  }
10280  }
10281  }
10282  }
10283 
10284  // add log entry if this is a player
10285  if ( repaired_objp->flags & OF_PLAYER_SHIP ){
10286  mission_log_add_entry(LOG_PLAYER_ABORTED_REARM, Ships[repaired_objp->instance].ship_name, NULL);
10287  }
10288 
10289  stamp = timestamp((int) ((30 + 10*frand()) * 1000));
10290  break;
10291 
10292  case REPAIR_INFO_COMPLETE:
10293  // clear the being repaired flag -- and
10294  if ( p_index >= 0 ) {
10295  Assert( repair_objp );
10296 
10298 
10301  }
10302  }
10303  stamp = timestamp((int) ((30 + 10*frand()) * 1000));
10304  break;
10305 
10306  case REPAIR_INFO_ONWAY:
10307  // need to set the signature so that clients in multiplayer games rearm correctly
10308  Assert( repair_objp );
10309  aip->support_ship_signature = repair_objp->signature;
10310  aip->support_ship_objnum = OBJ_INDEX(repair_objp);
10311  stamp = timestamp(-1);
10312  break;
10313 
10314  default:
10315  Int3(); // bogus type of repair info
10316  }
10317 
10318  // repair_objp might be NULL is we are cleaning up this mode because of the support ship
10319  // getting killed.
10320  if ( repair_objp ) {
10321  Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
10322  aip = &Ai_info[Ships[repair_objp->instance].ai_index];
10323 
10324  switch ( how ) {
10325  case REPAIR_INFO_ONWAY:
10326  Assert( repaired_objp != NULL );
10327  aip->goal_objnum = OBJ_INDEX(repaired_objp);
10328  aip->ai_flags |= AIF_REPAIRING;
10329  break;
10330 
10331  case REPAIR_INFO_BROKEN:
10332  break;
10333 
10334  case REPAIR_INFO_END:
10335  case REPAIR_INFO_ABORT:
10336  case REPAIR_INFO_KILLED:
10337  if ( how == REPAIR_INFO_ABORT )
10338  aip->goal_objnum = -1;
10339 
10340  aip->ai_flags &= ~AIF_REPAIRING;
10341  break;
10342 
10343  case REPAIR_INFO_QUEUE:
10344  ai_add_rearm_goal( repaired_objp, repair_objp );
10345  break;
10346 
10347  case REPAIR_INFO_BEGIN:
10348  case REPAIR_INFO_COMPLETE:
10349  break;
10350 
10351  default:
10352  Int3(); // bogus type of repair info
10353  }
10354  }
10355 
10356  multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
10357 }
10358 
10359 // Goober5000 - helper function that is also called from ai_dock()
10360 void ai_get_dock_goal_indexes(object *objp, ai_info *aip, ai_goal *aigp, object *goal_objp, int &docker_index, int &dockee_index)
10361 {
10362  // get the indexes
10363  switch (aip->submode)
10364  {
10365  case AIS_DOCK_1:
10366  case AIS_DOCK_2:
10367  case AIS_DOCK_3:
10368  Warning(LOCATION, "Normally dock indexes should be calculated for only AIS_DOCK_0 and AIS_UNDOCK_0. Trace out and debug.");
10369  case AIS_DOCK_0:
10370  {
10371  // get them from the active goal
10372  Assert(aigp != NULL);
10374  docker_index = aigp->docker.index;
10375  dockee_index = aigp->dockee.index;
10376  Assert(docker_index >= 0);
10377  Assert(dockee_index >= 0);
10378  break;
10379  }
10380 
10381  case AIS_DOCK_4:
10382  case AIS_DOCK_4A:
10383  case AIS_UNDOCK_1:
10384  case AIS_UNDOCK_2:
10385  Warning(LOCATION, "Normally dock indexes should be calculated for only AIS_DOCK_0 and AIS_UNDOCK_0. Trace out and debug.");
10386  case AIS_UNDOCK_0:
10387  {
10388  // get them from the guy I'm docked to
10389  Assert(goal_objp != NULL);
10390  docker_index = dock_find_dockpoint_used_by_object(objp, goal_objp);
10391  dockee_index = dock_find_dockpoint_used_by_object(goal_objp, objp);
10392  Assert(docker_index >= 0);
10393  Assert(dockee_index >= 0);
10394  break;
10395  }
10396 
10397  case AIS_UNDOCK_3:
10398  case AIS_UNDOCK_4:
10399  {
10400  Warning(LOCATION, "Normally dock indexes should be calculated for only AIS_DOCK_0 and AIS_UNDOCK_0. Additionally, dock indexes can't always be determined for AIS_UNDOCK_3 or AIS_UNDOCK_4. Trace out and debug.");
10401  docker_index = -1;
10402  dockee_index = -1;
10403  break;
10404  }
10405 
10406  default:
10407  {
10408  Error(LOCATION, "Unknown docking submode!");
10409  docker_index = -1;
10410  dockee_index = -1;
10411  break;
10412  }
10413  }
10414 }
10415 
10416 // Goober5000 - clean up my own dock mode
10418 {
10419  ship *shipp = &Ships[objp->instance];
10420 
10421  // get ai of object
10422  ai_info *aip = &Ai_info[shipp->ai_index];
10423 
10424  // if the object is in dock mode, force them to near last stage
10425  if ( (aip->mode == AIM_DOCK) && (aip->submode < AIS_UNDOCK_3) )
10426  {
10427  // get the object being acted upon
10428  object *goal_objp;
10429  ship *goal_shipp;
10430  if (aip->goal_objnum >= 0)
10431  {
10432  goal_objp = &Objects[aip->goal_objnum];
10433  Assert(goal_objp->type == OBJ_SHIP);
10434  goal_shipp = &Ships[goal_objp->instance];
10435  }
10436  else
10437  {
10438  goal_objp = NULL;
10439  goal_shipp = NULL;
10440  }
10441 
10442  // get the indexes from the saved parameters
10443  int docker_index = aip->submode_parm0;
10444  int dockee_index = aip->submode_parm1;
10445 
10446  // undo all the appropriate triggers
10447  switch (aip->submode)
10448  {
10449  case AIS_UNDOCK_0:
10450  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
10451  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
10452  case AIS_UNDOCK_1:
10453  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
10454  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
10455  case AIS_UNDOCK_2:
10456  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
10457  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
10458  break;
10459 
10460  case AIS_DOCK_4:
10461  case AIS_DOCK_4A:
10462  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
10463  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
10464  case AIS_DOCK_3:
10465  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
10466  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
10467  case AIS_DOCK_2:
10468  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
10469  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
10470  break;
10471  }
10472 
10473  aip->submode = AIS_UNDOCK_3;
10475  }
10476 }
10477 
10478 // Goober5000 - clean up the dock mode of everybody around me
10479 // (This function should ONLY need to be called from a ship doing a deathroll.
10480 // It ensures that any ship docking or undocking with it will finish gracefully.)
10482 {
10483  // process all directly docked objects
10484  for (dock_instance *ptr = objp->dock_list; ptr != NULL; ptr = ptr->next)
10485  ai_cleanup_dock_mode_subjective(ptr->docked_objp);
10486 }
10487 
10488 // Goober5000
10489 // (This function should ONLY need to be called from a ship doing a deathroll.
10490 // It ensures that any support ship stuff will be wrapped up gracefully.)
10492 {
10493  ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
10494 
10495  if (aip->ai_flags & AIF_REPAIRING) {
10496  Assert( aip->goal_objnum != -1 );
10498  } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
10499  // MWA/Goober5000 -- note that we have to use support object here instead of goal_objnum.
10500  Assert( aip->support_ship_objnum != -1 );
10502  } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
10503  // MWA/Goober5000 -- note that we have to use support object here instead of goal_objnum.
10504  // MWA -- 3/38/98 Check to see if this guy is queued for a support ship, or there is already
10505  // one in the mission
10506  if ( mission_is_repair_scheduled(objp) ) {
10507  mission_remove_scheduled_repair( objp ); // this function will notify multiplayer clients.
10508  } else {
10509  if ( aip->support_ship_objnum != -1 )
10511  else
10513  }
10514  }
10515 }
10516 
10517 // Make Pl_objp point at aip->goal_point.
10518 void ai_still()
10519 {
10520  ship *shipp;
10521  ai_info *aip;
10522 
10523  Assert(Pl_objp->type == OBJ_SHIP);
10524  Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
10525 
10526  shipp = &Ships[Pl_objp->instance];
10527  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
10528 
10529  aip = &Ai_info[shipp->ai_index];
10530 
10531  turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
10532 }
10533 
10534 // Make *Pl_objp stay near another ship.
10536 {
10537  ai_info *aip;
10538  int goal_objnum;
10539 
10540  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
10541 
10542  goal_objnum = aip->goal_objnum;
10543 
10544  if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
10545  aip->mode = AIM_NONE;
10546  } else {
10547  float dist, max_dist, scale;
10548  vec3d rand_vec, goal_pos, vec_to_goal;
10549  object *goal_objp;
10550 
10551  goal_objp = &Objects[goal_objnum];
10552 
10553  // Make not all ships pursue same point.
10554  static_randvec(OBJ_INDEX(Pl_objp), &rand_vec);
10555 
10556  // Make sure point is in front hemisphere (relative to Pl_objp's position.
10557  vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
10558  if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
10559  vm_vec_negate(&rand_vec);
10560  }
10561 
10562  // Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
10563  dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
10564  max_dist = aip->stay_near_distance;
10565  scale = dist - max_dist/2;
10566  if (scale < 0.0f)
10567  scale = 0.0f;
10568 
10569  vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
10570 
10571  if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
10572  max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
10573 
10574  if (dist > max_dist) {
10575  turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
10576  accelerate_ship(aip, dist / max_dist - 0.8f);
10577  }
10578 
10579  }
10580 
10581 }
10582 
10583 // Warn player if dock path is obstructed.
10584 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
10585 {
10586  vec3d *goalpos, *curpos;
10587  float radius;
10588  ai_info *aip;
10589  int collide_objnum;
10590 
10591  aip = &Ai_info[Ships[cur_objp->instance].ai_index];
10592 
10593  Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
10594 
10595  if (goal_objp != Player_obj)
10596  return -1;
10597 
10598  curpos = &cur_objp->pos;
10599  radius = cur_objp->radius;
10600  goalpos = &Path_points[aip->path_cur].pos;
10601  collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
10602 
10603  if (collide_objnum != -1)
10604  Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
10605 
10606  return collide_objnum;
10607 }
10608 
10609 
10610 // Docking behavior.
10611 // Approach a ship, follow path to docking platform, approach platform; after awhile,
10612 // undock.
10613 void ai_dock()
10614 {
10615  ship *shipp = &Ships[Pl_objp->instance];
10616  ai_info *aip = &Ai_info[shipp->ai_index];
10617 
10618  // Make sure we still have a dock goal.
10619  // Make sure the object we're supposed to dock with or undock from still exists.
10620  if ( ((aip->active_goal < 0) && (aip->submode != AIS_DOCK_4A))
10621  || (aip->goal_objnum < 0)
10622  || (Objects[aip->goal_objnum].signature != aip->goal_signature) )
10623  {
10625  }
10626 
10627  ship_info *sip = &Ship_info[shipp->ship_info_index];
10628 
10629  // we need to keep the dock indexes stored in the submode because the goal may become invalid at any point
10630  // (when we first dock or first undock, we'll calculate and overwrite these for the first time)
10631  int docker_index = aip->submode_parm0;
10632  int dockee_index = aip->submode_parm1;
10633 
10634  // get the active goal
10635  ai_goal *aigp;
10636  if (aip->active_goal >= 0)
10637  aigp = &aip->goals[aip->active_goal];
10638  else
10639  aigp = NULL;
10640 
10641  // get the object being acted upon
10642  object *goal_objp;
10643  ship *goal_shipp;
10644  if (aip->goal_objnum >= 0)
10645  {
10646  goal_objp = &Objects[aip->goal_objnum];
10647  Assert(goal_objp->type == OBJ_SHIP);
10648  goal_shipp = &Ships[goal_objp->instance];
10649  }
10650  else
10651  {
10652  goal_objp = NULL;
10653  goal_shipp = NULL;
10654  }
10655 
10656 
10657  // For docking submodes (ie, not undocking), follow path. Once at second last
10658  // point on path (point just before point on dock platform), orient into position.
10659  //
10660  // For undocking, first mode pushes docked ship straight back from docking point
10661  // second mode turns ship and moves to point on docking radius
10662  switch (aip->submode)
10663  {
10664 
10665  // This mode means to find the path to the docking point.
10666  case AIS_DOCK_0:
10667  {
10668  // save the dock indexes we're currently using
10669  ai_get_dock_goal_indexes(Pl_objp, aip, aigp, goal_objp, docker_index, dockee_index);
10670  aip->submode_parm0 = docker_index;
10671  aip->submode_parm1 = dockee_index;
10672 
10673  ai_path();
10674  if (aip->path_length < 4)
10675  {
10676  Assert(goal_objp != NULL);
10677  ship_info *goal_sip = &Ship_info[goal_shipp->ship_info_index];
10678  char *goal_ship_class_name = goal_sip->name;
10679  char *goal_dock_path_name = model_get(goal_sip->model_num)->paths[aip->mp_index].name;
10680 
10681  Warning(LOCATION, "Ship class %s has only %i points on dock path \"%s\". Recommended minimum number of points is 4. "\
10682  "Docking along that path will look strange. You may wish to edit the model.", goal_ship_class_name, aip->path_length, goal_dock_path_name);
10683  }
10684 
10685  aip->submode = AIS_DOCK_1;
10687  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_1, docker_index, 1);
10688  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, 1);
10689 
10690  aip->path_start = -1;
10691  break;
10692  }
10693 
10694  // This mode means to follow the path until just before the end.
10695  case AIS_DOCK_1:
10696  {
10697  int r;
10698 
10699  if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10700  int r1;
10701  if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10702  nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10703  break;
10704  }
10705  } //else {
10706  {
10707  ai_path();
10708 
10709  if (aip->path_cur-aip->path_start >= aip->path_length-1) { // If got this far, advance no matter what.
10710  aip->submode = AIS_DOCK_2;
10712  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, 1);
10713  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, 1);
10714 
10715  aip->path_cur--;
10716  Assert(aip->path_cur-aip->path_start >= 0);
10717  } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10718  if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10719  set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10720  } else {
10721  aip->submode = AIS_DOCK_2;
10723  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, 1);
10724  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, 1);
10725  }
10726  }
10727  }
10728  break;
10729  }
10730 
10731  // This mode means to drag oneself right to the second last point on the path.
10732  // Path code allows it to overshoot.
10733  case AIS_DOCK_2:
10734  {
10735  float dist;
10736  int r;
10737 
10738  if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 0)) != -1) {
10739  nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10740  accelerate_ship(aip, 0.0f);
10741 
10742  aip->submode = AIS_DOCK_1;
10744  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
10745  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
10746  } else {
10747  dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_APPROACH);
10748  Assert(dist != UNINITIALIZED_VALUE);
10749 
10750  float tolerance;
10751  if (goal_objp->flags & OF_PLAYER_SHIP)
10752  tolerance = 6*flFrametime + 1.0f;
10753  else
10754  tolerance = 4*flFrametime + 0.5f;
10755 
10756  if ( dist < tolerance) {
10757  aip->submode = AIS_DOCK_3;
10759  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, 1);
10760  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, 1);
10761 
10762  aip->path_cur++;
10763  }
10764  }
10765  break;
10766  }
10767 
10768  case AIS_DOCK_3:
10769  {
10770  Assert(aip->goal_objnum != -1);
10771  int r;
10772 
10773  if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10774  nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10775  accelerate_ship(aip, 0.0f);
10776 
10777  aip->submode = AIS_DOCK_2;
10779  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
10780  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
10781  } else {
10782  rotating_dockpoint_info rdinfo;
10783 
10784  float dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_DOCK, &rdinfo);
10785  Assert(dist != UNINITIALIZED_VALUE);
10786 
10787  float tolerance = 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed));
10788 
10789  // Goober5000
10790  if (rdinfo.submodel >= 0)
10791  {
10792  tolerance += 4*flFrametime * (rdinfo.submodel_r * rdinfo.submodel_w);
10793  }
10794 
10795  if (dist < tolerance)
10796  {
10797  // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10798  dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_DOCK);
10799  Assert(dist != UNINITIALIZED_VALUE);
10800 
10801  physics_ship_init(Pl_objp);
10802 
10803  ai_do_objects_docked_stuff( Pl_objp, docker_index, goal_objp, dockee_index );
10804 
10805  if (aip->submode == AIS_DOCK_3) {
10806  // Play a ship docking attach sound
10807  snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10808 
10809  // start the dock animation
10810  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, 1);
10811  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, 1);
10812 
10813  if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10814  joy_ff_docked(); // shake player's joystick a little
10815  }
10816 
10817  // If this ship is repairing another ship...
10818  if (aip->ai_flags & AIF_REPAIRING) {
10819  aip->submode = AIS_DOCK_4; // Special rearming only dock mode.
10821  } else {
10822  aip->submode = AIS_DOCK_4A;
10824  }
10825  }
10826  }
10827  break;
10828  }
10829 
10830  // Yes, we just sit here. We wait for further orders. No, it's not a bug.
10831  case AIS_DOCK_4A:
10832  {
10833  if (aigp == NULL) { // Can happen for initially docked ships.
10834  // this now "just sits here" for docked ships
10835  ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] ); // do the default behavior
10836  } else {
10838 
10839  if (aigp->ai_mode == AI_GOAL_DOCK) {
10840  ai_mission_goal_complete( aip ); // Note, this calls ai_do_default_behavior().
10841  }
10842  }
10843 
10844  break;
10845  }
10846 
10847  case AIS_DOCK_4:
10848  {
10849  // This mode is only for rearming/repairing.
10850  // The ship that is performing the rearm enters this mode after it docks.
10851  Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10852 
10853  float dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_DOCK);
10854  Assert(dist != UNINITIALIZED_VALUE);
10855 
10856  ai_info *goal_aip = &Ai_info[goal_shipp->ai_index];
10857 
10858  // Goober5000 - moved from call_doa
10859  // Abort if the ship being repaired exceeds my max speed
10860  if (goal_objp->phys_info.speed > MAX_REPAIR_SPEED)
10861  {
10862  // call the ai_abort rearm request code
10863  ai_abort_rearm_request( Pl_objp );
10864  // Goober5000 - add missionlog for support ships, per Mantis #2999
10866  }
10867  // Make sure repair has not broken off.
10868  else if (dist > 5.0f) // Oops, too far away!
10869  {
10870  if ( goal_aip->ai_flags & AIF_BEING_REPAIRED ) {
10871  ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10872  // Goober5000 - add missionlog for support ships, per Mantis #2999
10874  }
10875 
10876  if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10877  // Got real far away from goal, so move back a couple modes and try again.
10878  aip->submode = AIS_DOCK_2;
10880  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
10881  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
10882  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
10883  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
10884  }
10885  }
10886  else
10887  {
10888  if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR ) {
10889  ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10890  // Goober5000 - add missionlog for support ships, per Mantis #2999
10892  }
10893  }
10894 
10895  break;
10896  }
10897 
10898  case AIS_UNDOCK_0:
10899  {
10900  // First stage of undocking.
10901  int path_num;
10902 
10903  // If this is the first frame for this submode, play the animation and set the timestamp
10904  if (aip->mode_time < 0)
10905  {
10906  // save the dock indexes we're currently using
10907  ai_get_dock_goal_indexes(Pl_objp, aip, aigp, goal_objp, docker_index, dockee_index);
10908  aip->submode_parm0 = docker_index;
10909  aip->submode_parm1 = dockee_index;
10910 
10911  // start the detach animation (opposite of the dock animation)
10912  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKED, docker_index, -1);
10913  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index, -1);
10914 
10915  // calculate time until animations elapse
10916  int time1 = model_anim_get_time_type(shipp, TRIGGER_TYPE_DOCKED, docker_index);
10917  int time2 = model_anim_get_time_type(goal_shipp, TRIGGER_TYPE_DOCKED, dockee_index);
10918  aip->mode_time = MAX(time1, time2);
10919  }
10920 
10921  // if not enough time has passed, just wait
10922  if (!timestamp_elapsed(aip->mode_time))
10923  break;
10924 
10925  // clear timestamp
10926  aip->mode_time = -1;
10927 
10928  // set up the path points for the undocking procedure
10929  path_num = ai_return_path_num_from_dockbay(goal_objp, dockee_index);
10930  Assert(path_num >= 0);
10931  ai_find_path(Pl_objp, OBJ_INDEX(goal_objp), path_num, 0);
10932 
10933  // Play a ship docking detach sound
10934  snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10935 
10936  aip->submode = AIS_UNDOCK_1;
10938  break;
10939  }
10940 
10941  case AIS_UNDOCK_1:
10942  {
10943  // Using thrusters, exit from dock station to nearest next dock path point.
10944  float dist;
10945  rotating_dockpoint_info rdinfo;
10946 
10947  // Waiting for one second to elapse to let detach sound effect play out.
10949  break;
10950 
10951  // play the depart sound, but only once, since this mode is called multiple times per frame
10952  if ( !(aigp->flags & AIGF_DEPART_SOUND_PLAYED))
10953  {
10954  snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10956  }
10957 
10958  dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_UNDOCK_1, &rdinfo);
10959  Assert(dist != UNINITIALIZED_VALUE);
10960 
10961  float dist_to_dock;
10962 
10963  // Goober5000 - if via submodel, calc distance to point, not center
10964  if (rdinfo.submodel >= 0)
10965  dist_to_dock = vm_vec_dist_quick(&Pl_objp->pos, &rdinfo.dockee_point);
10966  else
10967  dist_to_dock = vm_vec_dist_quick(&Pl_objp->pos, &goal_objp->pos);
10968 
10969  // Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10970  // This allows undock to complete if first ship flies away.
10971  if ((dist < 2*flFrametime) || (dist_to_dock > 2*Pl_objp->radius)) {
10972  aip->submode = AIS_UNDOCK_2;
10974  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_3, docker_index, -1);
10975  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_3, dockee_index, -1);
10976  }
10977  break;
10978  }
10979 
10980  case AIS_UNDOCK_2:
10981  {
10982  float dist;
10983 
10984  // get pointer to docked object's aip to reset flags, etc
10985  Assert( aip->goal_objnum != -1 );
10986 
10987  // Second stage of undocking.
10988  dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_UNDOCK_2);
10989  Assert(dist != UNINITIALIZED_VALUE);
10990 
10991  // If at goal point, or quite far away from dock object
10992  // NOTE: the speed check has an etra 5 thousandths added on to account for some floating point error
10993  if ((dist < 2.0f) || (vm_vec_dist_quick(&Pl_objp->pos, &goal_objp->pos) > (Pl_objp->radius + goal_objp->radius)*2) || ((goal_objp->phys_info.speed + 0.005f) > MAX_UNDOCK_ABORT_SPEED) ) {
10994  // reset the dock flags. If rearm/repair, reset rearm repair flags for those ships as well.
10995  if ( sip->flags & SIF_SUPPORT ) {
10997  }
10998 
10999  // clear out dock stuff for both objects.
11000  ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
11001  physics_ship_init(Pl_objp);
11002 
11003  aip->submode = AIS_UNDOCK_3;
11005  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_2, docker_index, -1);
11006  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_2, dockee_index, -1);
11007 
11008  // don't add undock log entries for support ships.
11009  // Goober5000 - deliberately contravening Volition's above comment - add missionlog for support ships, per Mantis #2999
11011  }
11012  break;
11013  }
11014 
11015  case AIS_UNDOCK_3:
11016  {
11017  if (goal_objp == NULL)
11018  {
11019  // this might happen when a goal is cancelled before docking has finished
11020  aip->submode = AIS_UNDOCK_4;
11022  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_1, docker_index, -1);
11023  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, -1);
11024  }
11025  else
11026  {
11027  float dist = dock_orient_and_approach(Pl_objp, docker_index, goal_objp, dockee_index, DOA_UNDOCK_3);
11028  Assert(dist != UNINITIALIZED_VALUE);
11029 
11030  if (dist < Pl_objp->radius/2 + 5.0f) {
11031  aip->submode = AIS_UNDOCK_4;
11033  model_anim_start_type(shipp, TRIGGER_TYPE_DOCKING_STAGE_1, docker_index, -1);
11034  model_anim_start_type(goal_shipp, TRIGGER_TYPE_DOCKING_STAGE_1, dockee_index, -1);
11035  }
11036 
11037  // possible that this flag hasn't been cleared yet. When aborting a rearm, this submode might
11038  // be entered directly.
11039  if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
11040  ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_ABORT );
11041  }
11042  }
11043 
11044  break;
11045  }
11046 
11047  case AIS_UNDOCK_4:
11048  {
11049 
11050  aip->mode = AIM_NONE;
11051 
11052  // only call mission goal complete if this was indeed an undock goal
11053  if ( aip->active_goal >= 0 ) {
11054  if ( aigp->ai_mode == AI_GOAL_UNDOCK )
11055  ai_mission_goal_complete( aip ); // this call should reset the AI mode
11056  }
11057 
11058  break;
11059  }
11060 
11061  default:
11062  {
11063  Int3(); // Error, bogus submode
11064  }
11065 
11066  } // end of switch statement
11067 }
11068 
11069 #ifndef NDEBUG
11070 
11071 #define MAX_AI_DEBUG_RENDER_STUFF 100
11072 typedef struct ai_render_stuff {
11075 } ai_render_stuff;
11076 
11078 
11080 
11082 {
11083 
11084  vertex vert1, vert2;
11085  vec3d gpos2;
11086  int i;
11087 
11088  for (i=0; i<Num_AI_debug_render_stuff; i++) {
11089  ship_subsys *ss;
11090  int parent_objnum;
11091  vec3d gpos, gvec;
11092  model_subsystem *tp;
11093 
11094  ss = AI_debug_render_stuff[i].ss;
11095  tp = ss->system_info;
11096 
11097  parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11098 
11099  ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11100  g3_rotate_vertex(&vert1, &gpos);
11101  vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11102  g3_rotate_vertex(&vert2, &gpos2);
11103  gr_set_color(0, 0, 255);
11104  g3_draw_sphere(&vert1, 2.0f);
11105  gr_set_color(255, 0, 255);
11106  g3_draw_sphere(&vert2, 2.0f);
11107  g3_draw_line(&vert1, &vert2);
11108  }
11109 
11110  Num_AI_debug_render_stuff = 0;
11111 }
11112 
11113 #endif
11114 
11115 
11116 
11117 // --------------------------------------------------------------------------
11118 // Process subobjects of object objnum.
11119 // Deal with engines disabled.
11120 void process_subobjects(int objnum)
11121 {
11122  ship_subsys *pss;
11123  object *objp = &Objects[objnum];
11124  ship *shipp = &Ships[objp->instance];
11125  ai_info *aip = &Ai_info[shipp->ai_index];
11126  ship_info *sip = &Ship_info[shipp->ship_info_index];
11127 
11128  //Look for enemies. If none are present, we don't have to move turrets
11129  int enemies_present = -1;
11130 
11131  model_subsystem *psub;
11132  for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11133  psub = pss->system_info;
11134 
11135  // Don't process destroyed objects (but allow subobjects with hitpoints disabled -nuke) (but also process subobjects that are allowed to rotate)
11136  if (pss->max_hits > 0 && pss->current_hits <= 0.0f && !(psub->flags2 & MSS_FLAG2_DESTROYED_ROTATION))
11137  continue;
11138 
11139  switch (psub->type) {
11140  case SUBSYSTEM_TURRET:
11141  // handle ending animations
11145  }
11146  }
11147 
11148  if ( psub->turret_num_firing_points > 0 )
11149  {
11150  if(enemies_present == -1)
11151  {
11152  enemies_present = 0;
11153  for(unsigned int i = 0; i < MAX_OBJECTS; i++)
11154  {
11155  objp = &Objects[i];
11156  switch(objp->type)
11157  {
11158  case OBJ_SHIP:
11159  case OBJ_DEBRIS:
11160  case OBJ_WEAPON:
11161  if(obj_team(objp) != shipp->team)
11162  enemies_present = 1;
11163  break;
11164  case OBJ_ASTEROID:
11165  enemies_present = 1;
11166  break;
11167  }
11168 
11169  if(enemies_present==1)
11170  break;
11171  }
11172  //Reset objp
11173  objp = &Objects[objnum];
11174  }
11175  //Only move turrets if enemies are present
11176  if(enemies_present == 1 || pss->turret_enemy_objnum >= 0)
11177  ai_fire_from_turret(shipp, pss, objnum);
11178  } else {
11179  Warning( LOCATION, "Turret %s on ship %s has no firing points assigned to it.\nThis needs to be fixed in the model.\n", psub->name, shipp->ship_name );
11180  }
11181  break;
11182 
11183  case SUBSYSTEM_ENGINE:
11184  case SUBSYSTEM_NAVIGATION:
11186  case SUBSYSTEM_WEAPONS:
11187  case SUBSYSTEM_SENSORS:
11188  case SUBSYSTEM_UNKNOWN:
11189  break;
11190 
11191  // next set of subsystems may rotation
11192  case SUBSYSTEM_RADAR:
11193  case SUBSYSTEM_SOLAR:
11194  case SUBSYSTEM_GAS_COLLECT:
11195  case SUBSYSTEM_ACTIVATION:
11196  break;
11197  default:
11198  Error(LOCATION, "Illegal subsystem type.\n");
11199  }
11200 
11201  // do solar/radar/gas/activator rotation here
11202  ship_do_submodel_rotation(shipp, psub, pss);
11203  }
11204 
11205  // Deal with a ship with blown out engines.
11206  if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11207  // Karajorma - if Player_use_ai is ever fixed to work on multiplayer it should be checked that any player ships
11208  // aren't under AI control here
11209  if ( (!(objp->flags & OF_PLAYER_SHIP) ) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) && !(shipp->flags & SF_DYING) ) {
11210  // Goober5000 - don't do anything if docked
11211  if (!object_is_docked(objp)) {
11212  // AL: Only attack forever if not trying to depart to a docking bay. Need to have this in, since
11213  // a ship may get repaired... and it should still try to depart. Since docking bay departures
11214  // are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11215  if ( aip->mode != AIM_BAY_DEPART ) {
11216  ai_attack_object(objp, NULL, NULL); // Regardless of current mode, enter attack mode.
11217  aip->submode = SM_ATTACK_FOREVER; // Never leave attack submode, don't avoid, evade, etc.
11219  }
11220  }
11221  }
11222  }
11223 }
11224 
11225 // Given an object and the wing it's in, return its index in the wing list.
11226 // This defines its location in the wing formation.
11227 // If the object can't be found in the wing, return -1.
11228 // *objp object of interest
11229 // wingnum the wing *objp is in
11230 int get_wing_index(object *objp, int wingnum)
11231 {
11232  wing *wingp;
11233  int i;
11234 
11235  Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11236 
11237  wingp = &Wings[wingnum];
11238 
11239  for (i=wingp->current_count-1; i>=0; i--)
11240  if ( objp->instance == wingp->ship_index[i] )
11241  break;
11242 
11243  return i; // Note, returns -1 if string not found.
11244 }
11245 
11246 // Given a wing, return a pointer to the object of its leader.
11247 // Asserts if object not found.
11248 // Currently, the wing leader is defined as the first object in the wing.
11249 // wingnum Wing number in Wings array.
11250 // If wing leader is disabled, swap it with another ship.
11251 object *get_wing_leader(int wingnum)
11252 {
11253  wing *wingp;
11254  int ship_num = 0;
11255 
11256  if (wingnum < 0) {
11257  return NULL;
11258  }
11259 
11260  Assert( wingnum < MAX_WINGS );
11261 
11262  wingp = &Wings[wingnum];
11263 
11264  Assert(wingp->current_count != 0); // Make sure there is a leader
11265 
11266  ship_num = wingp->ship_index[0];
11267 
11268  // If this ship is disabled, try another ship in the wing.
11269  int n = 0;
11270  while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11271  n++;
11272 
11273  if (n >= wingp->current_count) {
11274  break;
11275  }
11276 
11277  ship_num = wingp->ship_index[n];
11278  }
11279 
11280  if ( (n != 0) && (n != wingp->current_count) ) {
11281  int t = wingp->ship_index[0];
11282  wingp->ship_index[0] = wingp->ship_index[n];
11283  wingp->ship_index[n] = t;
11284  }
11285 
11286  return &Objects[Ships[ship_num].objnum];
11287 }
11288 
11289 #define DEFAULT_WING_X_DELTA 1.0f
11290 #define DEFAULT_WING_Y_DELTA 0.25f
11291 #define DEFAULT_WING_Z_DELTA 0.75f
11292 #define DEFAULT_WING_MAG (fl_sqrt(DEFAULT_WING_X_DELTA*DEFAULT_WING_X_DELTA + DEFAULT_WING_Y_DELTA*DEFAULT_WING_Y_DELTA + DEFAULT_WING_Z_DELTA*DEFAULT_WING_Z_DELTA))
11293 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11294 #define MAX_FORMATION_ROWS 4
11295 
11296 // Given a position in a wing, return the desired location of the ship relative to the leader
11297 // *_delta_vec OUTPUT. delta vector based on wing_index
11298 // wing_index position in wing.
11299 void get_wing_delta(vec3d *_delta_vec, int wing_index)
11300 {
11301  int wi0;
11302 
11303  Assert(wing_index >= 0);
11304 
11305  int k, row, column;
11306 
11307  int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11308  wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11309 
11310  k = 0;
11311  for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11312  k += row;
11313  if (wi0 < k)
11314  break;
11315  }
11316 
11317  row--;
11318  column = wi0 - k + row + 1;
11319 
11320  _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11321  _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11322  _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11323 
11324 }
11325 
11329 float gwlr_1(object *objp, ai_info *aip)
11330 {
11331  int wingnum = aip->wing;
11332  float max_radius;
11333  object *o;
11334  ship_obj *so;
11335 
11336  Assert(wingnum >= 0);
11337 
11338  max_radius = objp->radius;
11339 
11340  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11341  o = &Objects[so->objnum];
11342  if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11343  if (o->radius > max_radius)
11344  max_radius = o->radius;
11345  }
11346 
11347  return max_radius;
11348 }
11349 
11353 float gwlr_object_1(object *objp, ai_info *aip)
11354 {
11355  float max_radius;
11356  object *o;
11357  ship_obj *so;
11358 
11359  max_radius = objp->radius;
11360 
11361  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11362  o = &Objects[so->objnum];
11363  if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11364  if (o->radius > max_radius)
11365  max_radius = o->radius;
11366  }
11367 
11368  return max_radius;
11369 }
11370 
11374 float get_wing_largest_radius(object *objp, int formation_object_flag)
11375 {
11376  ship *shipp;
11377  ai_info *aip;
11378 
11379  Assert(objp->type == OBJ_SHIP);
11380  Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11381  shipp = &Ships[objp->instance];
11382  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11383  aip = &Ai_info[shipp->ai_index];
11384 
11385  if (formation_object_flag) {
11386  return gwlr_object_1(objp, aip);
11387  } else {
11388  return gwlr_1(objp, aip);
11389  }
11390 
11391 }
11392 
11393 float Wing_y_scale = 2.0f;
11394 float Wing_scale = 1.0f;
11395 DCF(wing_y_scale, "Adjusts the wing formation scale along the Y axis (Default is 2.0)")
11396 {
11398 }
11399 
11400 DCF(wing_scale, "Adjusts the wing formation scale. (Default is 1.0f)")
11401 {
11403 }
11404 
11409 void get_absolute_wing_pos(vec3d *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11410 {
11411  vec3d wing_delta, rotated_wing_delta;
11412  float wing_spread_size;
11413 
11414  get_wing_delta(&wing_delta, wing_index); // Desired location in leader's reference frame
11415 
11416  wing_spread_size = MAX(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11417 
11418  // for player obj (1) move ships up 20% (2) scale formation up 20%
11419  if (leader_objp->flags & OF_PLAYER_SHIP) {
11420  wing_delta.xyz.y *= Wing_y_scale;
11421  wing_spread_size *= Wing_scale;
11422  }
11423 
11424  vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11425 
11426  vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient); // Rotate into leader's reference.
11427 
11428  vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); // goal_point is absolute 3-space point.
11429 }
11430 
11431 // autopilot variant.. removes some scaling crap
11432 void get_absolute_wing_pos_autopilot(vec3d *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11433 {
11434  vec3d wing_delta, rotated_wing_delta;
11435  float wing_spread_size;
11436 
11437  get_wing_delta(&wing_delta, wing_index); // Desired location in leader's reference frame
11438  wing_spread_size = MAX(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11439 
11440  vm_vec_scale(&wing_delta, wing_spread_size * 1.5f);
11441  vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient); // Rotate into leader's reference.
11442  vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); // goal_point is absolute 3-space point.
11443 }
11444 
11445 #ifndef NDEBUG
11447 
11449 {
11450  int i;
11451  ship *shipp;
11452  ai_info *aip;
11453  int wingnum;
11454  int wing_index; // Index in wing struct, defines 3-space location in wing.
11455  vec3d goal_point;
11456 
11457  Assert(objp->type == OBJ_SHIP);
11458  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11459 
11460  shipp = &Ships[objp->instance];
11461  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11462 
11463  aip = &Ai_info[shipp->ai_index];
11464 
11465  wingnum = aip->wing;
11466 
11467  if (wingnum == -1)
11468  return;
11469 
11470  wing_index = get_wing_index(objp, wingnum);
11471 
11472  // If this ship is NOT the leader, abort.
11473  if (wing_index != 0)
11474  return;
11475 
11476  for (i=0; i<32; i++)
11477  if (Debug_render_wing_phantoms & (1 << i)) {
11478  get_absolute_wing_pos(&goal_point, objp, i, 0);
11479 
11480  vertex vert;
11481  gr_set_color(255, 0, 128);
11482  g3_rotate_vertex(&vert, &goal_point);
11483  g3_draw_sphere(&vert, 2.0f);
11484  }
11485 
11487 
11488 }
11489 
11491 {
11492  object *objp;
11493  ship_obj *so;
11494 
11495  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11496  ship *shipp;
11497  ai_info *aip;
11498  int wingnum;
11499  int wing_index; // Index in wing struct, defines 3-space location in wing.
11500 
11501  objp = &Objects[so->objnum];
11502 
11503  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11504  shipp = &Ships[objp->instance];
11505  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11506 
11507  aip = &Ai_info[shipp->ai_index];
11508 
11509  wingnum = aip->wing;
11510 
11511  if (wingnum == -1)
11512  continue;
11513 
11514  wing_index = get_wing_index(objp, wingnum);
11515 
11516  // If this ship is NOT the leader, abort.
11517  if (wing_index != 0)
11518  continue;
11519 
11520  render_wing_phantoms(objp);
11521 
11522  return;
11523  }
11524 }
11525 
11526 #endif
11527 
11528 // Hook from goals code to AI.
11529 // Force a wing to fly in formation.
11530 // Sets AIF_FORMATION bit in ai_flags.
11531 // wingnum Wing to force to fly in formation
11532 void ai_fly_in_formation(int wingnum)
11533 {
11534  object *objp;
11535  ship *shipp;
11536  ship_obj *so;
11537 
11538  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11539  objp = &Objects[so->objnum];
11540  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11541 
11542  shipp = &Ships[objp->instance];
11543  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11544 
11545  if (Ai_info[shipp->ai_index].wing == wingnum) {
11548  }
11549  }
11550 }
11551 
11552 // Hook from goals code to AI.
11553 // Force a wing to abandon formation flying.
11554 // Clears AIF_FORMATION bit in ai_flags.
11555 // wingnum Wing to force to fly in formation
11556 void ai_disband_formation(int wingnum)
11557 {
11558  object *objp;
11559  ship *shipp;
11560  ship_obj *so;
11561 
11562  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11563  objp = &Objects[so->objnum];
11564  Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11565 
11566  shipp = &Ships[objp->instance];
11567  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11568 
11569  if (Ai_info[shipp->ai_index].wing == wingnum) {
11571  }
11572  }
11573 }
11574 
11575 float Leader_chaos = 0.0f;
11576 int Chaos_frame = -1;
11577 
11578 // Return true if objp is flying in an erratic manner
11579 // Only true if objp is a player
11581 {
11582  if (Game_mode & GM_MULTIPLAYER)
11583  return 0;
11584 
11585  if (objp != Player_obj)
11586  return 0;
11587 
11588  if (Framecount != Chaos_frame) {
11589  float speed_scale;
11590  float fdot, udot;
11591 
11592  speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11593 
11594  fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.vec.fvec, &objp->last_orient.vec.fvec)) * flFrametime;
11595  udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.vec.uvec, &objp->last_orient.vec.uvec)) * flFrametime;
11596 
11597  Leader_chaos += fdot * speed_scale + udot * speed_scale;
11598 
11599  Leader_chaos *= (1.0f - flFrametime*0.2f);
11600 
11601  CLAMP(Leader_chaos, 0.0f, 1.7f);
11602 
11604  }
11605 
11606  return (Leader_chaos > 1.0f);
11607 }
11608 
11610 {
11611  // check that I am a ship
11612  if (objp->type == OBJ_SHIP)
11613  {
11614  // check that wings match
11616  {
11617  // if this guy has a higher mass, he is now the most massive object
11619  {
11621  }
11622  // if masses are equal, then check if this guy has a higher signature - if so, he is now the most massive object
11623  else if (objp->phys_info.mass == infop->maintained_variables.objp_value->phys_info.mass)
11624  {
11625  if (objp->signature > infop->maintained_variables.objp_value->signature)
11626  {
11628  }
11629  }
11630  }
11631  }
11632 }
11633 
11634 // Fly in formation.
11635 // Make Pl_objp assume its proper place in formation.
11636 // If the leader of the wing is doing something stupid, like fighting a battle,
11637 // then the poor sap wingmates will be in for a "world of hurt"
11638 // Return TRUE if we need to process this object's normal mode
11640 {
11641  object *leader_objp;
11642  ship *shipp;
11643  ai_info *aip, *laip;
11644  int wingnum;
11645  int wing_index; // Index in wing struct, defines 3-space location in wing.
11646  vec3d goal_point, future_goal_point_5, future_goal_point_2, future_goal_point_x, future_goal_point_1000x, vec_to_goal, dir_to_goal;
11647  float dot_to_goal, dist_to_goal, leader_speed;
11648 
11649  Assert(Pl_objp->type == OBJ_SHIP);
11650  Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11651 
11652  shipp = &Ships[Pl_objp->instance];
11653 
11654  Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11655 
11656  aip = &Ai_info[shipp->ai_index];
11657 
11658  Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION); // Make sure not both types of formation flying in effect.
11659 
11660  // Determine which kind of formation flying.
11661  // If tracking an object, not in waypoint mode:
11662  if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11663  if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS) || (aip->mode == AIM_BAY_DEPART)) {
11664  aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11665  return 1;
11666  }
11667 
11668  wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11669  leader_objp = &Objects[aip->goal_objnum];
11670  } else { // Formation flying in waypoint mode.
11672 
11673  if ( (aip->mode != AIM_WAYPOINTS) && (aip->mode != AIM_FLY_TO_SHIP) ) {
11674  aip->ai_flags &= ~AIF_FORMATION_WING;
11675  return 1;
11676  }
11677 
11678  wingnum = aip->wing;
11679 
11680  if (wingnum == -1) {
11681  return 1;
11682  }
11683 
11684  // disable formation flying for any ship in the players wing
11685  // ... except when using auto-pilot
11686  if ( !AutoPilotEngaged ) {
11687  if (wingnum == Ships[Player_obj->instance].wingnum) {
11688  return 1;
11689  }
11690  }
11691 
11692  wing_index = get_wing_index(Pl_objp, wingnum);
11693 
11694  leader_objp = get_wing_leader(wingnum);
11695  }
11696 
11697  // if Pl_objp is docked with a ship in his own wing, only the most massive one
11698  // in the whole assembly actually flies in formation
11699  // Goober5000 - this is really stupid code
11700  if ( object_is_docked(Pl_objp) ) {
11701  // assume I am the most massive
11702  dock_function_info dfi;
11703  dfi.parameter_variables.int_value = aip->wing;
11705 
11706  // check docked objects
11708 
11709  // if I am not the most massive, return
11710  if (dfi.maintained_variables.objp_value != Pl_objp)
11711  {
11712  return 0;
11713  }
11714  }
11715 
11716  Assert(leader_objp != NULL);
11717  laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11718 
11719  // Make sure we're really in this wing.
11720  if (wing_index == -1) {
11721  return 1;
11722  }
11723 
11724  // skip ourselves, if we happen to be the leader
11725  if (leader_objp == Pl_objp) {
11726  return 1;
11727  }
11728 
11729  if (aip->mode == AIM_WAYPOINTS) {
11730 
11732  // skip if wing leader has no waypoint order or a different waypoint list
11733  if ((laip->mode != AIM_WAYPOINTS) || !(aip->wp_list == laip->wp_list)){
11734  return 1;
11735  }
11736  }
11737 
11738  aip->wp_list = laip->wp_list;
11739  aip->wp_index = laip->wp_index;
11740  aip->wp_flags = laip->wp_flags;
11741 
11742  if ((aip->wp_list != NULL) && (aip->wp_index == aip->wp_list->get_waypoints().size()))
11743  --aip->wp_index;
11744  }
11745 
11746  #ifndef NDEBUG
11747  Debug_render_wing_phantoms |= (1 << wing_index);
11748  #endif
11749 
11750  leader_speed = leader_objp->phys_info.speed;
11751  vec3d leader_vec = leader_objp->phys_info.vel;
11752 
11753  get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11754  vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11755  vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11756  vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.vec.fvec, 10.0f); // used when very close to destination
11757  vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.vec.fvec, 1000.0f); // used when very close to destination
11758 
11759  // Now, get information telling this object how to turn and accelerate to get to its
11760  // desired location.
11761  vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11762  if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11763  vec_to_goal.xyz.x += 0.1f;
11764 
11765  vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11766  dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.vec.fvec);
11767  dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11768  float dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11769 
11770  int chaotic_leader = 0;
11771 
11772  chaotic_leader = formation_is_leader_chaotic(leader_objp); // Set to 1 if leader is player and flying erratically. Causes ships to not aggressively pursue formation location.
11773 
11774  ship_info *sip = &Ship_info[shipp->ship_info_index];
11775  bool ab_allowed = false;
11776  if ((sip->flags & SIF_AFTERBURNER) && !(shipp->flags2 & SF2_AFTERBURNER_LOCKED) && (aip->ai_flags & AIF_FREE_AFTERBURNER_USE)) {
11777  ab_allowed = true;
11778  } else {
11779  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11780  afterburners_stop(Pl_objp);
11781  }
11782 
11783  float switch_value = 0.0f;
11784  if (ab_allowed) {
11785  if ((Pl_objp->phys_info.afterburner_max_vel.xyz.z - leader_speed) > (0.2f * Pl_objp->phys_info.afterburner_max_vel.xyz.z)) {
11786  switch_value = 0.2f * Pl_objp->phys_info.afterburner_max_vel.xyz.z;
11787  } else {
11788  switch_value = Pl_objp->phys_info.afterburner_max_vel.xyz.z - leader_speed;
11789  }
11790  }
11791 
11792  if (dist_to_goal > 500.0f) {
11793  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11794  if (ab_allowed && !(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && (dot_to_goal > 0.2f)) {
11795  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
11796  if (percent_left > 30.0f) {
11797  afterburners_start(Pl_objp);
11798  aip->afterburner_stop_time = Missiontime + 5 * F1_0;
11799  }
11800  }
11801  accelerate_ship(aip, 1.0f);
11802  } else if (dist_to_goal > 200.0f) {
11803  if (dot_to_goal > -0.5f) {
11804  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11805  float range_speed = shipp->current_max_speed - leader_speed;
11806  if (range_speed > 0.0f) {
11807  set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11808  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11809  afterburners_stop(Pl_objp);
11810  } else {
11812  if (ab_allowed) {
11813  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && (dot_to_goal > 0.4f)) {
11814  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
11815  if ((percent_left > 30.0f) && (Pl_objp->phys_info.speed < leader_speed)) {
11816  afterburners_start(Pl_objp);
11817  aip->afterburner_stop_time = Missiontime + 5 * F1_0;
11818  }
11819  } else {
11820  if ((Pl_objp->phys_info.speed > leader_speed + 1.5 * switch_value) || (dot_to_goal < 0.2f))
11821  afterburners_stop(Pl_objp);
11822  }
11823  }
11824  }
11825  } else {
11826  turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11827  if (leader_speed > 10.0f)
11828  set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11829  else
11830  set_accel_for_target_speed(Pl_objp, 10.0f);
11831  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11832  afterburners_stop(Pl_objp);
11833 
11834  }
11835  } else {
11836  vec3d v2f2;
11837  float dot_to_f2;
11838 
11839  vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11840  dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.vec.fvec);
11841 
11842  // Leader flying like a maniac. Don't try hard to form on wing.
11843  if (chaotic_leader) {
11844  turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11845  set_accel_for_target_speed(Pl_objp, MIN(leader_speed*0.8f, 20.0f));
11846  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11847  afterburners_stop(Pl_objp);
11848  } else if (dist_to_goal > 75.0f) {
11849  turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11850  float delta_speed;
11851  float range_speed = shipp->current_max_speed - leader_speed;
11852  if (range_speed > 0.0f) {
11853  delta_speed = dist_to_goal_2/500.0f * range_speed;
11854  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11855  afterburners_stop(Pl_objp);
11856  } else {
11857  delta_speed = shipp->current_max_speed - leader_speed;
11858  if (ab_allowed) {
11859  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && (dot_to_goal > 0.6f)) {
11860  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
11861  if ((percent_left > 30.0f) && (Pl_objp->phys_info.speed < leader_speed - 0.5 * switch_value)) {
11862  afterburners_start(Pl_objp);
11863  aip->afterburner_stop_time = Missiontime + 5 * F1_0;
11864  }
11865  } else {
11866  if ((Pl_objp->phys_info.speed > leader_speed + 0.95 * switch_value) || (dot_to_goal < 0.4f))
11867  afterburners_stop(Pl_objp);
11868  }
11869  }
11870  }
11871  if (dot_to_goal < 0.0f) {
11872  delta_speed = -delta_speed;
11873  if (-delta_speed > leader_speed/2)
11874  delta_speed = -leader_speed/2;
11875  }
11876 
11877  if (leader_speed < 5.0f)
11878  if (delta_speed < 5.0f)
11879  delta_speed = 5.0f;
11880 
11881  float scale = dot_to_f2;
11882  if (scale < 0.1f)
11883  scale = 0.0f;
11884  else
11885  scale *= scale;
11886 
11887  set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11888  } else {
11889 
11890  if (leader_speed < 5.0f) {
11891  // Leader very slow. If not close to goal point, get very close. Note, keep trying to get close unless
11892  // moving very slowly, else momentum can carry far away from goal.
11893 
11894  if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11895  turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11896  set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11897  } else {
11898  if (Pl_objp->phys_info.speed < 0.5f) {
11899  turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11900  }
11901  set_accel_for_target_speed(Pl_objp, leader_speed);
11902  }
11903  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11904  afterburners_stop(Pl_objp);
11905 
11906  } else if (dist_to_goal > 10.0f) {
11907  float dv;
11908 
11909  turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11910 
11911  if (dist_to_goal > 25.0f) {
11912  if (dot_to_goal < 0.3f)
11913  dv = -0.1f;
11914  else
11915  dv = dot_to_goal - 0.2f;
11916 
11917  set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11918  } else {
11919  set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11920  }
11921 
11922  float range_speed = shipp->current_max_speed - leader_speed;
11923  if (range_speed > 0.0f) {
11924  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11925  afterburners_stop(Pl_objp);
11926  } else {
11927  if (ab_allowed) {
11928  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && (dot_to_goal > 0.9f)) {
11929  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
11930  if ((percent_left > 30.0f) && (Pl_objp->phys_info.speed < leader_speed - 0.75 * switch_value)) {
11931  afterburners_start(Pl_objp);
11932  aip->afterburner_stop_time = Missiontime + 3 * F1_0;
11933  }
11934  } else {
11935  if ((Pl_objp->phys_info.speed > leader_speed + 0.95 * switch_value) || (dot_to_goal < 0.7f))
11936  afterburners_stop(Pl_objp);
11937  }
11938  }
11939  }
11940 
11941  } else {
11942  if (Pl_objp->phys_info.speed < 0.1f)
11943  turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11944  else
11945  turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11946 
11947  // Goober5000 7/5/2006 changed to leader_speed from 0.0f
11948  set_accel_for_target_speed(Pl_objp, leader_speed);
11949 
11950  float range_speed = shipp->current_max_speed - leader_speed;
11951  if (range_speed > 0.0f) {
11952  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON)
11953  afterburners_stop(Pl_objp);
11954  } else {
11955  if (ab_allowed) {
11956  if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && (dot_to_goal > 0.9f)) {
11957  float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
11958  if ((percent_left > 30.0f) && (Pl_objp->phys_info.speed < leader_speed - 0.75 * switch_value)) {
11959  afterburners_start(Pl_objp);
11960  aip->afterburner_stop_time = Missiontime + 3 * F1_0;
11961  }
11962  } else {
11963  if ((Pl_objp->phys_info.speed > leader_speed + 0.75 * switch_value) || (dot_to_goal < 0.8f))
11964  afterburners_stop(Pl_objp);
11965  }
11966  }
11967  }
11968 
11969  }
11970  }
11971 
11972  }
11973 
11974  // See how different this ship's bank is relative to wing leader
11975  float up_dot = vm_vec_dot(&leader_objp->orient.vec.uvec, &Pl_objp->orient.vec.uvec);
11976  if (up_dot < 0.996f) {
11977  vec3d w_out;
11978  matrix new_orient;
11979  vec3d angular_accel;
11980 
11981  vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11982  vm_matrix_interpolate(&leader_objp->orient, &Pl_objp->orient, &Pl_objp->phys_info.rotvel, flFrametime, &new_orient, &w_out, &Pl_objp->phys_info.max_rotvel, &angular_accel, 1);
11983 
11984  Pl_objp->orient = new_orient;
11985  Pl_objp->phys_info.rotvel = w_out;
11986  } else {
11987  Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11988  }
11989 
11990  return 0;
11991 }
11992 
11996 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11997 {
11998  static bool rearm_eta_found=false;
11999 
12000  if (Ships[objp->instance].team == Iff_traitor) {
12001  ai_abort_rearm_request(objp);
12002  return;
12003  }
12004 
12006  int support_objnum;
12007 
12008  ai_info *repair_aip;
12009 
12010  support_objnum = aip->support_ship_objnum;
12011 
12012  if (support_objnum == -1)
12013  return;
12014 
12015  // Curious -- object numbers match, but signatures do not.
12016  // Must mean original repair ship died and was replaced by current ship.
12017  Assert(Objects[support_objnum].signature == aip->support_ship_signature);
12018 
12019  repair_aip = &Ai_info[Ships[Objects[support_objnum].instance].ai_index];
12020 
12021  if (aip->ai_flags & AIF_BEING_REPAIRED) {
12022 
12023  // Wait awhile into the mode to synchronize with sound effect.
12024  if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
12025  int repaired;
12026 
12027  if (!rearm_eta_found && objp==Player_obj && Player_rearm_eta <= 0)
12028  {
12029  rearm_eta_found = true;
12031  }
12032 
12033  repaired = ship_do_rearm_frame( objp, frametime ); // hook to do missile rearming
12034 
12035  // See if fully repaired. If so, cause process to stop.
12036  if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
12037 
12038  repair_aip->submode = AIS_UNDOCK_0;
12039  repair_aip->submode_start_time = Missiontime;
12040 
12041  // if repairing player object -- tell him done with repair
12042  if ( !MULTIPLAYER_CLIENT ) {
12043  ai_do_objects_repairing_stuff( objp, &Objects[support_objnum], REPAIR_INFO_COMPLETE );
12044  }
12045  }
12046  }
12047  } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
12048  // If this ship has been awaiting repair for 90+ seconds, abort.
12049  if ( !MULTIPLAYER_CLIENT ) {
12050  if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
12051  if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
12052  ai_abort_rearm_request(objp);
12054  }
12055  }
12056  }
12057  }
12058  } else {
12059  // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing. We need to
12060  // do this check, since this is a looping sound, and may continue on if rearm/repair
12061  // finishes abnormally once sound begins looping.
12062  if ( objp == Player_obj ) {
12063  Player_rearm_eta = 0;
12064  rearm_eta_found = false;
12066  }
12067  }
12068 }
12069 
12070 // Shell around dock_orient_and_approach to detect whether dock process should be aborted.
12071 // Goober5000 - The child should always keep up with the parent.
12072 void call_doa(object *child, object *parent)
12073 {
12075 
12076  // get indexes
12077  int parent_index = dock_find_dockpoint_used_by_object(parent, child);
12078  int child_index = dock_find_dockpoint_used_by_object(child, parent);
12079 
12080  // child keeps up with parent
12081  dock_orient_and_approach(child, child_index, parent, parent_index, DOA_DOCK_STAY);
12082 }
12083 
12084 // Maybe launch a countermeasure.
12085 // Also, detect a supposed homing missile that no longer exists.
12087 {
12088  float dist;
12089  ship_info *sip;
12090  ship *shipp;
12091 
12092  shipp = &Ships[objp->instance];
12093  sip = &Ship_info[shipp->ship_info_index];
12094 
12095  if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
12096  return;
12097 
12098  if (object_is_docked(objp))
12099  return;
12100 
12101  if (!shipp->cmeasure_count)
12102  return;
12103 
12104  if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12105  return;
12106 
12107  // If not on player's team and Skill_level + ai_class is low, never fire a countermeasure. The ship is too dumb.
12108  if (iff_x_attacks_y(Player_ship->team, shipp->team)) {
12109  //SUSHI: Only bail if autoscale is on...
12110  if (aip->ai_class_autoscale && Game_skill_level + aip->ai_class < 4){
12111  return;
12112  }
12113  }
12114 
12115  if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12116  object *weapon_objp;
12117 
12118  weapon_objp = &Objects[aip->nearest_locked_object];
12119 
12120  if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12121 
12122  aip->nearest_locked_distance = dist;
12123  // Verify that this object is really homing on us.
12124 
12125  float fire_chance;
12126 
12127  // For ships on player's team, have constant, average chance to fire.
12128  // For enemies, increasing chance with higher skill level.
12129  if (shipp->team == Player_ship->team)
12131  else
12132  fire_chance = aip->ai_cmeasure_fire_chance;
12133 
12134  // Decrease chance to fire at lower ai class (SUSHI: Only if autoscale is on)
12135  if (aip->ai_class_autoscale)
12136  fire_chance *= (float) aip->ai_class/Num_ai_classes;
12137 
12138  float r = frand();
12139  if (fire_chance < r) {
12140  shipp->cmeasure_fire_stamp = timestamp(CMEASURE_WAIT + (int) (fire_chance*2000)); // Wait 1/2 second (CMEASURE_WAIT) + additional delay to decrease chance of firing very soon.
12141  return;
12142  }
12143 
12144  if (weapon_objp->type == OBJ_WEAPON) {
12145  if (weapon_objp->instance >= 0) {
12148  return;
12149  }
12150  }
12151 
12152  }
12153  }
12154 
12155  return;
12156 }
12157 
12158 // --------------------------------------------------------------------------
12160 {
12161  if (aip->ai_flags & AIF_TEMPORARY_IGNORE)
12162  {
12165  }
12166 
12167  if (is_ignore_object(aip, aip->goal_objnum))
12168  {
12169  // you can land and launch on a ship you're ignoring!
12170  if (aip->mode != AIM_BAY_EMERGE && aip->mode != AIM_BAY_DEPART) {
12171  aip->goal_objnum = -1;
12172  }
12173 
12174  // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12175  // set to -1
12176  if (aip->mode == AIM_STRAFE)
12177  aip->target_objnum = -1;
12178  }
12179 
12180  if (is_ignore_object(aip, aip->target_objnum))
12181  aip->target_objnum = -1;
12182 }
12183 
12184 #define CHASE_CIRCLE_DIST 100.0f
12185 
12186 void ai_chase_circle(object *objp)
12187 {
12188  float dist_to_goal;
12189  float target_speed;
12190  vec3d goal_point;
12191  ship_info *sip;
12192  ai_info *aip;
12193 
12194  sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12195 
12196  target_speed = sip->max_speed/4.0f;
12197  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12198 
12199  Assert(vm_vec_mag(&aip->goal_point) >= 0.0f); // Supposedly detects bogus vector
12200 
12201  goal_point = aip->goal_point;
12202 
12203  if (aip->ignore_objnum == UNUSED_OBJNUM) {
12204  dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12205 
12206  if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12207  vec3d vec_to_goal;
12208  // Too far from circle goal, create a new goal point.
12209  vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12210  vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12211  }
12212 
12213  goal_point = aip->goal_point;
12214  } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12215  object *ignore_objp = &Objects[aip->ignore_objnum];
12216 
12217  vec3d tvec1;
12218  float dist;
12219 
12220  dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12221 
12222  if (dist < ignore_objp->radius*2 + 1500.0f) {
12223  vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12224  if (dist < ignore_objp->radius*2 + 1300.0f)
12225  target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12226  }
12227  }
12228 
12229  Assert(vm_vec_mag(&aip->goal_point) >= 0.0f); // Supposedly detects bogus vector
12230 
12231  turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12232 
12233  set_accel_for_target_speed(Pl_objp, target_speed);
12234 
12235 }
12236 
12237 #define SHIELD_BALANCE_RATE 0.2f // 0.1f -> takes 10 seconds to equalize shield.
12238 
12242 void ai_transfer_shield(object *objp, int quadrant_num)
12243 {
12244  int i;
12245  float transfer_amount;
12246  float transfer_delta;
12247  float max_quadrant_strength;
12248 
12249  max_quadrant_strength = get_max_shield_quad(objp);
12250 
12251  transfer_amount = 0.0f;
12252  transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12253 
12254  if (objp->shield_quadrant[quadrant_num] + (objp->n_quadrants-1)*transfer_delta > max_quadrant_strength)
12255  transfer_delta = (max_quadrant_strength - objp->shield_quadrant[quadrant_num])/(objp->n_quadrants-1);
12256 
12257  for (i=0; i<objp->n_quadrants; i++)
12258  if (i != quadrant_num) {
12259  if (objp->shield_quadrant[i] >= transfer_delta) {
12260  objp->shield_quadrant[i] -= transfer_delta;
12261  transfer_amount += transfer_delta;
12262  } else {
12263  transfer_amount += objp->shield_quadrant[i];
12264  objp->shield_quadrant[i] = 0.0f;
12265  }
12266  }
12267 
12268  objp->shield_quadrant[quadrant_num] += transfer_amount;
12269 }
12270 
12272 {
12273  int i;
12274  float shield_strength_avg;
12275  float delta;
12276 
12277  // if we are already at the max shield strength for all quads then just bail now
12279  return;
12280 
12281 
12282  shield_strength_avg = shield_get_strength(objp)/objp->n_quadrants;
12283 
12284  delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12285 
12286  for (i=0; i<objp->n_quadrants; i++) {
12287  if (objp->shield_quadrant[i] < shield_strength_avg) {
12288  // only do it the retail way if using smart shields (since that's a bigger thing) - taylor
12290  shield_add_strength(objp, delta);
12291  else
12292  objp->shield_quadrant[i] += delta/objp->n_quadrants;
12293 
12294  if (objp->shield_quadrant[i] > shield_strength_avg)
12295  objp->shield_quadrant[i] = shield_strength_avg;
12296 
12297  } else {
12298  // only do it the retail way if using smart shields (since that's a bigger thing) - taylor
12300  shield_add_strength(objp, -delta);
12301  else
12302  objp->shield_quadrant[i] -= delta/objp->n_quadrants;
12303 
12304  if (objp->shield_quadrant[i] < shield_strength_avg)
12305  objp->shield_quadrant[i] = shield_strength_avg;
12306  }
12307  }
12308 }
12309 
12310 // Manage the shield for this ship.
12311 // Try to max out the side that was most recently hit.
12312 void ai_manage_shield(object *objp, ai_info *aip)
12313 {
12314  ship_info *sip;
12315 
12316  sip = &Ship_info[Ships[objp->instance].ship_info_index];
12317 
12319  float delay;
12320 
12321  // Scale time until next manage shield based on Skill_level.
12322  // Ships on player's team are treated as if Skill_level is average.
12323  if (iff_x_attacks_y(Player_ship->team, Ships[objp->instance].team))
12324  {
12325  delay = aip->ai_shield_manage_delay;
12326  }
12327  else
12328  {
12330  }
12331 
12332  // Scale between 1x and 3x based on ai_class (SUSHI: only if autoscale is on)
12333  if (aip->ai_class_autoscale)
12334  delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12335 
12336  // set timestamp
12337  aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12338 
12340  if (Missiontime - aip->last_hit_time < F1_0*10)
12342  else
12343  ai_balance_shield(objp);
12344  }
12345  }
12346 }
12347 
12348 // See if object *objp should evade an incoming missile.
12349 // *aip is the ai_info pointer within *objp.
12351 {
12352  ship *shipp;
12353  shipp = &Ships[objp->instance];
12354 
12355  // Only small ships evade an incoming missile. Why would a capital ship try to swerve?
12356  if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12357  return;
12358  }
12359 
12360  // don't evade missiles if you're docked
12361  if (object_is_docked(objp)) {
12362  return;
12363  }
12364 
12365  if (aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) { // If not allowed to pursue dynamic objectives, don't evade. Dumb? Maybe change. -- MK, 3/15/98
12366  return;
12367  }
12368 
12369  if (aip->nearest_locked_object != -1) {
12370  object *missile_objp;
12371 
12372  missile_objp = &Objects[aip->nearest_locked_object];
12373 
12374  if (Weapons[missile_objp->instance].homing_object != objp) {
12375  aip->nearest_locked_object = -1;
12376  return;
12377  }
12378 
12379  if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12380  float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12381  float dist2 = 4.0f * vm_vec_mag_quick(&missile_objp->phys_info.vel);
12382  if (dist < dist2) {
12383  switch (aip->mode) {
12384  // If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12385  case AIM_STRAFE:
12386  if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12387  ;
12388  } else {
12389  ; // Alan -- If you want to handle incoming weapons from someone other than the ship
12390  // the strafing ship is attacking, do it here.
12391  }
12392  break;
12393  case AIM_CHASE:
12394  // Don't always go into evade weapon mode. Usually, a countermeasure gets launched.
12395  // If low on countermeasures, more likely to try to evade. If 8+, never evade due to low cmeasures.
12396  if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) ||
12397  (objp->phys_info.speed < 40.0f) ||
12398  (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12399  if (aip->submode != SM_ATTACK_FOREVER) { // SM_ATTACK_FOREVER means engines blown.
12400  aip->submode = SM_EVADE_WEAPON;
12402  }
12403  }
12404  break;
12405  case AIM_DOCK: // Ships in dock mode can evade iif they are not currently repairing or docked.
12407  break;
12408  case AIM_GUARD:
12409  // If in guard mode and far away from guard object, don't pursue guy that hit me.
12410  if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12411  if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12412  return;
12413  }
12414  }
12415  case AIM_EVADE:
12416  case AIM_GET_BEHIND:
12417  case AIM_STAY_NEAR:
12418  case AIM_STILL:
12419  case AIM_AVOID:
12420  case AIM_WAYPOINTS:
12421  case AIM_NONE:
12422  case AIM_BIGSHIP:
12423  case AIM_PATH:
12424  case AIM_BE_REARMED:
12425  case AIM_SAFETY:
12426  case AIM_BAY_EMERGE:
12427  case AIM_FLY_TO_SHIP:
12429  aip->previous_mode = aip->mode;
12430  aip->previous_submode = aip->submode;
12431  aip->mode = AIM_EVADE_WEAPON;
12432  aip->submode = -1;
12434  aip->mode_time = timestamp(MAX_EVADE_TIME); // Max time to evade.
12435  break;
12436  case AIM_EVADE_WEAPON: // Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12437  case AIM_PLAY_DEAD:
12438  case AIM_BAY_DEPART:
12439  case AIM_SENTRYGUN:
12440  break;
12441  case AIM_WARP_OUT:
12442  break;
12443  default:
12444  Int3(); // Hey, what mode is it?
12445  break;
12446  }
12447  }
12448  } else {
12449  aip->nearest_locked_object = -1;
12450  }
12451  }
12452 }
12453 
12454 // Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12455 // Have an 80% chance of evading in a second
12457 {
12458  // Only small ships evade an incoming missile. Why would a capital ship try to swerve?
12459  if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12460  return;
12461  }
12462 
12463  // don't evade missiles if you're docked
12464  if (object_is_docked(Pl_objp)) {
12465  return;
12466  }
12467 
12468  // Make sure in a mode in which we evade dumbfire weapons.
12469  switch (aip->mode) {
12470  case AIM_CHASE:
12471  if (aip->submode == SM_ATTACK_FOREVER) {
12472  return;
12473  }
12474  break;
12475  case AIM_GUARD:
12476  // If in guard mode and far away from guard object, don't pursue guy that hit me.
12477  if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12478  if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12479  return;
12480  }
12481  }
12482  case AIM_STILL:
12483  case AIM_STAY_NEAR:
12484  case AIM_EVADE:
12485  case AIM_GET_BEHIND:
12486  case AIM_AVOID:
12487  case AIM_PATH:
12488  case AIM_NONE:
12489  case AIM_WAYPOINTS:
12490  case AIM_SAFETY:
12491  break;
12492  case AIM_STRAFE:
12493  case AIM_BIGSHIP:
12494  case AIM_DOCK:
12495  case AIM_PLAY_DEAD:
12496  case AIM_EVADE_WEAPON:
12497  case AIM_BAY_EMERGE:
12498  case AIM_BAY_DEPART:
12499  case AIM_SENTRYGUN:
12500  case AIM_WARP_OUT:
12501  case AIM_FLY_TO_SHIP:
12502  return;
12503  default:
12504  Int3(); // Bogus mode!
12505  return;
12506  }
12507 
12508  if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12509  return; // Instructor doesn't evade.
12510 
12511  float t = ai_endangered_by_weapon(aip);
12512  if ((t > 0.0f) && (t < 1.0f)) {
12513  // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12514  if ( !(aip->ai_flags & AIF_NO_DYNAMIC) ) {
12516  return;
12517  }
12518  }
12519 
12520  switch (aip->mode) {
12521  case AIM_CHASE:
12522  switch (aip->submode) {
12523  case SM_EVADE:
12524  case SM_ATTACK_FOREVER:
12525  case SM_AVOID:
12526  case SM_GET_AWAY:
12527  case SM_EVADE_WEAPON:
12528  break;
12529  default:
12530  if (ai_near_full_strength(Pl_objp)) {
12531  aip->submode = SM_SUPER_ATTACK;
12534  } else {
12535  aip->submode = SM_EVADE_WEAPON;
12537  }
12538  break;
12539  }
12540  break;
12541  case AIM_GUARD:
12542  case AIM_STILL:
12543  case AIM_STAY_NEAR:
12544  case AIM_EVADE:
12545  case AIM_GET_BEHIND:
12546  case AIM_AVOID:
12547  case AIM_PATH:
12548  case AIM_NONE:
12549  case AIM_WAYPOINTS:
12550  case AIM_FLY_TO_SHIP:
12551  case AIM_SAFETY:
12552  if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12554  aip->previous_mode = aip->mode;
12555  aip->previous_submode = aip->submode;
12556  aip->mode = AIM_EVADE_WEAPON;
12557  aip->submode = -1;
12559  aip->mode_time = timestamp(MAX_EVADE_TIME); // Evade for up to five seconds.
12560  }
12561  break;
12562  case AIM_STRAFE:
12563  case AIM_BIGSHIP:
12564  case AIM_DOCK:
12565  case AIM_PLAY_DEAD:
12566  case AIM_EVADE_WEAPON:
12567  case AIM_BAY_EMERGE:
12568  case AIM_BAY_DEPART:
12569  case AIM_SENTRYGUN:
12570  break;
12571  default:
12572  Int3(); // Bogus mode!
12573  }
12574  }
12575 }
12576 
12577 // manage the opening and closing of fighter bay related subobject animations
12578 // input: pl_objp => the object which is/has entered/exited the fighter bay
12579 // aip => ai info for said object
12580 // done => true if the object has is finished with the path, or is dead
12581 void ai_manage_bay_doors(object *pl_objp, ai_info *aip, bool done)
12582 {
12583  Assert( pl_objp );
12584  Assert( pl_objp->instance >= 0 );
12585  Assert( aip );
12586 
12587  ship *shipp = &Ships[pl_objp->instance];
12588 
12589  if (done && !shipp->bay_doors_need_open)
12590  return;
12591 
12592  if (done)
12593  shipp->bay_doors_need_open = false;
12594 
12595  // if this happens then the parent was probably destroyed/departed, so just skip the rest
12596  if (shipp->bay_doors_parent_shipnum < 0)
12597  return;
12598 
12599  ship *parent_ship = &Ships[shipp->bay_doors_parent_shipnum];
12600 
12601  if (done)
12602  parent_ship->bay_doors_wanting_open--;
12603 
12604  // trigger an open if we need it
12605  if ( (parent_ship->bay_doors_status == MA_POS_NOT_SET) && (parent_ship->bay_doors_wanting_open > 0) ) {
12607  parent_ship->bay_doors_status = MA_POS_SET;
12609  } else {
12610  parent_ship->bay_doors_status = MA_POS_READY;
12611  }
12612  }
12613 
12614  // if we are already open, and no longer need to be, then close the doors
12615  if ( (parent_ship->bay_doors_status == MA_POS_READY) && (parent_ship->bay_doors_wanting_open <= 0) ) {
12617  parent_ship->bay_doors_status = MA_POS_NOT_SET;
12618  }
12619 
12620  if ( (parent_ship->bay_doors_status == MA_POS_SET) && timestamp_elapsed(parent_ship->bay_doors_anim_done_time) ) {
12621  parent_ship->bay_doors_status = MA_POS_READY;
12622  }
12623 }
12624 
12625 // determine what path to use when emerging from a fighter bay
12626 // input: pl_objp => pointer to object for ship that is arriving
12627 // pos => output parameter, it is the starting world pos for path choosen
12628 // fvec => output parameter, this is the forward vector that ship has when arriving
12629 //
12630 // exit: -1 => path could not be located
12631 // 0 => success
12632 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_mask, vec3d *pos, vec3d *fvec)
12633 {
12634  int path_index, bay_path;
12635  pnode *pnp;
12636  vec3d *next_point;
12637 
12638  ship *shipp = &Ships[pl_objp->instance];
12639  ai_info *aip = &Ai_info[shipp->ai_index];
12640 
12641  if ( parent_objnum == -1 ) {
12642  Int3();
12643  return -1;
12644  }
12645 
12646  object *parent_objp = &Objects[parent_objnum];
12647  ship *parent_shipp = &Ships[parent_objp->instance];
12648 
12649  polymodel *pm = model_get( Ship_info[parent_shipp->ship_info_index].model_num );
12650  ship_bay *bay = pm->ship_bay;
12651 
12652  if ( bay == NULL ) {
12653  WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay exists on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
12654  return -1;
12655  }
12656 
12657  if ( bay->num_paths <= 0 ) {
12658  WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay paths exist on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
12659  return -1;
12660  }
12661 
12662  // try to find a bay path that is not taken
12663  // Goober5000 - choose from among allowed paths
12664  if (allowed_path_mask != 0)
12665  {
12666  int i, num_allowed_paths = 0, allowed_bay_paths[MAX_SHIP_BAY_PATHS];
12667 
12668  memset(allowed_bay_paths, 0, sizeof(allowed_bay_paths));
12669 
12670  for (i = 0; i < bay->num_paths; i++)
12671  {
12672  if (allowed_path_mask & (1 << i))
12673  {
12674  allowed_bay_paths[num_allowed_paths] = i;
12675  num_allowed_paths++;
12676  }
12677  }
12678 
12679  // cycle through the allowed paths
12680  bay_path = allowed_bay_paths[Ai_last_arrive_path % num_allowed_paths];
12681  }
12682  // choose from among all paths
12683  else
12684  {
12685  bay_path = Ai_last_arrive_path % bay->num_paths;
12686  }
12687 
12689 
12690  path_index = bay->path_indexes[bay_path];
12691 
12692  if ( path_index == -1 )
12693  return -1;
12694 
12695  // notify parent that I'll want to leave
12696  parent_shipp->bay_doors_wanting_open++;
12697 
12698  // keep track of my own status as well
12699  shipp->bay_doors_need_open = true;
12700  shipp->bay_doors_launched_from = (ubyte)bay_path;
12701  shipp->bay_doors_parent_shipnum = parent_objp->instance;
12702 
12703  // create the path for pl_objp to follow
12704  create_model_exit_path(pl_objp, parent_objp, path_index, pm->paths[path_index].nverts);
12705 
12706  // now return to the caller what the starting world pos and starting fvec for the ship will be
12707  Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12708  pnp = &Path_points[aip->path_start];
12709  *pos = pnp->pos;
12710 
12711  // calc the forward vector using the starting two points of the path
12712  pnp = &Path_points[aip->path_start+1];
12713  next_point = &pnp->pos;
12714  vm_vec_normalized_dir(fvec, next_point, pos);
12715 
12716  // record the parent objnum, since we'll need it once we're done with following the path
12717  aip->goal_objnum = parent_objnum;
12718  aip->goal_signature = parent_objp->signature;
12719  aip->mode = AIM_BAY_EMERGE;
12721 
12722  //due to the door animations the ship has to remain still until the doors are open
12723  vec3d vel = ZERO_VECTOR;
12724  pl_objp->phys_info.vel = vel;
12725  pl_objp->phys_info.desired_vel = vel;
12726  pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12727  pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12728  pl_objp->phys_info.prev_ramp_vel.xyz.z = 0.0f;
12729  pl_objp->phys_info.forward_thrust = 0.0f; // How much the forward thruster is applied. 0-1.
12730 
12731  return 0;
12732 }
12733 
12738 {
12739  aip->path_start = -1;
12740  aip->path_cur = -1;
12741  aip->path_length = 0;
12742  aip->mode = AIM_NONE;
12743 }
12744 
12749 {
12750  ai_info *aip;
12751  ship *shipp;
12752  int parent_died=0;
12753 
12754  Assert( Pl_objp != NULL );
12755  Assert( Pl_objp->instance >= 0 );
12756 
12757  shipp = &Ships[Pl_objp->instance];
12758  aip = &Ai_info[shipp->ai_index];
12759 
12760  // if no path to follow, leave this mode
12761  if ( aip->path_start < 0 ) {
12762  ai_manage_bay_doors(Pl_objp, aip, true);
12763  aip->mode = AIM_NONE;
12764  return;
12765  }
12766 
12767  // ensure parent ship is still alive
12768  if (aip->goal_objnum < 0) {
12769  parent_died = 1;
12770  }
12771 
12772  if ( !parent_died ) {
12773  if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12774  parent_died = 1;
12775  }
12776  }
12777 
12778  if ( !parent_died ) {
12780  if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12781  parent_died = 1;
12782  }
12783  }
12784 
12785  if ( parent_died ) {
12787  return;
12788  }
12789 
12790  ai_manage_bay_doors(Pl_objp, aip, false);
12791 
12793  return;
12794 
12795  // follow the path to the final point
12796  ai_path();
12797 
12798  // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12799  if ( ( (Missiontime - aip->submode_start_time) > 10*F1_0 ) && (vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos) > 0.75f * Objects[aip->goal_objnum].radius)) {
12800  // erase path
12802  // deal with model animations
12803  ai_manage_bay_doors(Pl_objp, aip, true);
12804  }
12805 
12806  // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps
12807  if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12809  // deal with model animations
12810  ai_manage_bay_doors(Pl_objp, aip, true);
12811  }
12812 }
12813 
12814 // Select the closest depart path
12815 //
12816 // input: aip => ai info pointer to ship seeking to depart
12817 // pm => pointer to polymodel for the ship contining the ship bay/depart paths
12818 //
12819 // exit: >=0 => ship bay path index for depart path (ie index into sb->paths[])
12820 // -1 => no path could be found
12821 //
12822 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12823 // AI_BAY_DEPART. It tries to find the closest path that isn't already in use
12824 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm, int allowed_path_mask)
12825 {
12826  int i, j, best_path, best_free_path;
12827  float dist, min_dist, min_free_dist;
12828  vec3d *source;
12829  model_path *mp;
12830  ship_bay *bay;
12831 
12832  bay = pm->ship_bay;
12833 
12834  best_free_path = best_path = -1;
12835  min_free_dist = min_dist = 1e20f;
12836  Assert(aip->shipnum >= 0);
12837  source = &Objects[Ships[aip->shipnum].objnum].pos;
12838 
12839  for ( i = 0; i < bay->num_paths; i++ )
12840  {
12841  // Goober5000 - maybe skip this path
12842  if ((allowed_path_mask != 0) && !(allowed_path_mask & (1 << i)))
12843  continue;
12844 
12845  mp = &pm->paths[bay->path_indexes[i]];
12846  for ( j = 0; j < mp->nverts; j++ )
12847  {
12848  dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12849 
12850  if (dist < min_dist)
12851  {
12852  min_dist = dist;
12853  best_path = i;
12854  }
12855 
12856  // If this is a free path
12857  if (!(bay->depart_flags & (1 << i)))
12858  {
12859  if (dist < min_free_dist)
12860  {
12861  min_free_dist = dist;
12862  best_free_path = i;
12863  }
12864  }
12865  }
12866  }
12867 
12868  if (best_free_path >= 0)
12869  {
12870  return best_free_path;
12871  }
12872 
12873  return best_path;
12874 }
12875 
12876 // determine what path to use when trying to depart to a fighter bay
12877 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12878 //
12879 // input: pl_objp => pointer to object for ship that is departing
12880 //
12881 // exit: -1 => could not find depart path
12882 // 0 => found depart path
12883 int ai_acquire_depart_path(object *pl_objp, int parent_objnum, int allowed_path_mask)
12884 {
12885  int path_index = -1;
12886  int ship_bay_path = -1;
12887 
12888  ship *shipp = &Ships[pl_objp->instance];
12889  ai_info *aip = &Ai_info[shipp->ai_index];
12890 
12891  if ( parent_objnum < 0 )
12892  {
12893  Warning(LOCATION, "In ai_acquire_depart_path(), specified a negative object number for the parent ship! (Departing ship is %s.) Looking for another ship...", shipp->ship_name);
12894 
12895  // try to locate a capital ship on the same team:
12896  int shipnum = ship_get_ship_for_departure(shipp->team);
12897 
12898  if (shipnum >= 0)
12899  parent_objnum = Ships[shipnum].objnum;
12900  }
12901 
12902  aip->path_start = -1;
12903 
12904  if ( parent_objnum < 0 )
12905  return -1;
12906 
12907  object *parent_objp = &Objects[parent_objnum];
12908  polymodel *pm = model_get(Ship_info[Ships[parent_objp->instance].ship_info_index].model_num );
12909  ship_bay *bay = pm->ship_bay;
12910 
12911  if ( bay == NULL )
12912  return -1;
12913  if ( bay->num_paths <= 0 )
12914  return -1;
12915 
12916  // take the closest path we can find
12917  ship_bay_path = ai_find_closest_depart_path(aip, pm, allowed_path_mask);
12918 
12919  if ( ship_bay_path < 0 )
12920  return -1;
12921 
12922  path_index = bay->path_indexes[ship_bay_path];
12923  aip->submode_parm0 = ship_bay_path;
12924  bay->depart_flags |= (1<<ship_bay_path);
12925 
12926  if ( path_index == -1 ) {
12927  return -1;
12928  }
12929 
12930  // notify parent that I'll want to enter bay
12931  Ships[parent_objp->instance].bay_doors_wanting_open++;
12932 
12933  // keep track of my own status as well
12934  shipp->bay_doors_need_open = true;
12935  shipp->bay_doors_launched_from = (ubyte)ship_bay_path;
12936  shipp->bay_doors_parent_shipnum = parent_objp->instance;
12937 
12938  Assert(path_index < pm->n_paths);
12939  ai_find_path(pl_objp, parent_objnum, path_index, 0);
12940 
12941  // Set this flag, so we don't bother recreating the path... we won't need to update the path
12942  // that has just been created.
12943  aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12944 
12945  aip->goal_objnum = parent_objnum;
12946  aip->goal_signature = parent_objp->signature;
12947  aip->mode = AIM_BAY_DEPART;
12948 
12949  shipp->flags |= SF_DEPART_DOCKBAY;
12950  return 0;
12951 }
12952 
12957 {
12958  ai_info *aip;
12959  int anchor_shipnum;
12960 
12961  aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12962 
12963  // if no path to follow, leave this mode
12964  if ( aip->path_start < 0 ) {
12965  aip->mode = AIM_NONE;
12966  return;
12967  }
12968 
12969  // check if parent ship valid; if not, abort depart
12970  anchor_shipnum = ship_name_lookup(Parse_names[Ships[Pl_objp->instance].departure_anchor]);
12971  if (anchor_shipnum < 0 || !ship_useful_for_departure(anchor_shipnum, Ships[Pl_objp->instance].departure_path_mask))
12972  {
12973  mprintf(("Aborting bay departure!\n"));
12974  aip->mode = AIM_NONE;
12975 
12976  Ships[Pl_objp->instance].flags &= ~SF_DEPART_DOCKBAY;
12977  return;
12978  }
12979 
12980  ai_manage_bay_doors(Pl_objp, aip, false);
12981 
12982  if ( Ships[anchor_shipnum].bay_doors_status != MA_POS_READY )
12983  return;
12984 
12985  // follow the path to the final point
12986  ai_path();
12987 
12988  //if we are on the last segment of the path and the bay doors are not open, open them
12989  //if we are on any other segment of the path, close them
12990 
12991  // if the final point is reached, let default AI take over
12992  if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12993  ai_manage_bay_doors(Pl_objp, aip, true);
12994 
12995  // Volition bay code
12996  polymodel *pm;
12997  ship_bay *bay;
12998 
12999  pm = model_get(Ship_info[Ships[Objects[aip->goal_objnum].instance].ship_info_index].model_num);
13000  bay = pm->ship_bay;
13001  if ( bay != NULL ) {
13002  bay->depart_flags &= ~(1<<aip->submode_parm0);
13003  }
13004 
13005  // make ship disappear
13007 
13008  // clean up path stuff
13009  aip->path_start = -1;
13010  aip->path_cur = -1;
13011  aip->path_length = 0;
13012  aip->mode = AIM_NONE;
13013  }
13014 }
13015 
13020 {
13021  // Nothing to do here. Turret firing is handled via process_subobjects().
13022  // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
13023 }
13024 
13030 {
13031  switch (aip->mode) {
13032  case AIM_CHASE:
13033  if (En_objp) {
13034  ai_chase();
13035  } else if (aip->submode == SM_EVADE_WEAPON) {
13036  evade_weapon();
13037  // maybe reset submode
13038  if (aip->danger_weapon_objnum == -1) {
13039  aip->submode = SM_ATTACK;
13042  }
13043  } else {
13044  // Don't circle if this is the instructor.
13045  ship *shipp = &Ships[aip->shipnum];
13046  ship_info *sip = &Ship_info[shipp->ship_info_index];
13047 
13049  if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
13050  aip->mode = AIM_NONE;
13051  } else {
13052  ai_chase_circle(Pl_objp);
13053  }
13054  }
13055  }
13056  break;
13057  case AIM_EVADE:
13058  if (En_objp) {
13059  ai_evade();
13060  } else {
13061  vec3d tvec;
13062  vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, 100.0f);
13063  turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
13064  accelerate_ship(aip, 0.5f);
13065  }
13066  break;
13067  case AIM_STILL:
13068  ai_still();
13069  break;
13070  case AIM_STAY_NEAR:
13071  ai_stay_near();
13072  break;
13073  case AIM_GUARD:
13074  ai_guard();
13075  break;
13076  case AIM_WAYPOINTS:
13077  ai_waypoints();
13078  break;
13079  case AIM_FLY_TO_SHIP:
13080  ai_fly_to_ship();
13081  break;
13082  case AIM_DOCK:
13083  ai_dock();
13084  break;
13085  case AIM_NONE:
13086  break;
13087  case AIM_BIGSHIP:
13088  ai_big_ship(Pl_objp);
13089  break;
13090  case AIM_PATH: {
13091  int path_num;
13092  path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
13093  ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
13094  ai_path();
13095  break;
13096  }
13097  case AIM_SAFETY:
13098  ai_safety();
13099  break;
13100  case AIM_EVADE_WEAPON:
13101  evade_weapon();
13102  break;
13103  case AIM_STRAFE:
13104  if (En_objp) {
13105  Assert(En_objp->type == OBJ_SHIP);
13106  ai_big_strafe(); // strafe a big ship
13107  } else {
13108  aip->mode = AIM_NONE;
13109  }
13110  break;
13111  case AIM_BAY_EMERGE:
13112  ai_bay_emerge();
13113  break;
13114  case AIM_BAY_DEPART:
13115  ai_bay_depart();
13116  break;
13117  case AIM_SENTRYGUN:
13118  ai_sentrygun();
13119  break;
13120  case AIM_WARP_OUT:
13121  break; // Note, handled directly from ai_frame().
13122  case AIM_GET_BEHIND:
13123  // FIXME: got this from TBP and added it here to skip the Int3() but don't really want to handle it
13124  // properly until after 3.6.7 just to avoid delaying release or breaking something - taylor
13125  break;
13126  default:
13127  Int3(); // This should never happen -- MK, 5/12/97
13128  break;
13129  }
13130 
13131  if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
13133  }
13134 }
13135 
13136 // Auxiliary function for maybe_request_support.
13137 // Return 1 if subsystem "type" is worthy of repair, else return 0.
13138 // Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
13140 {
13141  float t;
13142 
13143  t = ship_get_subsystem_strength(shipp, type);
13144 
13145  if (t > 0.0f) {
13146  return (int) ((1.0f - t) * 3);
13147  } else {
13148  return 3;
13149  }
13150 }
13151 
13156 {
13157  ship_obj *so;
13158  int team;
13159  int count = 0;
13160 
13161  team = Ships[objp->instance].team;
13162 
13163  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13164  object *A;
13165 
13166  Assert (so->objnum != -1);
13167  A = &Objects[so->objnum];
13168 
13169  if (Ships[A->instance].team == team) {
13171  count++;
13172  }
13173  }
13174  }
13175 
13176  return count;
13177 }
13178 
13179 
13180 // Maybe ship *objp should request support (rearm/repair).
13181 // If it does, return TRUE, else return FALSE.
13183 {
13184  ship_info *sip;
13185  ship *shipp;
13186  ai_info *aip;
13187  weapon_info *wip;
13188  int desire;
13189  int i;
13190  float r;
13191 
13192  Assert(objp->type == OBJ_SHIP);
13193  shipp = &Ships[objp->instance];
13194  aip = &Ai_info[shipp->ai_index];
13195  sip = &Ship_info[shipp->ship_info_index];
13196 
13197  // Only fighters and bombers request support.
13198  if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
13199  return 0;
13200 
13201  // A ship that is currently awaiting does not need support!
13203  return 0;
13204 
13206  return 0;
13207 
13208  // just do a simple check first, and the more expensive check later
13209  if (!is_support_allowed(objp, true))
13210  return 0;
13211 
13212  // Goober5000 - a ship that is currently docked shouldn't request repair.
13213  // This is to prevent support ships being summoned for a ship that is getting
13214  // towed away by other means.
13215  if (object_is_docked(objp))
13216  return 0;
13217 
13218  // Compute a desire value.
13219  // Desire of 0 means no reason to request support.
13220  // 1 is slight, 2 more, etc. Maximum is around 20. Anything larger than 3 is pretty strong.
13221  desire = 0;
13222 
13223  // Set desire based on hull strength.
13224  // Note: We no longer repair hull, so this would cause repeated repair requests.
13225  // Added back in upon mission flag condition - Goober5000
13227  {
13228  desire += 6 - (int) (get_hull_pct(objp) * 6.0f);
13229  }
13230 
13231  // Set desire based on key subsystems.
13232  desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE); // Note, disabled engine forces repair request, regardless of nearby enemies.
13233  desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
13234  desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
13235  desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13236 
13237 
13238  // Set desire based on percentage of secondary weapons.
13239  ship_weapon *swp = &shipp->weapons;
13240 
13241  for ( i = 0; i < swp->num_secondary_banks; i++ ) {
13242  if (swp->secondary_bank_start_ammo[i] > 0) {
13243  r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13244  desire += (int) ((1.0f - r) * 3.0f);
13245  }
13246  }
13247 
13248  // Set desire based on ballistic weapons - Goober5000
13249  if (sip->flags & SIF_BALLISTIC_PRIMARIES)
13250  {
13251  for (i = 0; i < swp->num_primary_banks; i++)
13252  {
13253  wip = &Weapon_info[swp->primary_bank_weapons[i]];
13254 
13255  if (wip->wi_flags2 & WIF2_BALLISTIC)
13256  {
13257  r = (float) swp->primary_bank_ammo[i] / swp->primary_bank_start_ammo[i];
13258 
13259  // cube ammo level for better behavior, and adjust for number of banks
13260  desire += (int) ((1.0f - r)*(1.0f - r)*(1.0f - r) * (5.0f / swp->num_primary_banks));
13261  }
13262  }
13263  }
13264 
13265 
13266  // If no reason to repair, don't bother to see if it's safe to repair.
13267  if (desire == 0){
13268  return 0;
13269  }
13270 
13271  bool try_to_rearm = false;
13272 
13273  // Compute danger threshold.
13274  // Balance this with desire and maybe request support.
13275  if (ai_good_time_to_rearm( objp )) {
13276  try_to_rearm = true;
13277  } else if (num_allies_rearming(objp) < 2) {
13278  if (desire >= 8) { // guarantees disabled will cause repair request
13279  try_to_rearm = true;
13280  } else if (desire >= 3) { // >= 3 means having a single subsystem fully blown will cause repair.
13281  int count;
13282  int objnum = find_nearby_threat(OBJ_INDEX(objp), iff_get_attacker_mask(obj_team(objp)), 2000.0f, &count);
13283 
13284  if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13285  try_to_rearm = true;
13286  }
13287  }
13288  }
13289 
13290  if (try_to_rearm) {
13291  // Now do the more thorough check
13292  if (is_support_allowed(objp)) {
13293  ai_issue_rearm_request(objp);
13294  return 1;
13295  }
13296  }
13297 
13298  return 0;
13299 }
13300 
13301 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13302 {
13303  ai_abort_rearm_request(objp);
13304  if (aip->mode != AIM_WARP_OUT) {
13305  aip->mode = AIM_WARP_OUT;
13306  aip->submode = AIS_WARP_1;
13308  }
13309 }
13310 
13311 // Maybe make ship depart (Goober5000 - changed from always warp ship out)
13312 // Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13313 void ai_maybe_depart(object *objp)
13314 {
13315  ship *shipp;
13316  ship_info *sip;
13317 
13318  // don't do anything if in a training mission.
13320  return;
13321 
13322  Assert(objp->type == OBJ_SHIP);
13323 
13324  shipp = &Ships[objp->instance];
13325  ai_info *aip = &Ai_info[shipp->ai_index];
13326  sip = &Ship_info[shipp->ship_info_index];
13327 
13328  if (aip->mode == AIM_WARP_OUT || aip->mode == AIM_BAY_DEPART)
13329  return;
13330 
13331  // If a support ship with no goals and low hull, depart. Be sure that there are no pending goals
13332  // in the support ships ai_goal array. Just process this ships goals.
13333  if (sip->flags & SIF_SUPPORT) {
13334  if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13335  ai_process_mission_orders( OBJ_INDEX(objp), aip );
13336  if ( (aip->support_ship_objnum == -1) && (get_hull_pct(objp) < 0.25f) ) {
13337  if (!(shipp->flags & SF_DEPARTING)) {
13338  if (!mission_do_departure(objp)) {
13339  // if departure failed, try again at a later point
13340  // (timestamp taken from ai_do_objects_repairing_stuff)
13341  aip->warp_out_timestamp = timestamp((int) ((30 + 10*frand()) * 1000));
13342  }
13343  }
13344  }
13345  }
13346  }
13347 
13348  // some iffs don't warp out, they'll eventually request support.
13349  if (Iff_info[shipp->team].flags & IFFF_SUPPORT_ALLOWED)
13350  return;
13351 
13352  if (!(shipp->flags & SF_DEPARTING)) {
13353  if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13354  if (aip->warp_out_timestamp == 0) {
13355  //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13356  // aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13357  //}
13358  } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13359  mission_do_departure(objp);
13360  }
13361  }
13362  }
13363 }
13364 
13368 void ai_warp_out(object *objp)
13369 {
13370  ship *shipp = &Ships[objp->instance];
13371  ai_info *aip = &Ai_info[shipp->ai_index];
13372 
13373  // if dying, don't warp out.
13374  if (shipp->flags & SF_DYING)
13375  return;
13376 
13377  // Goober5000 - check for engine or navigation failure
13378  if (!ship_engine_ok_to_warp(shipp) || !ship_navigation_ok_to_warp(shipp))
13379  {
13380  // you shouldn't hit this... if you do, then I need to add a check for it
13381  // in whatever function initiates a warpout
13382  Assert (!(shipp->flags2 & SF2_NO_SUBSPACE_DRIVE));
13383 
13384  // flag us as trying to warp so that this function keeps getting called
13385  // (in other words, if we can't warp just yet, we want to warp at the first
13386  // opportunity)
13387  if (aip->mode != AIM_WARP_OUT)
13388  aip->mode = AIM_WARP_OUT;
13389  aip->submode = AIS_WARP_1;
13391 
13392  return;
13393  }
13394 
13395  // Goober5000 - make sure the flag is clear (if it was previously set)
13397 
13398  switch (aip->submode) {
13399  case AIS_WARP_1:
13400  aip->force_warp_time = timestamp(10*1000); // Try to avoid a collision for up to ten seconds.
13401  aip->submode = AIS_WARP_2;
13403  break;
13404  case AIS_WARP_2: // Make sure won't collide with any object.
13406  || (!collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)
13407  || (Ship_info[shipp->ship_info_index].warpout_type == WT_HYPERSPACE
13408  && !collide_predict_large_ship(objp, 100000.0f)))) {
13409  aip->submode = AIS_WARP_3;
13411 
13412  // maybe recalculate collision pairs.
13413  if (ship_get_warpout_speed(objp) > ship_get_max_speed(shipp)) {
13414  // recalculate collision pairs
13415  OBJ_RECALC_PAIRS(objp);
13416  }
13417 
13418  aip->force_warp_time = timestamp(4*1000); // Try to attain target speed for up to 4 seconds.
13419  } else {
13420  vec3d goal_point;
13421  vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.vec.uvec, 100.0f);
13422  turn_towards_point(objp, &goal_point, NULL, 0.0f);
13423  accelerate_ship(aip, 0.0f);
13424  }
13425  break;
13426  case AIS_WARP_3:
13427  // Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13428  // See shipfx#572 for sample code.
13429  float speed, goal_speed;
13430  goal_speed = ship_get_warpout_speed(objp);
13431 
13432  // HUGE ships go immediately to AIS_WARP_4
13433  if (Ship_info[shipp->ship_info_index].flags & SIF_HUGE_SHIP) {
13434  aip->submode = AIS_WARP_4;
13436  break;
13437  }
13438 
13439  speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13440  vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.vec.fvec, speed);
13441  objp->phys_info.desired_vel = objp->phys_info.vel;
13442 
13443  if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f)) {
13444  aip->submode = AIS_WARP_4;
13446  }
13447  break;
13448  case AIS_WARP_4: {
13449  // Only lets the ship warp after waiting for the warpout engage time
13450  if ( (Missiontime / 100) >= (aip->submode_start_time / 100 + Ship_info[shipp->ship_info_index].warpout_engage_time) ) {
13451  shipfx_warpout_start(objp);
13452  aip->submode = AIS_WARP_5;
13454  }
13455  break;
13456  }
13457  case AIS_WARP_5:
13458  break;
13459  default:
13460  Int3(); // Illegal submode for warping out.
13461  }
13462 }
13463 
13464 // Return object index of weapon that could produce a shockwave that should be known about to *objp.
13465 // Return nearest one.
13467 {
13468  missile_obj *mo;
13469  float nearest_dist = 999999.9f;
13470  int nearest_index = -1;
13471 
13472  for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13473  object *A;
13474  weapon *wp;
13475  weapon_info *wip;
13476 
13477  Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13478  A = &Objects[mo->objnum];
13479 
13480  Assert(A->type == OBJ_WEAPON);
13481  Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13482  wp = &Weapons[A->instance];
13483  wip = &Weapon_info[wp->weapon_info_index];
13484  Assert( wip->subtype == WP_MISSILE );
13485 
13486  if (wip->shockwave.speed > 0.0f) {
13487  float dist;
13488 
13489  dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13490  if (dist < nearest_dist) {
13491  nearest_dist = dist;
13492  nearest_index = mo->objnum;
13493  }
13494  }
13495  }
13496 
13497  return nearest_index;
13498 
13499 }
13500 
13501 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD 100.0f
13502 
13503 // Tell all ships to avoid a big ship that is blowing up.
13504 // Only avoid if shockwave is fairly large.
13505 // OK to tell everyone to avoid. If they're too far away, that gets cleaned up in the frame interval.
13506 void ai_announce_ship_dying(object *dying_objp)
13507 {
13508  float damage = ship_get_exp_damage(dying_objp);
13509  if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13510  ship_obj *so;
13511 
13512  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13513  object *A = &Objects[so->objnum];
13514  Assert(A->type == OBJ_SHIP);
13515 
13516  // Goober5000 - the disallow is now handled uniformly in the ai_avoid_shockwave function
13517  /*if (Ship_info[Ships[A->instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER | SIF_TRANSPORT))*/ {
13518  ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
13519 
13520  // AL 1-5-98: only avoid shockwave if not docked or repairing
13521  if ( !object_is_docked(A) && !(aip->ai_flags & (AIF_REPAIRING|AIF_BEING_REPAIRED)) ) {
13523  }
13524  }
13525  }
13526  }
13527 }
13528 
13529 
13530 // Return object index of weapon that could produce a shockwave that should be known about to *objp.
13531 // Return nearest one.
13533 {
13534  ship_obj *so;
13535  float nearest_dist = 999999.9f;
13536  int nearest_index = -1;
13537 
13538  for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13539  object *A;
13540  ship *shipp;
13541 
13542  Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13543  A = &Objects[so->objnum];
13544 
13545  Assert(A->type == OBJ_SHIP);
13546  Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13547  shipp = &Ships[A->instance];
13548  // Only look at objects in the process of dying.
13549  if (shipp->flags & SF_DYING) {
13550  float damage = ship_get_exp_damage(objp);
13551 
13552  if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) { // Only evade quite large blasts
13553  float dist;
13554 
13555  dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13556  if (dist < nearest_dist) {
13557  nearest_dist = dist;
13558  nearest_index = so->objnum;
13559  }
13560  }
13561  }
13562  }
13563 
13564  return nearest_index;
13565 
13566 }
13567 
13568 int aas_1(object *objp, ai_info *aip, vec3d *safe_pos)
13569 {
13570  // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13571  if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13572  // If we don't currently know of a weapon to avoid, try to find one.
13573  // If we can't find one, then clear the bit so we don't keep coming here.
13574  if (aip->shockwave_object == -1) {
13575  int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13576  if (shockwave_weapon == -1) {
13578  return 0;
13579  } else {
13580  aip->shockwave_object = shockwave_weapon;
13581  }
13582  }
13583 
13584  // OK, we have reason to believe we should avoid aip->shockwave_object.
13585  Assert(aip->shockwave_object > -1);
13586  object *weapon_objp = &Objects[aip->shockwave_object];
13587  if (weapon_objp->type != OBJ_WEAPON) {
13589  aip->shockwave_object = -1;
13590  return 0;
13591  }
13592 
13593  weapon *weaponp = &Weapons[weapon_objp->instance];
13594  weapon_info *wip = &Weapon_info[weaponp->weapon_info_index];
13595  object *target_ship_obj = NULL;
13596 
13597  if (wip->shockwave.speed == 0.0f) {
13599  aip->shockwave_object = -1;
13600  return 0;
13601  }
13602 
13603  float danger_dist;
13604  vec3d expected_pos; // Position at which we expect the weapon to detonate.
13605  int pos_set = 0;
13606 
13607  danger_dist = wip->shockwave.outer_rad;
13608  // Set predicted position of detonation.
13609  // If an aspect locked missile, assume it will detonate at the homing position.
13610  // If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13611  // time in the future, this time based on max lifetime and life left.
13612  if (wip->wi_flags & WIF_LOCKED_HOMING) {
13613  expected_pos = weaponp->homing_pos;
13614  if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13615  target_ship_obj = weaponp->homing_object;
13616  }
13617  pos_set = 1;
13618  if (IS_VEC_NULL(&weaponp->homing_pos)) {
13619  pos_set = 0;
13620  if (weaponp->target_num != -1) {
13621  if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13622  target_ship_obj = &Objects[weaponp->target_num];
13623  expected_pos = target_ship_obj->pos;
13624  pos_set = 1;
13625  }
13626  }
13627  }
13628  }
13629 
13630  if (!pos_set) {
13631  float time_scale;
13632 
13633  if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13634  time_scale = 1.0f;
13635  } else {
13636  time_scale = weaponp->lifeleft/2.0f;
13637  }
13638 
13639  vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.vec.fvec, time_scale);
13640  }
13641 
13642  // See if too far away to care about shockwave.
13643  if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13644  //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13645  return 0;
13646  } else {
13647  // try to find a safe position
13648  vec3d vec_from_exp;
13649  float dir = 1.0f;
13650  vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13651  float dot = vm_vec_dot(&vec_from_exp, &weapon_objp->orient.vec.fvec);
13652  if (dot > -30) {
13653  // if we're already on the other side of the explosion, don't try to fly behind it
13654  dir = -1.0f;
13655  }
13656 
13657  // Fly towards a point behind the weapon.
13658  vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.vec.fvec, -50000.0f*dir);
13659 
13660  return 1;
13661  }
13662  } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13663  if (aip->shockwave_object == -1) {
13664  int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13665  if (shockwave_ship == -1) {
13667  return 0;
13668  } else {
13669  aip->shockwave_object = shockwave_ship;
13670  }
13671  }
13672 
13673  Assert(aip->shockwave_object > -1);
13674  object *ship_objp = &Objects[aip->shockwave_object];
13675  if (ship_objp == objp) {
13676  aip->shockwave_object = -1;
13677  return 0;
13678  }
13679 
13680  if (ship_objp->type != OBJ_SHIP) {
13682  return 0;
13683  }
13684 
13685  // Optimize note! Don't really have to normalize. We only need a point away from the blowing-up ship.
13686  vec3d safe_vec;
13687 
13688  vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13689  vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f); // Fly away from the ship.
13690 
13691  float outer_rad = ship_get_exp_outer_rad(ship_objp);
13692 
13693  if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13695  return 0;
13696  }
13697 
13698  return 1;
13699 
13700  } else {
13701  Int3(); // Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon. What is it!?
13702  }
13703 
13704  return 0;
13705 }
13706 
13707 // --------------------------------------------------------------------------
13708 // Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13709 // If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13710 // Return 1 if avoiding a shockwave, else return 0.
13711 int ai_avoid_shockwave(object *objp, ai_info *aip)
13712 {
13713  vec3d safe_pos;
13714 
13715  // BIG|HUGE do not respond to shockwaves
13716  // Goober5000 - let's treat shockwave response the same way whether from weapon or ship
13717  if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_AVOID_SHOCKWAVE))) {
13718  // don't come here again
13719  aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13720  return 0;
13721  }
13722 
13723  // Don't all react right away.
13724  if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED)) {
13725  float evadeChance = (aip->ai_shockwave_evade_chance == FLT_MIN)
13726  ? ((float) aip->ai_class/4.0f + 0.25f)
13728  if (!rand_chance(flFrametime, evadeChance)) // Chance to avoid in 1 second is 0.25 + ai_class/4
13729  return 0;
13730  }
13731 
13732  if (!aas_1(objp, aip, &safe_pos)) {
13734  return 0;
13735  }
13736 
13738 
13739  // OK, evade the shockwave!
13740  turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13741  vec3d vec_to_safe_pos;
13742  float dot_to_goal;
13743 
13744  vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13745 
13746  dot_to_goal = vm_vec_dot(&objp->orient.vec.fvec, &vec_to_safe_pos);
13747  if (dot_to_goal < -0.5f)
13748  accelerate_ship(aip, 0.3f);
13749  else {
13750  accelerate_ship(aip, 1.0f + dot_to_goal);
13751  if (dot_to_goal > 0.2f) {
13752  if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13753  afterburners_start(objp);
13755  }
13756  }
13757  }
13758 
13759  return 1;
13760 }
13761 
13762 // Awaiting repair. Be useful.
13763 // Probably fly towards incoming repair ship.
13764 // Return true if this ship is close to being repaired, else return false.
13766 {
13768  return 0;
13769 
13770  if (aip->support_ship_objnum == -1)
13771  return 0;
13772 
13773  ship *shipp;
13774  ship_info *sip;
13775 
13776  shipp = &Ships[Objects[aip->support_ship_objnum].instance];
13777  sip = &Ship_info[shipp->ship_info_index];
13778 
13779  aip->ai_flags &= ~AIF_FORMATION_OBJECT; // Prevents endless rotation.
13780 
13781  if (!(sip->flags & SIF_SUPPORT))
13782  return 0;
13783 
13784  vec3d goal_point;
13785  object *repair_objp;
13786 
13787  repair_objp = &Objects[aip->support_ship_objnum];
13788 
13789  if (Ships[repair_objp->instance].team == Iff_traitor) {
13790  ai_abort_rearm_request(repair_objp);
13791  return 0;
13792  }
13793 
13794  vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.vec.uvec, -50.0f); // Fly towards point below repair ship.
13795 
13796  vec3d vtr;
13797  float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13798  float dot = vm_vec_dot(&vtr, &objp->orient.vec.fvec);
13799 
13800  if (dist > 200.0f) {
13801  accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13802  turn_towards_point(objp, &goal_point, NULL, 0.0f);
13803  } else {
13804  accelerate_ship(aip, 0.0f);
13805  }
13806 
13807  return 1;
13808 }
13809 
13810 // Maybe cause this ship to self-destruct.
13811 // Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13812 // Maybe should only do this if they are preventing their wing from re-entering.
13814 {
13815  Assertion(objp->type == OBJ_SHIP, "ai_maybe_self_destruct() can only be called with objects that are ships!");
13816  ship *shipp = &Ships[objp->instance];
13817 
13818  // Some IFFs can be repaired, so no self-destruct.
13819  // In multiplayer, just don't self-destruct. I figured there would be a problem. -- MK, 3/19/98.
13821  return;
13822 
13823  // Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13824  // Reason: Don't want them to prevent a re-emergence of the wing.
13825  // Note: Don't blow up if not in a wing for two reasons: One, won't affect re-emergence of waves and (1) disable the Dragon
13826  // mission would be broken.
13827  // Also, don't blow up the ship if it has a ship flag preventing this - Goober5000
13828  if ((Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP) && (shipp->wingnum >= 0) && !(shipp->flags2 & SF2_NO_DISABLED_SELF_DESTRUCT)) {
13829  if ((ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) <= 0.0f) ||
13831  if (aip->self_destruct_timestamp < 0)
13832  aip->self_destruct_timestamp = timestamp(90 * 1000); // seconds until self-destruct
13833  } else {
13834  aip->self_destruct_timestamp = -1;
13835  }
13836 
13837  if (aip->self_destruct_timestamp < 0) {
13838  return;
13839  }
13840 
13842  ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13843  }
13844  }
13845 }
13846 
13850 int ai_need_new_target(object *pl_objp, int target_objnum)
13851 {
13852  object *objp;
13853 
13854  if ( target_objnum < 0 ) {
13855  return 1;
13856  }
13857 
13858  objp = &Objects[target_objnum];
13859 
13860  if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13861  return 1;
13862  }
13863 
13864  if ( objp->type == OBJ_SHIP ) {
13865  if ( Ships[objp->instance].flags & SF_DYING ) {
13866  return 1;
13867  } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team) {
13868  // Goober5000 - targeting the same team is allowed if pl_objp is going bonkers
13869  ai_info *pl_aip = &Ai_info[Ships[pl_objp->instance].ai_index];
13870  if (pl_aip->active_goal != AI_GOAL_NONE && pl_aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) {
13871  if (pl_aip->goals[pl_aip->active_goal].flags & AIGF_TARGET_OWN_TEAM) {
13872  return 0;
13873  }
13874  }
13875 
13876  return 1;
13877  }
13878  }
13879 
13880  return 0;
13881 }
13882 
13888 {
13889  float dot, dist;
13890  vec3d v2g;
13891 
13893  ai_turn_towards_vector(&aip->big_recover_pos_1, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, NULL);
13894  dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13895  dot = vm_vec_dot(&objp->orient.vec.fvec, &v2g);
13896  accelerate_ship(aip, dot);
13897 
13898  // If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13899  if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13902  }
13903 
13904  return 1;
13905 
13906  } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13907  ai_turn_towards_vector(&aip->big_recover_pos_2, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, NULL);
13908  dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13909  dot = vm_vec_dot(&objp->orient.vec.fvec, &v2g);
13910  accelerate_ship(aip, dot);
13911 
13912  // If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13913  if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13915  aip->ai_flags &= ~AIF_TARGET_COLLISION;
13916  }
13917 
13918  return 1;
13919  }
13920 
13921  if (aip->ai_flags & AIF_TARGET_COLLISION) {
13922  aip->ai_flags &= ~AIF_TARGET_COLLISION;
13923  }
13924  return 0;
13925 }
13926 
13928 {
13929  switch (aip->mode) {
13930  case AIM_CHASE:
13931  // check valid submode
13932  switch (aip->submode) {
13933  case SM_CONTINUOUS_TURN:
13934  case SM_ATTACK:
13935  case SM_EVADE_SQUIGGLE:
13936  case SM_EVADE_BRAKE:
13937  case SM_EVADE:
13938  case SM_SUPER_ATTACK:
13939  case SM_AVOID:
13940  case SM_GET_BEHIND:
13941  case SM_GET_AWAY:
13942  case SM_EVADE_WEAPON:
13943  case SM_FLY_AWAY:
13944  case SM_ATTACK_FOREVER:
13945  break;
13946  default:
13947  Int3();
13948  }
13949  break;
13950 
13951  case AIM_STRAFE:
13952  // check valid submode
13953  switch(aip->submode) {
13954  case AIS_STRAFE_ATTACK:
13955  case AIS_STRAFE_AVOID:
13956  case AIS_STRAFE_RETREAT1:
13957  case AIS_STRAFE_RETREAT2:
13958  case AIS_STRAFE_POSITION:
13959  break;
13960  default:
13961  Int3();
13962  }
13963  break;
13964  }
13965 }
13966 
13970 void ai_frame(int objnum)
13971 {
13972  ship *shipp = &Ships[Objects[objnum].instance];
13973  ai_info *aip = &Ai_info[shipp->ai_index];
13974  int target_objnum;
13975 
13977 
13978  // Set globals defining the current object and its enemy object.
13979  Pl_objp = &Objects[objnum];
13980 
13981  //Default to glide OFF
13982  Pl_objp->phys_info.flags &= ~PF_GLIDING;
13983 
13984  // warping out?
13985  if ((aip->mode == AIM_WARP_OUT) || (aip->ai_flags & AIF_TRYING_UNSUCCESSFULLY_TO_WARP))
13986  {
13987  ai_warp_out(Pl_objp);
13988 
13989  // Goober5000 - either we were never trying unsuccessfully, or we were but now
13990  // we're successful... in either case, since we're actually warping we simply return
13992  return;
13993  }
13994 
13995  ai_maybe_self_destruct(Pl_objp, aip);
13996  ai_process_mission_orders( objnum, aip );
13997 
13998  // Avoid a shockwave, if necessary. If a shockwave and rearming, stop rearming.
13999  if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
14000  if (ai_avoid_shockwave(Pl_objp, aip)) {
14003  ai_abort_rearm_request(Pl_objp);
14004  return;
14005  }
14006  } else {
14008  }
14009 
14010  // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
14011  // if the ship is getting repaired
14012  // If waiting to be repaired, just stop and sit.
14013  ai_do_repair_frame(Pl_objp, aip, flFrametime);
14014  if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
14015  if (ai_await_repair_frame(Pl_objp, aip))
14016  return;
14017  }
14018 
14019  if (aip->mode == AIM_PLAY_DEAD)
14020  return;
14021 
14022  // If recovering from a collision with a big ship, don't continue.
14023  if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
14024  return;
14025 
14026  ai_preprocess_ignore_objnum(Pl_objp, aip);
14027  target_objnum = set_target_objnum(aip, aip->target_objnum);
14028 
14029  Assert(objnum != target_objnum);
14030 
14031  ai_manage_shield(Pl_objp, aip);
14032 
14033  if ( maybe_request_support(Pl_objp) ) {
14034  if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
14036  }
14037  }
14038  else {
14040  }
14041 
14042  ai_maybe_depart(Pl_objp);
14043 
14044  // Find an enemy if don't already have one.
14045  En_objp = NULL;
14046  if ( ai_need_new_target(Pl_objp, target_objnum) ) {
14047  if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
14048  aip->resume_goal_time = -1;
14049  aip->active_goal = AI_GOAL_NONE;
14050  } else if (aip->resume_goal_time == -1) {
14051  // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
14052  if ( Ship_info[shipp->ship_info_index].class_type > -1 && (Ship_types[Ship_info[shipp->ship_info_index].class_type].ai_bools & STI_AI_AUTO_ATTACKS) ) {
14053  target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 25K units away.
14054  if (target_objnum != -1) {
14055  if (aip->target_objnum != target_objnum)
14056  aip->aspect_locked_time = 0.0f;
14057  target_objnum = set_target_objnum(aip, target_objnum);
14058 
14059  if (target_objnum >= 0)
14060  {
14061  En_objp = &Objects[target_objnum];
14062  }
14063  }
14064  }
14065  }
14066  } else if (target_objnum >= 0) {
14067  En_objp = &Objects[target_objnum];
14068  }
14069 
14070  // set base stealth info each frame
14071  aip->ai_flags &= ~AIF_STEALTH_PURSUIT;
14072  if (En_objp && En_objp->type == OBJ_SHIP) {
14073  if (Ships[En_objp->instance].flags2 & SF2_STEALTH) {
14074  int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
14075  float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
14076 
14077  if (stealth_state != STEALTH_FULLY_TARGETABLE) {
14078  aip->ai_flags |= AIF_STEALTH_PURSUIT;
14079  }
14080 
14081  if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_IN_FRUSTUM) ) {
14084  aip->stealth_last_pos = En_objp->pos;
14085  aip->stealth_velocity = En_objp->phys_info.vel;
14086  } else if (dist < 100) {
14087  // get cheat timestamp
14089 
14090  // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
14092  }
14093  }
14094  }
14095 
14096  // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
14097  if ( Ship_info[shipp->ship_info_index].class_type > -1 && !(Ship_types[Ship_info[shipp->ship_info_index].class_type].ai_bools & STI_AI_AUTO_ATTACKS)) {
14098  aip->target_objnum = -1;
14099  }
14100 
14101  if ((En_objp != NULL) && (En_objp->pos.xyz.x == Pl_objp->pos.xyz.x) && (En_objp->pos.xyz.y == Pl_objp->pos.xyz.y) && (En_objp->pos.xyz.z == Pl_objp->pos.xyz.z)) {
14102  mprintf(("Warning: Object and its enemy have same position. Object #" PTRDIFF_T_ARG "\n", OBJ_INDEX(Pl_objp)));
14103  En_objp = NULL;
14104  }
14105 
14106  if (aip->mode == AIM_CHASE) {
14107  if (En_objp == NULL) {
14108  aip->active_goal = -1;
14109  }
14110  }
14111 
14112  // If there is a goal to resume and enough time has elapsed, resume the goal.
14113  if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
14114  aip->active_goal = AI_GOAL_NONE;
14115  aip->resume_goal_time = -1;
14116  target_objnum = find_enemy(objnum, 2000.0f, The_mission.ai_profile->max_attackers[Game_skill_level]);
14117  if (target_objnum != -1) {
14118  if (aip->target_objnum != target_objnum) {
14119  aip->aspect_locked_time = 0.0f;
14120  }
14121  set_target_objnum(aip, target_objnum);
14122  }
14123  }
14124 
14125  // check if targeted subsystem has been destroyed, if so, move onto another subsystem
14126  // if trying to disable or disarm the target
14127  if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
14128  Assert(En_objp->type == OBJ_SHIP);
14129  if ( aip->targeted_subsys->current_hits <= 0.0f ) {
14130  int subsys_type;
14131 
14132  if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
14133  subsys_type = SUBSYSTEM_ENGINE;
14134  } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
14135  subsys_type = SUBSYSTEM_TURRET;
14136  } else {
14137  subsys_type = -1;
14138  }
14139 
14140  if ( subsys_type != -1 ) {
14141  ship_subsys *new_subsys;
14142  new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
14143  if ( new_subsys != NULL ) {
14144  set_targeted_subsys(aip, new_subsys, aip->target_objnum);
14145  } else {
14146  // AL 12-16-97: no more subsystems to attack... reset targeting info
14147  aip->target_objnum = -1;
14148  set_targeted_subsys(aip, NULL, -1);
14149  }
14150  } else {
14151  // targeted subsys is destroyed, so stop attacking it
14152  set_targeted_subsys(aip, NULL, -1);
14153  }
14154  }
14155  }
14156 
14157  ai_maybe_launch_cmeasure(Pl_objp, aip);
14158  ai_maybe_evade_locked_missile(Pl_objp, aip);
14159 
14160  aip->target_time += flFrametime;
14161 
14162  int in_formation = 0;
14163  if (aip->ai_flags & AIF_FORMATION) {
14164  in_formation = !ai_formation();
14165  }
14166 
14167  if ( !in_formation ) {
14168  ai_execute_behavior(aip);
14169  }
14170 
14171  process_subobjects(objnum);
14172  maybe_resume_previous_mode(Pl_objp, aip);
14173 
14174  if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
14175  if (Missiontime > aip->afterburner_stop_time) {
14176  afterburners_stop(Pl_objp);
14177  }
14178  }
14179 }
14180 
14181 void ai_control_info_check( object *obj, ai_info *aip )
14182 {
14183  if(aip->ai_override_flags == 0)
14184  return;
14185 
14187  aip->ai_override_flags = 0;
14188  } else {
14189  if(aip->ai_override_flags & AIORF_FULL)
14190  {
14191  AI_ci.pitch = aip->ai_override_ci.pitch;
14192  AI_ci.heading = aip->ai_override_ci.heading;
14193  AI_ci.bank = aip->ai_override_ci.bank;
14194  } else {
14195  if(aip->ai_override_flags & AIORF_PITCH)
14196  {
14197  AI_ci.pitch = aip->ai_override_ci.pitch;
14198  }
14199  if(aip->ai_override_flags & AIORF_HEADING)
14200  {
14201  AI_ci.heading = aip->ai_override_ci.heading;
14202  }
14203  if(aip->ai_override_flags & AIORF_ROLL)
14204  {
14205  AI_ci.bank = aip->ai_override_ci.bank;
14206  }
14207  }
14209  {
14210  AI_ci.vertical = aip->ai_override_ci.vertical;
14211  AI_ci.sideways = aip->ai_override_ci.sideways;
14212  AI_ci.forward = aip->ai_override_ci.forward;
14213  } else {
14214  if(aip->ai_override_flags & AIORF_UP)
14215  {
14216  AI_ci.vertical = aip->ai_override_ci.vertical;
14217  }
14219  {
14220  AI_ci.sideways = aip->ai_override_ci.sideways;
14221  }
14222  if(aip->ai_override_flags & AIORF_FORWARD)
14223  {
14224  AI_ci.forward = aip->ai_override_ci.forward;
14225  }
14226  }
14227  }
14228 }
14229 
14230 int Last_ai_obj = -1;
14231 
14232 void ai_process( object * obj, int ai_index, float frametime )
14233 {
14234  if (obj->flags & OF_SHOULD_BE_DEAD)
14235  return;
14236 
14237  // return if ship is dead, unless it's a big ship...then its turrets still fire, like I was quoted in a magazine. -- MK, 5/15/98.
14238  if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
14239  return;
14240  }
14241 
14242  int rfc = 1; // Assume will be Reading Flying Controls.
14243 
14244  Assert( obj->type == OBJ_SHIP );
14245  Assert( ai_index >= 0 );
14246 
14247  AI_frametime = frametime;
14248  if (OBJ_INDEX(obj) <= Last_ai_obj) {
14249  AI_FrameCount++;
14250  }
14251 
14252  memset( &AI_ci, 0, sizeof(AI_ci) );
14253 
14254  ai_frame(OBJ_INDEX(obj));
14255 
14256  AI_ci.pitch = 0.0f;
14257  AI_ci.bank = 0.0f;
14258  AI_ci.heading = 0.0f;
14259 
14260  // the ships maximum velocity now depends on the energy flowing to engines
14262  ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14263 
14264  // In certain circumstances, the AI says don't fly in the normal way.
14265  // One circumstance is in docking and undocking, when the ship is moving
14266  // under thruster control.
14267  switch (aip->mode) {
14268  case AIM_DOCK:
14269  if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14270  rfc = 0;
14271  break;
14272  case AIM_WARP_OUT:
14273  if (aip->submode >= AIS_WARP_3)
14274  rfc = 0;
14275  break;
14276  default:
14277  break;
14278  }
14279 
14280  if (rfc == 1) {
14281  // Wanderer - sexp based override goes here - only if rfc is valid though
14282  ai_control_info_check(obj, aip);
14283  vec3d copy_desired_rotvel = obj->phys_info.rotvel;
14284  physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14285  // if obj is in formation and not flight leader, don't update rotvel
14286  if (aip->ai_flags & AIF_FORMATION) {
14287  if (&Objects[aip->goal_objnum] != obj) {
14288  obj->phys_info.desired_rotvel = copy_desired_rotvel;
14289  obj->phys_info.rotvel = copy_desired_rotvel;
14290  }
14291  }
14292  }
14293 
14294  Last_ai_obj = OBJ_INDEX(obj);
14295 }
14296 
14300 void init_ai_object(int objnum)
14301 {
14302  int i, ship_index, ai_index, ship_type;
14303  ai_info *aip;
14304  object *objp;
14305  vec3d near_vec; // A vector nearby and mainly in front of this object.
14306 
14307  objp = &Objects[objnum];
14308  ship_index = objp->instance;
14309  ai_index = Ships[ship_index].ai_index;
14310  Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14311 
14312  aip = &Ai_info[ai_index];
14313 
14314  ship_type = Ships[ship_index].ship_info_index;
14315 
14316  vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.vec.fvec, 100.0f);
14317  vm_vec_scale_add2(&near_vec, &objp->orient.vec.rvec, 10.0f);
14318 
14319  // Things that shouldn't have to get initialized, but initialize them just in case!
14320  aip->ai_flags = 0;
14321  aip->previous_mode = AIM_NONE;
14322  aip->mode_time = -1;
14323  aip->target_objnum = -1;
14324  aip->target_signature = -1;
14325  aip->previous_target_objnum = -1;
14326  aip->target_time = 0.0f;
14327  aip->enemy_wing = -1;
14328  aip->attacker_objnum = -1;
14329  aip->goal_objnum = -1;
14330  aip->goal_signature = -1;
14331  aip->guard_objnum = -1;
14332  aip->guard_signature = -1;
14333  aip->guard_wingnum = -1;
14334  aip->submode = 0;
14335  aip->previous_submode = 0;
14336  aip->best_dot_to_enemy = -1.0f;
14337  aip->best_dot_from_enemy = -1.0f;
14338  aip->best_dot_to_time = 0;
14339  aip->best_dot_from_time = 0;
14340  aip->submode_start_time = 0;
14341  aip->submode_parm0 = 0;
14342  aip->submode_parm1 = 0;
14343  aip->active_goal = -1;
14344  aip->goal_check_time = timestamp(0);
14345  aip->last_predicted_enemy_pos = near_vec;
14346  aip->prev_goal_point = near_vec;
14347  aip->goal_point = near_vec;
14348  aip->time_enemy_in_range = 0.0f;
14349  aip->time_enemy_near = 0.0f;
14350  aip->last_attack_time = 0;
14351  aip->last_hit_time = 0;
14352  aip->last_hit_quadrant = 0;
14353  aip->hitter_objnum = -1;
14354  aip->hitter_signature = -1;
14355  aip->resume_goal_time = -1;
14356  aip->prev_accel = 0.0f;
14357  aip->prev_dot_to_goal = 0.0f;
14358 
14360  aip->ignore_signature = -1;
14361 
14362  // Goober5000
14363  for (i = 0; i < MAX_IGNORE_NEW_OBJECTS; i++)
14364  {
14366  aip->ignore_new_signatures[i] = -1;
14367  }
14368 
14369  //Init stuff from AI class and AI profiles
14371 
14372  aip->wp_list = NULL;
14374 
14375  aip->attacker_objnum = -1;
14376  aip->goal_signature = -1;
14377 
14378  Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.vec.fvec;
14379 
14380  aip->last_predicted_enemy_pos.xyz.x = 0.0f; // Says this value needs to be recomputed!
14381  aip->time_enemy_in_range = 0.0f;
14382  aip->time_enemy_near = 0.0f;
14383 
14384  aip->resume_goal_time = -1; // Say there is no goal to resume.
14385 
14386  aip->active_goal = -1;
14387  aip->path_start = -1;
14388  aip->path_goal_dist = -1;
14389  aip->path_length = 0;
14390  aip->path_subsystem_next_check = 1;
14391 
14392  aip->support_ship_objnum = -1;
14393  aip->support_ship_signature = -1;
14394 
14395  aip->danger_weapon_objnum = -1;
14396  aip->danger_weapon_signature = -1;
14397 
14398  aip->lead_scale = 0.0f;
14400  aip->last_hit_time = Missiontime;
14401 
14402  aip->nearest_locked_object = -1;
14403  aip->nearest_locked_distance = 99999.0f;
14404 
14405  aip->targeted_subsys = NULL;
14406  aip->last_subsys_target = NULL;
14407  aip->targeted_subsys_parent = -1;
14408 
14409  // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14410  aip->rearm_first_missile = TRUE; // flag to indicate that next missile to load is the first missile
14411  aip->rearm_first_ballistic_primary = TRUE; // flag to indicate that next ballistic to load is the first ballistic
14412  aip->rearm_release_delay = 0; // timestamp to delay the separation of docked ships after rearm
14413 
14414  aip->next_predict_pos_time = 0;
14415  aip->next_aim_pos_time = 0;
14416 
14417  aip->afterburner_stop_time = 0;
14418  aip->last_objsig_hit = -1; // object signature of the ship most recently hit by aip
14419 
14420  aip->path_next_create_time = timestamp(1);
14421  aip->path_create_pos = Objects[objnum].pos;
14422  aip->path_create_orient = Objects[objnum].orient;
14424 
14426  aip->warp_out_timestamp = 0;
14431 
14433 
14434  aip->shockwave_object = -1;
14436  aip->self_destruct_timestamp = -1; // This is a flag that we have not yet set this.
14440 
14441  aip->avoid_check_timestamp = timestamp(1);
14442 
14443  aip->abort_rearm_timestamp = -1;
14444 
14445  // artillery stuff
14446  aip->artillery_objnum = -1;
14447  aip->artillery_sig = -1;
14448 
14449  // waypoint speed cap
14450  aip->waypoint_speed_cap = -1;
14451 
14452  // set lethality to enemy team
14453  aip->lethality = 0.0f;
14454  aip->ai_override_flags = 0;
14455  memset(&aip->ai_override_ci,0,sizeof(control_info));
14456 }
14457 
14459 {
14460  Ppfp = Path_points;
14461 }
14462 
14463 int combine_flags(int base_flags, int override_flags, int override_set)
14464 {
14465  int result = 0;
14466  //Scan through every bit in the flag int
14467  for (int i = 0; i < 31; i++)
14468  {
14469  int flag = (1 << i);
14470  //If this flag is marked in the override as set, copy it from the override
14471  if (override_set & flag)
14472  {
14473  if (override_flags & flag)
14474  result |= flag;
14475  }
14476  else //Otherwise, copy it from the base flag
14477  {
14478  if (base_flags & flag)
14479  result |= flag;
14480  }
14481  }
14482  return result;
14483 }
14484 
14485 //Sets the ai_info stuff based on what is in the ai class and the current ai profile
14486 //Stuff in the ai class will override what is in the ai profile, but only if it is set.
14487 //Unset per-difficulty-level values are marked with FLT_MIN or INT_MIN
14488 //Which flags are set is handled by using two flag ints: one with the flag values (TRUE/FALSE), one that
14489 //just says which flags are set.
14491 {
14492  // since we use it so much in this function, sanity check the value for Game_skill_level
14493  if (Game_skill_level < 0 || Game_skill_level >= NUM_SKILL_LEVELS) {
14494  Warning(LOCATION, "Invalid skill level %i! Valid range 0 to %i. Resetting to default.", Game_skill_level, NUM_SKILL_LEVELS);
14496  }
14497 
14498  //ai_class-only stuff
14499  aip->ai_courage = aicp->ai_courage[Game_skill_level];
14501  aip->ai_evasion = aicp->ai_evasion[Game_skill_level];
14503 
14509 
14510  //Apply overrides from ai class to ai profiles values
14511  //Only override values which were explicitly set in the AI class
14514  aip->ai_in_range_time = (aicp->ai_in_range_time[Game_skill_level] == FLT_MIN) ?
14528  aip->ai_shield_manage_delay = (aicp->ai_shield_manage_delay[Game_skill_level] == FLT_MIN) ?
14538  aip->ai_turn_time_scale = (aicp->ai_turn_time_scale[Game_skill_level] == FLT_MIN) ?
14558 
14559  //Combine AI profile and AI class flags
14562 }
14563 
14565 {
14566  Assert(obj != NULL);
14567  Assert(obj->instance != -1);
14568  Assert(Ships[obj->instance].ai_index != -1);
14569 
14570  ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14571  int si_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14572 
14573  // default behavior in most cases (especially if we're docked) is to just stay put
14574  aip->mode = AIM_NONE;
14576  aip->active_goal = AI_GOAL_NONE;
14577 
14578  // if we're not docked, we may modify the behavior a bit
14579  if (!object_is_docked(obj))
14580  {
14581  // fighters automatically chase things
14582  if (!is_instructor(obj) && (si_flags & (SIF_FIGHTER | SIF_BOMBER)))
14583  {
14584  int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, The_mission.ai_profile->max_attackers[Game_skill_level]);
14585  set_target_objnum(aip, enemy_objnum);
14586  aip->mode = AIM_CHASE;
14587  aip->submode = SM_ATTACK;
14588  }
14589  // support ships automatically keep a safe distance
14590  else if (si_flags & SIF_SUPPORT)
14591  {
14592  aip->mode = AIM_SAFETY;
14593  aip->submode = AISS_1;
14594  aip->ai_flags &= ~(AIF_REPAIRING);
14595  }
14596  // sentry guns... do their thing
14597  else if (si_flags & SIF_SENTRYGUN)
14598  {
14599  aip->mode = AIM_SENTRYGUN;
14600  }
14601  }
14602 }
14603 
14604 #define FRIENDLY_DAMAGE_THRESHOLD 50.0f // Display a message at this threshold. Note, this gets scaled by Skill_level
14605 
14606 // send the given message from objp. called from the maybe_process_friendly_hit
14607 // code below when a message must get send to the player when he fires on friendlies
14609 {
14610  int index;
14611 
14612  // no traitor in multiplayer
14613  if(Game_mode & GM_MULTIPLAYER){
14614  return;
14615  }
14616 
14617  // don't send this message if a player ship was hit.
14618  if ( objp->flags & OF_PLAYER_SHIP ){
14619  return;
14620  }
14621 
14622  // check if objp is a fighter/bomber -- if not, then find a new ship to send the message
14623  index = objp->instance;
14624  if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14625  index = -1;
14626  }
14627 
14628  // If the ship can't send messages pick someone else
14630  index = -1;
14631  }
14632 
14633  // Karajorma - pick a random ship to send Command messages if command is silenced.
14634  if (index < 0 && (The_mission.flags & MISSION_FLAG_NO_BUILTIN_COMMAND) ) {
14636  }
14637 
14638  if ( index >= 0 )
14639  {
14641  } else {
14643  }
14644 }
14645 
14646 extern void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14647 
14651 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14652 {
14653  // no turning traitor in multiplayer
14654  if ( Game_mode & GM_MULTIPLAYER ) {
14655  return;
14656  }
14657 
14658  // ditto if mission says no traitors allowed
14660  return;
14661  }
14662 
14663  if ((obj_team(objp_hitter) == obj_team(objp_hit)) && (objp_hitter == Player_obj)) {
14664 
14665  // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point. If so, bail out.
14666  if ( objp_hitter->type != OBJ_SHIP ) {
14667  return;
14668  }
14669 
14670  Assert(objp_hitter->type == OBJ_SHIP);
14671  Assert(objp_hit->type == OBJ_SHIP);
14672  Assert((objp_weapon->type == OBJ_WEAPON) || (objp_weapon->type == OBJ_BEAM)); //beam added for traitor detection - FUBAR
14673 
14674  ship *shipp_hitter = &Ships[objp_hitter->instance];
14675  ship *shipp_hit = &Ships[objp_hit->instance];
14676 
14677  if (shipp_hitter->team != shipp_hit->team) {
14678  return;
14679  }
14680 
14681  // get the player
14682  player *pp = &Players[Player_num];
14683 
14684  // wacky stuff here
14685  if (pp->friendly_hits != 0) {
14686  float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14687  if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14688  if (time_since_last_hit > 60.0f) {
14689  pp->friendly_hits = 0;
14690  pp->friendly_damage = 0.0f;
14691  } else if (time_since_last_hit > 2.0f) {
14692  pp->friendly_hits -= (int) time_since_last_hit/2;
14693  pp->friendly_damage -= time_since_last_hit;
14694  }
14695 
14696  if (pp->friendly_damage < 0.0f) {
14697  pp->friendly_damage = 0.0f;
14698  }
14699 
14700  if (pp->friendly_hits < 0) {
14701  pp->friendly_hits = 0;
14702  }
14703  }
14704  }
14705 
14706  float damage; // Damage done by weapon. Gets scaled down based on size of ship.
14707 
14708  if (objp_weapon->type == OBJ_BEAM) // added beam for traitor detection -FUBAR
14709  damage = beam_get_ship_damage(&Beams[objp_weapon->instance], objp_hit);
14710  else
14711  damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14712 
14713  // wacky stuff here
14714  ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14715  if (shipp_hit->ship_max_hull_strength > 1000.0f) {
14716  float factor = shipp_hit->ship_max_hull_strength / 1000.0f;
14717  factor = MIN(100.0f, factor);
14718  damage /= factor;
14719  }
14720 
14721  // Don't penalize much at all for hitting cargo
14722  if (sip->class_type > -1) {
14723  damage *= Ship_types[sip->class_type].ff_multiplier;
14724  }
14725 
14726  // Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14727  if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14728  damage /= 5.0f;
14729  }
14730 
14732  pp->friendly_hits++;
14733 
14734  // cap damage and number of hits done this frame
14735  float accredited_damage = MIN(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14736  pp->friendly_damage += accredited_damage;
14737  pp->damage_this_burst += accredited_damage;
14738 
14739  // Done with adjustments to damage. Evaluate based on current friendly_damage
14740  nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
14741 
14742  if (is_instructor(objp_hit)) {
14743  // it's not nice to hit your instructor
14748 
14749  training_fail();
14750 
14751  // Instructor leaves.
14752  mission_do_departure(objp_hit);
14753  gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
14754  } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14755  // warning every 4 sec
14756  // use NULL as the message sender here since it is the Terran Command persona
14759  }
14760 
14761  // not nice to hit your friends
14762  } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14766 
14768 
14769  } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14770  // no closer than 4 sec intervals
14771  // Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
14772  // many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
14775  }
14776  }
14777 }
14778 
14782 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14783 {
14785 
14786  // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14787  if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14788  return;
14789  }
14790 
14791  // only set as target if can be targeted.
14792  if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14793  return;
14794  }
14795 
14796  if (aip->target_objnum != hitter_objnum)
14797  aip->aspect_locked_time = 0.0f;
14798  set_target_objnum(aip, hitter_objnum);
14799  aip->resume_goal_time = Missiontime + i2f(20); // Only chase up to 20 seconds.
14801 
14802  set_targeted_subsys(aip, NULL, -1); // Say not attacking any particular subsystem.
14803 
14804  aip->previous_submode = aip->mode;
14805  aip->mode = AIM_CHASE;
14806  aip->submode = SM_ATTACK;
14808 }
14809 
14810 
14811 // Return true if *objp has armed an aspect seeking bomb.
14812 // This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14814 {
14815  ship *shipp;
14816  int bank_index;
14817  ship_weapon *swp;
14818 
14819  shipp = &Ships[objp->instance];
14820 
14821  swp = &shipp->weapons;
14822 
14823  bank_index = swp->current_secondary_bank;
14824 
14825  if (bank_index != -1) {
14826  if (swp->secondary_bank_weapons[bank_index] > 0) {
14827  if (swp->secondary_bank_ammo[bank_index] > 0) {
14828  if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14830  return 1;
14831  }
14832  }
14833  }
14834  }
14835  }
14836 
14837  return 0;
14838 }
14839 
14840 // *objp collided with big ship *big_objp at global point *collide_pos
14841 // Make it fly away from the collision point.
14842 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14843 void big_ship_collide_recover_start(object *objp, object *big_objp, vec3d *collide_pos, vec3d *collision_normal)
14844 {
14845  ai_info *aip;
14846 
14847  Assert(objp->type == OBJ_SHIP);
14848 
14849  aip = &Ai_info[Ships[objp->instance].ai_index];
14850 
14852  return;
14853 
14854  if (collision_normal) {
14855  aip->big_recover_timestamp = timestamp(2000);
14856  aip->big_collision_normal = *collision_normal;
14857  } else {
14858  aip->big_recover_timestamp = timestamp(500);
14859  }
14860 
14863 
14864  // big_recover_pos_1 is 100 m out along normal
14865  vec3d direction;
14866  if (collision_normal) {
14867  direction = *collision_normal;
14868  } else {
14869  vm_vec_copy_scale(&direction, &objp->orient.vec.fvec, -1.0f);
14870  }
14871  vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14872 
14873  // go out 200 m from box closest box point
14874  get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14875 
14876  accelerate_ship(aip, 0.0f);
14877 }
14878 
14879 float max_lethality = 0.0f;
14880 
14881 void ai_update_lethality(object *pship_obj, object *other_obj, float damage)
14882 {
14883  // Goober5000 - stop any trickle-down errors from ship_do_damage
14884  Assert(other_obj);
14885  if (!other_obj)
14886  {
14887  return;
14888  }
14889 
14890  Assert(pship_obj); // Goober5000
14891  Assert(pship_obj->type == OBJ_SHIP);
14892  Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14893  int dont_count = FALSE;
14894 
14895  int parent = other_obj->parent;
14896  if (Objects[parent].type == OBJ_SHIP) {
14897  if (Objects[parent].signature == other_obj->parent_sig) {
14898 
14899  // check damage done to enemy team
14900  if (iff_x_attacks_y(Ships[pship_obj->instance].team, Ships[Objects[parent].instance].team)) {
14901 
14902  // other is weapon
14903  if (other_obj->type == OBJ_WEAPON) {
14904  weapon *wp = &Weapons[other_obj->instance];
14906 
14907  // if parent is BIG|HUGE, don't count beam
14908  if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14909  if (wif->wi_flags & WIF_BEAM) {
14910  dont_count = TRUE;
14911  }
14912  }
14913  }
14914 
14915  if (!dont_count) {
14916  float lethality = 0.025f * damage; // 2 cyclops (@2000) put you at 100 lethality
14917 
14918  // increase lethality weapon's parent ship
14919  ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14920  aip->lethality += lethality;
14921  aip->lethality = MIN(110.0f, aip->lethality);
14922  // if you hit, don't be less than 0
14923  aip->lethality = MAX(0.0f, aip->lethality);
14924  }
14925  }
14926  }
14927  }
14928 }
14929 
14930 
14934 void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hitpos, int shield_quadrant, vec3d *hit_normal)
14935 {
14936  int hitter_objnum = -2;
14937  object *objp_hitter = NULL;
14938  ship *shipp;
14939  ai_info *aip, *hitter_aip;
14940 
14941  shipp = &Ships[objp_ship->instance];
14942  aip = &Ai_info[shipp->ai_index];
14943 
14944  if (objp_ship->flags & OF_PLAYER_SHIP) {
14945  //SUSHI: So that hitting a player ship actually resets the last_hit_target_time counter for whoever hit the player.
14946  //This is all copypasted from code below
14947  // Added OBJ_BEAM for traitor detection - FUBAR
14948  if ((hit_objp->type == OBJ_WEAPON) || (hit_objp->type == OBJ_BEAM)) {
14949  hitter_objnum = hit_objp->parent;
14950  Assert((hitter_objnum < MAX_OBJECTS));
14951  if (hitter_objnum == -1) {
14952  return; // Possible SSM, bail while we still can.
14953  }
14954  objp_hitter = &Objects[hitter_objnum];
14955  } else if (hit_objp->type == OBJ_SHIP) {
14956  objp_hitter = hit_objp;
14957  } else {
14958  Int3(); // Should never happen.
14959  return;
14960  }
14961  Assert(objp_hitter != NULL);
14962  hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14963  hitter_aip->last_hit_target_time = Missiontime;
14964  return;
14965  }
14966 
14967  if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14968  return;
14969 
14970  if (hit_objp->type == OBJ_SHIP) {
14971  // If the object that this ship collided with is a big ship
14972  if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14973  // And the current object is _not_ a big ship
14974  if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14975  // Recover from hitting a big ship. Note, if two big ships collide, they just pound away at each other. Oh well. Recovery looks dumb and it's very late.
14976  big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14977  }
14978  }
14979  }
14980 
14981  // Added OBJ_BEAM for traitor detection - FUBAR
14982  if ((hit_objp->type == OBJ_WEAPON) || (hit_objp->type == OBJ_BEAM)) {
14983  if(hit_objp->parent < 0){
14984  return;
14985  }
14986  if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14987  return;
14988  }
14989 
14990  hitter_objnum = hit_objp->parent;
14991  Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14992  objp_hitter = &Objects[hitter_objnum];
14993 
14994  // lets not check hits by ghosts any further either
14995  if(objp_hitter->type == OBJ_GHOST)
14996  return;
14997 
14998  // Hit by a protected ship, don't attack it.
14999  if (objp_hitter->flags & OF_PROTECTED) {
15000  if (!object_is_docked(objp_ship)) {
15001  if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
15002  if (aip->mode == AIM_CHASE) {
15003  if (aip->submode != SM_EVADE_WEAPON) {
15004  aip->mode = AIM_CHASE;
15005  aip->submode = SM_EVADE_WEAPON;
15007  }
15008  } else if (aip->mode != AIM_EVADE_WEAPON) {
15010  aip->previous_mode = aip->mode;
15011  aip->previous_submode = aip->submode;
15012  aip->mode = AIM_EVADE_WEAPON;
15013  aip->submode = -1;
15015  aip->mode_time = timestamp(MAX_EVADE_TIME); // Evade for up to five seconds.
15016  }
15017  }
15018  }
15019  return;
15020  }
15021 
15022  maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp); // Deal with player's friendly fire.
15023 
15024  ship_maybe_ask_for_help(shipp);
15025  } else if (hit_objp->type == OBJ_SHIP) {
15026  if (shipp->team == Ships[hit_objp->instance].team) // Don't have AI react to collisions between teammates.
15027  return;
15028  objp_hitter = hit_objp;
15029  hitter_objnum = OBJ_INDEX(hit_objp);
15030  } else {
15031  Int3(); // Hmm, what kind of object hit this if not weapon or ship? Get MikeK.
15032  return;
15033  }
15034 
15035  // traitor detection was the only reason for OBJ_BEAM to make it this far.
15036  // so bail if it wasn't fired by a player.
15037  if ((hit_objp->type == OBJ_BEAM) && (!(objp_hitter->flags & OF_PLAYER_SHIP)))
15038  return;
15039 
15040  // Collided into a protected ship, don't attack it.
15041  if (hit_objp->flags & OF_PROTECTED)
15042  return;
15043 
15044  Assert(objp_hitter != NULL);
15045  hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
15046  hitter_aip->last_hit_target_time = Missiontime;
15047 
15048  // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
15049  hitter_aip->last_objsig_hit = objp_ship->signature;
15050 
15051  aip->last_hit_time = Missiontime;
15052 
15053  if (aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) // If not allowed to pursue dynamic objectives, don't evade. Dumb? Maybe change. -- MK, 3/15/98
15054  return;
15055 
15056  // If this ship is awaiting repair, abort!
15058  if (get_hull_pct(objp_ship) < 0.3f) {
15059  // Note, only abort if hull below a certain level.
15060  aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2); // Might request again after 15 seconds.
15061  if ( !(objp_ship->flags & OF_PLAYER_SHIP) ) // mwa -- don't abort rearm for a player
15062  ai_abort_rearm_request(objp_ship);
15063  }
15064  }
15065 
15066  // If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
15067  // Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
15068  if (firing_aspect_seeking_bomb(objp_ship)) {
15069  if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
15070  return;
15071  }
15072 
15073  // If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
15074  if (aip->mode == AIM_STRAFE) {
15075  Assert(hitter_objnum != -2);
15076  if (aip->target_objnum == hitter_objnum) {
15077  if ( hit_objp->type == OBJ_WEAPON ) {
15078  ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
15079  }
15080  return;
15081  }
15082  }
15083 
15084  if ((objp_ship == Player_obj) && !Player_use_ai) // Goober5000 - allow for exception
15085  return; // We don't do AI for the player.
15086 
15087  maybe_update_guard_object(objp_ship, objp_hitter);
15088 
15089  // Big ships don't go any further.
15090  if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
15091  return;
15092 
15093  // If the hitter object is the ignore object, don't attack it.
15094  ship_info *sip = &Ship_info[shipp->ship_info_index];
15095  if ((is_ignore_object(aip, OBJ_INDEX(objp_hitter))) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
15096  if (!object_is_docked(objp_ship)) {
15097  if (aip->mode == AIM_NONE) {
15098  aip->mode = AIM_CHASE; // This will cause the ship to move, if not attack.
15099  aip->submode = SM_EVADE;
15101  }
15102  }
15103  return;
15104  }
15105 
15106  // Maybe abort based on mode.
15107  switch (aip->mode) {
15108  case AIM_CHASE:
15109  if (aip->submode == SM_ATTACK_FOREVER)
15110  return;
15111 
15112  if ( (hit_objp->type == OBJ_WEAPON) && !(aip->ai_flags & AIF_NO_DYNAMIC) ) {
15113  if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
15114  return;
15115  }
15116  break;
15117  case AIM_GUARD:
15118  // If in guard mode and far away from guard object, don't pursue guy that hit me.
15119  if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
15120  if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
15121  return;
15122  }
15123  }
15124  case AIM_STILL:
15125  case AIM_STAY_NEAR:
15126  // Note: Dealt with above, at very top. case AIM_PLAY_DEAD:
15127  case AIM_STRAFE:
15128  break;
15129  case AIM_EVADE_WEAPON:
15130  case AIM_EVADE:
15131  case AIM_GET_BEHIND:
15132  case AIM_AVOID:
15133  case AIM_DOCK:
15134  case AIM_BIGSHIP:
15135  case AIM_PATH:
15136  case AIM_NONE:
15137  case AIM_BAY_DEPART:
15138  case AIM_SENTRYGUN:
15139  return;
15140  case AIM_BAY_EMERGE:
15141  // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
15142  if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
15143  return;
15144  }
15145  break;
15146  case AIM_WAYPOINTS:
15147  if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
15148  break;
15149  else
15150  return;
15151  break;
15152  case AIM_SAFETY:
15153  if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
15154  aip->submode = AISS_1;
15156  }
15157  return;
15158  break;
15159  case AIM_WARP_OUT:
15160  return;
15161  break;
15162  default:
15163  Int3(); // Bogus mode!
15164  }
15165 
15167  aip->ai_flags &= ~AIF_FORMATION; // If flying in formation, bug out!
15168 
15169  aip->hitter_objnum = hitter_objnum;
15170  aip->hitter_signature = Objects[hitter_objnum].signature;
15171 
15172  // If the hitter is not on the same team as the hittee, do some stuff.
15173  if (iff_x_attacks_y(shipp->team, Ships[objp_hitter->instance].team)) {
15174 
15175  if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
15176  maybe_set_dynamic_chase(aip, hitter_objnum);
15177  maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
15178  } else {
15179  if ((aip->mode == AIM_CHASE) && ai_near_full_strength(objp_ship)) {
15180  switch (aip->submode) {
15181  case SM_ATTACK:
15182  case SM_SUPER_ATTACK:
15183  case SM_GET_AWAY:
15184  break;
15185  default:
15186  if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
15187  maybe_set_dynamic_chase(aip, hitter_objnum);
15188  }
15189  maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
15190  break;
15191  }
15192  } else if (aip->mode == AIM_CHASE) {
15193  switch (aip->submode) {
15194  case SM_ATTACK:
15195  aip->submode = SM_EVADE;
15197  break;
15198  case SM_SUPER_ATTACK:
15199  if (Missiontime - aip->submode_start_time > i2f(1)) {
15200  aip->submode = SM_EVADE;
15202  }
15203  break;
15204  case SM_EVADE_BRAKE:
15205  break;
15206  case SM_EVADE_SQUIGGLE:
15207  aip->submode = SM_EVADE;
15209  break;
15210  default:
15211  if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
15212  maybe_set_dynamic_chase(aip, hitter_objnum);
15213  maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
15214  }
15215 
15216  break;
15217  }
15218  } else {
15219  // AL 3-15-98: Prevent escape pods from entering chase mode
15220  if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
15221  maybe_set_dynamic_chase(aip, hitter_objnum);
15222  }
15223  maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
15224  }
15225  }
15226  }
15227 }
15228 
15229 // Ship shipnum has been destroyed.
15230 // Cleanup.
15231 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
15232 // This function will get called in either case, and there are things that should be done if
15233 // the ship actually gets destroyed which shouldn't get done if it departed.
15234 void ai_ship_destroy(int shipnum, int method)
15235 {
15236  int objnum;
15237  object *other_objp;
15238  ship *shipp;
15239  ship_obj *so;
15240  ai_info *dead_aip;
15241 
15242  Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
15243  Assert((Ships[shipnum].ai_index >= 0) && (Ships[shipnum].ai_index < MAX_AI_INFO));
15244  objnum = Ships[shipnum].objnum;
15245  dead_aip = &Ai_info[Ships[shipnum].ai_index];
15246 
15247  // if I was getting repaired, or awaiting repair, then cleanup the repair mode. When awaiting repair, the support objnum
15248  // is -1. When the support ship is on the way, the suppoort objnum >= 0 (points to support ship).
15249  if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
15250  if ( dead_aip->support_ship_objnum >= 0 )
15252  else
15254  }
15255 
15256  // clear bay door animations
15257  ai_manage_bay_doors(&Objects[objnum], dead_aip, true);
15258 
15259  // For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
15260  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
15261  other_objp = &Objects[so->objnum];
15262  Assert(other_objp->instance != -1);
15263 
15264  shipp = &Ships[other_objp->instance];
15265  Assert(shipp->ai_index != -1);
15266 
15267  ai_info *aip = &Ai_info[shipp->ai_index];
15268 
15269  if (aip->target_objnum == objnum) {
15270  set_target_objnum(aip, -1);
15271  // If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
15272  if (aip->resume_goal_time != -1)
15273  aip->active_goal = AI_GOAL_NONE;
15274  }
15275 
15276  if (aip->goal_objnum == objnum) {
15277  aip->goal_objnum = -1;
15278  aip->goal_signature = -1;
15279  }
15280 
15281  if (aip->guard_objnum == objnum) {
15282  aip->guard_objnum = -1;
15283  aip->guard_signature = -1;
15284  }
15285 
15286  if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
15287  if (aip->guard_wingnum != aip->wing)
15288  ai_set_guard_wing(other_objp, aip->guard_wingnum);
15289  }
15290 
15291  if (aip->hitter_objnum == objnum)
15292  aip->hitter_objnum = -1;
15293  }
15294 }
15295 
15299 void ai_deathroll_start(object *dying_objp)
15300 {
15301  // make sure this is a ship
15302  Assert(dying_objp->type == OBJ_SHIP);
15303 
15304  // mark objects we are docked with so we can do damage and separate during death roll
15305  for (dock_instance *ptr = dying_objp->dock_list; ptr != NULL; ptr = ptr->next)
15306  {
15307  object *docked_objp = ptr->docked_objp;
15308  int docker_index = ptr->dockpoint_used;
15309  int dockee_index = dock_find_dockpoint_used_by_object(docked_objp, dying_objp);
15310 
15311  dock_dead_dock_objects(dying_objp, docker_index, docked_objp, dockee_index);
15312  }
15313 
15314  // clean up any rearm-related stuff
15315  ai_cleanup_rearm_mode(dying_objp);
15316 
15317  // clean up anybody docking or undocking to me
15318  ai_cleanup_dock_mode_objective(dying_objp);
15319 
15320  // Undock from every object directly docked to dying_objp. We can't just iterate through the list because
15321  // we're undocking the objects while we iterate over them and the pointers get seriously messed up.
15322  // So we just repeatedly remove the first object until the dying object is no longer docked to anything.
15323  while (object_is_docked(dying_objp))
15324  {
15325  object *docked_objp = dock_get_first_docked_object(dying_objp);
15326 
15327  // undock these objects
15328  ai_do_objects_undocked_stuff(dying_objp, docked_objp);
15329  }
15330 
15331  // clear my ai mode
15332  Ai_info[Ships[dying_objp->instance].ai_index].mode = AIM_NONE;
15333 }
15334 
15335 // Object *requester_objp tells rearm ship to abort rearm.
15336 // Returns true if it succeeded, else false.
15337 // To succeed means you were previously rearming.
15338 int ai_abort_rearm_request(object *requester_objp)
15339 {
15340  ship *requester_shipp;
15341  ai_info *requester_aip;
15342 
15343  Assert(requester_objp->type == OBJ_SHIP);
15344  if(requester_objp->type != OBJ_SHIP){
15345  return 0;
15346  }
15347  Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15348  if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15349  return 0;
15350  }
15351  requester_shipp = &Ships[requester_objp->instance];
15352  Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15353  if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15354  return 0;
15355  }
15356  requester_aip = &Ai_info[requester_shipp->ai_index];
15357 
15358  if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15359 
15360  // support objnum is always valid once a rearm repair has been requested. It points to the
15361  // ship that is coming to repair me.
15362  if (requester_aip->support_ship_objnum != -1) {
15363  object *repair_objp;
15364  ai_info *repair_aip;
15365 
15366  repair_objp = &Objects[requester_aip->support_ship_objnum];
15367  repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15368 
15369  // Make sure signatures match. This prevents nasty bugs in which an object
15370  // that was repairing another is destroyed and is replaced by another ship
15371  // before this code comes around.
15372  if (repair_objp->signature == requester_aip->support_ship_signature) {
15373 
15374  Assert( repair_objp->type == OBJ_SHIP );
15375 
15376  // if support ship is in the process of undocking, don't do anything.
15377  if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15378  ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15379 
15380  if ( repair_aip->submode == AIS_DOCK_4 )
15381  {
15382  repair_aip->submode = AIS_UNDOCK_0;
15383  repair_aip->submode_start_time = Missiontime;
15384  }
15385  else
15386  {
15387  // unwind all the support ship docking operations
15388  ai_cleanup_dock_mode_subjective(repair_objp);
15389  }
15390  } else {
15391  nprintf(("AI", "Not aborting rearm since already undocking\n"));
15392  }
15393  }
15394  } else {
15395  // setting these flags is the safe things to do. There may not be a corresponding repair
15396  // ship for this guys since a repair ship may be currently repairing someone else.
15397  ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15398 
15399  // try and remove this guy from an arriving support ship.
15400  mission_remove_scheduled_repair(requester_objp);
15401  }
15402 
15403  return 1;
15404  } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15405  // a support ship can request to abort when he is told to do something else (like warp out).
15406  // see if this support ships goal_objnum is valid. If so, then issue this ai_abort comment
15407  // for the ship that he is enroute to repair
15408  if ( requester_aip->goal_objnum != -1 ) {
15409  int val;
15410 
15411  val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15412  return val;
15413  }
15414  }
15415 
15416  return 0;
15417 }
15418 
15419 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15420 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15421 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15422 {
15423  ship *support_shipp, *requester_shipp;
15424  ai_info *support_aip, *requester_aip;
15425 
15426  support_shipp = &Ships[support_objp->instance];
15427  requester_shipp = &Ships[requester_objp->instance];
15428  requester_aip = &Ai_info[requester_shipp->ai_index];
15429 
15430  Assert( support_shipp->ai_index != -1 );
15431  support_aip = &Ai_info[support_shipp->ai_index];
15432 
15433  // if the requester is a player object, issue the order as the squadmate messaging code does. Doing so
15434  // ensures that the player get a higher priority!
15435  requester_aip->ai_flags |= AIF_AWAITING_REPAIR; // Tell that I'm awaiting repair.
15436  if ( requester_objp->flags & OF_PLAYER_SHIP )
15437  ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15438  else
15439  ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15440 
15441 }
15442 
15443 // Object *requester_objp requests rearming.
15444 // Returns objnum of ship coming to repair requester on success
15445 // Success means you found someone to rearm you and you weren't previously rearming.
15446 int ai_issue_rearm_request(object *requester_objp)
15447 {
15448  object *objp = NULL;
15449  ship *requester_shipp;
15450  ai_info *requester_aip;
15451 
15452  Assert(requester_objp->type == OBJ_SHIP);
15453  Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15454  requester_shipp = &Ships[requester_objp->instance];
15455 
15456  Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15457  requester_aip = &Ai_info[requester_shipp->ai_index];
15458 
15459  // these should have already been caught by the time we get here!
15460  Assert(!(requester_aip->ai_flags & AIF_AWAITING_REPAIR));
15461  Assert(is_support_allowed(requester_objp));
15462 
15463  requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP); // Might request again after this much time.
15464 
15465  // call ship_find_repair_ship to get a support ship. If none is found, then we will warp one in. This
15466  // function will return the next available ship which can repair requester
15467  int result = ship_find_repair_ship( requester_objp, &objp );
15468 
15469  // we are able to call in a ship, or a ship is warping in
15470  // (Arriving_support_ship may be non-NULL in either of these cases, but mission_bring_in_support_ship has a check for that)
15471  if (result == 0 || result == 2) {
15472  ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_QUEUE );
15473  mission_bring_in_support_ship( requester_objp );
15474  return -1;
15475  }
15476  // we are able to service a request
15477  else if (result == 1 || result == 3) {
15478  Assert(objp != NULL);
15479  ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15480  return OBJ_INDEX(objp);
15481  }
15482  // we aren't able to do anything!
15483  else {
15484  Assert(result == 4);
15485  Assertion(false, "This case should have already been caught by the is_support_allowed precheck!");
15486  return -1;
15487  }
15488 }
15489 
15493 void ai_rearm_repair( object *objp, int docker_index, object *goal_objp, int dockee_index )
15494 {
15495  ai_info *aip, *goal_aip;
15496 
15497  aip = &Ai_info[Ships[objp->instance].ai_index];
15498  aip->goal_objnum = OBJ_INDEX(goal_objp);
15499 
15500  ai_dock_with_object(objp, docker_index, goal_objp, dockee_index, AIDO_DOCK);
15501  aip->ai_flags |= AIF_REPAIRING; // Tell that repair guy is busy trying to repair someone.
15502 
15503  goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15504 
15505  goal_aip->support_ship_objnum = OBJ_INDEX(objp); // Tell which object is coming to repair.
15506  goal_aip->support_ship_signature = objp->signature;
15507 
15509 
15511 }
15512 
15513 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15514 // into polymodel->dockbays[] for the model associated with the object), return the index
15515 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15516 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15517 {
15518  if ( dockbay_index < 0 || dockee_objp == NULL ) {
15519  Int3(); // should never happen
15520  return -1;
15521  }
15522 
15523  if ( dockee_objp->type == OBJ_SHIP ) {
15524  int path_num;
15525  polymodel *pm;
15526 
15527  pm = model_get(Ship_info[Ships[dockee_objp->instance].ship_info_index].model_num );
15528 
15529  Assert(pm->n_docks > dockbay_index);
15530  Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15531  Assert(pm->docking_bays[dockbay_index].splines != NULL);
15532 
15533  if(pm->n_docks <= dockbay_index){
15534  return -1;
15535  }
15536  if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15537  return -1;
15538  }
15539  if(pm->docking_bays[dockbay_index].splines == NULL){
15540  return -1;
15541  }
15542 
15543  // We only need to return one path for the dockbay, so return the first
15544  path_num = pm->docking_bays[dockbay_index].splines[0];
15545  return path_num;
15546  } else {
15547  return -1;
15548  }
15549 }
15550 
15555 {
15556  ship_weapon *swp;
15557  swp = &shipp->weapons;
15558  int current_bank = swp->current_secondary_bank;
15559 
15560  ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15561  if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15562  if (ship_fire_secondary(objp)) {
15563  nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15564  swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15565  }
15566  }
15567 }
15568 
15569 // For the subspace mission (sm3-09a)
15570 // for delta wing
15571 // if they're sufficiently far into the mission
15572 // if they're near one or more enemies
15573 // every so often
15574 // fire a synaptic if they have one.
15576 {
15577  // Only do in subspace missions.
15579  return;
15580 
15581  // Only do in sm3-09a
15582  if (!stricmp(Game_current_mission_filename, "sm3-09a"))
15583  {
15584  ship *shipp;
15585  int wing_index, time;
15586 
15587  shipp = &Ships[objp->instance];
15588 
15589  if (!(strnicmp(shipp->ship_name, NOX("delta"), 5)))
15590  {
15591  wing_index = shipp->ship_name[6] - '1';
15592 
15593  if ((wing_index >= 0) && (wing_index < MAX_SHIPS_PER_WING))
15594  {
15595  time = Missiontime >> 16; // Convert to seconds.
15596  time -= 2*60; // Subtract off two minutes.
15597 
15598  if (time > 0)
15599  {
15600  int modulus = 17 + wing_index*3;
15601 
15602  if ((time % modulus) < 2)
15603  {
15604  int count = num_nearby_fighters(iff_get_attackee_mask(obj_team(objp)), &objp->pos, 1500.0f);
15605 
15606  if (count > 0)
15607  cheat_fire_synaptic(objp, shipp, aip);
15608  }
15609  }
15610  }
15611  }
15612  }
15613 }
void mc_info_init(mc_info *mc)
Definition: model.h:1138
#define DEFAULT_WING_MAG
Definition: aicode.cpp:11292
float ai_stalemate_time_thresh[NUM_SKILL_LEVELS]
Definition: ai.h:225
int ai_profile_flags2_set
Definition: ai.h:233
void dock_dead_dock_objects(object *objp1, int dockpoint1, object *objp2, int dockpoint2)
int target_num
Definition: weapon.h:172
vec3d dockee_point
Definition: ai.h:556
GLuint64EXT * result
Definition: Glext.h:10775
float ai_glide_strafe_percent
Definition: ai.h:456
void ai_update_aim(ai_info *aip)
Definition: aicode.cpp:3490
float lethality
Definition: ai.h:546
#define SUBSYSTEM_RADAR
Definition: model.h:55
void ai_attack_wing(object *attacker, int wingnum)
Definition: aicode.cpp:2326
fix last_attack_time
Definition: ai.h:419
int model_collide(mc_info *mc_info_obj)
char Parse_names[MAX_SHIPS+MAX_WINGS][NAME_LENGTH]
#define MESSAGE_PRIORITY_NORMAL
size_t wp_index
Definition: ai.h:373
vec3d big_recover_pos_2
Definition: ai.h:536
bool model_anim_start_type(ship_subsys *pss, int animation_type, int subtype, int direction, bool instant)
Definition: modelanim.cpp:566
#define AIDO_DOCK_NOW
Definition: ai.h:82
fix submode_start_time
Definition: ai.h:400
#define SHIP_GET_UNSILENCED
Definition: ship.h:1789
bsp_info * submodel
Definition: model.h:764
#define MESSAGE_TIME_IMMEDIATE
int flags
Definition: ship.h:150
vec3d G_fire_pos
Definition: aicode.cpp:5212
int timestamp(int delta_ms)
Definition: timer.cpp:226
void ai_chase_eb(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, float dist_to_enemy)
Definition: aicode.cpp:6544
int n_paths
Definition: model.h:783
wing Wings[MAX_WINGS]
Definition: ship.cpp:128
float vm_vec_dot_to_point(const vec3d *dir, const vec3d *p1, const vec3d *p2)
Definition: vecmat.cpp:1348
#define MULTIPLAYER_CLIENT
Definition: multi.h:132
#define AIF_REPAIRING
Definition: ai.h:41
int flags
Definition: iff_defs.h:45
int chance_to_use_missiles_on_plr[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:147
float get_world_closest_box_point_with_delta(vec3d *closest_box_point, object *box_obj, vec3d *start_point, int *is_inside, float delta)
struct eval_nearest_objnum eval_nearest_objnum
#define SUBSYSTEM_NAVIGATION
Definition: model.h:56
float prev_accel
Definition: ai.h:426
int Framecount
Definition: systemvars.cpp:22
int i
Definition: multi_pxo.cpp:466
float get_max_shield_quad(object *objp)
Definition: object.cpp:260
fix Missiontime
Definition: systemvars.cpp:19
int ai_find_shockwave_weapon(object *objp, ai_info *aip)
Definition: aicode.cpp:13466
#define WIF_BOMBER_PLUS
Definition: weapon.h:69
#define TRIGGER_TYPE_DOCKING_STAGE_2
Definition: modelanim.h:29
#define vm_free(ptr)
Definition: pstypes.h:548
#define AISS_1a
Definition: ai.h:89
int ai_profile_flags_set
Definition: ai.h:231
void ship_set_subsystem_strength(ship *shipp, int type, float strength)
Definition: ship.cpp:13500
#define AI_GOAL_UNDOCK
Definition: aigoals.h:36
void ai_set_guard_wing(object *objp, int wingnum)
Definition: aicode.cpp:7450
#define DEFAULT_WING_Y_DELTA
Definition: aicode.cpp:11290
#define AIS_STRAFE_RETREAT1
Definition: ai.h:290
model_subsystem * system_info
Definition: ship.h:314
#define STEALTH_FULLY_TARGETABLE
Definition: aicode.cpp:908
weapon Weapons[MAX_WEAPONS]
Definition: weapons.cpp:78
int primary_bank_weapons[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:103
float ai_accuracy[NUM_SKILL_LEVELS]
Definition: ai.h:200
fix avoid_check_timestamp
Definition: ai.h:532
float ai_ship_fire_secondary_delay_scale_friendly
Definition: ai.h:451
union ai_goal::@1 dockee
void ai_mission_goal_complete(ai_info *aip)
Definition: aigoals.cpp:360
void ai_process(object *obj, int ai_index, float frametime)
Definition: aicode.cpp:14232
#define MIN(a, b)
Definition: pstypes.h:296
#define MAX_SHIP_PRIMARY_BANKS
Definition: globals.h:62
vec3d * pos
Definition: model.h:1113
int ai_find_shockwave_ship(object *objp, ai_info *aip)
Definition: aicode.cpp:13532
#define SM_SF_AHEAD
Definition: aicode.cpp:91
EModelAnimationPosition turret_animation_position
Definition: ship.h:351
int cmeasure_fire_stamp
Definition: ship.h:654
#define AIM_BAY_DEPART
Definition: ai.h:184
#define vp(p)
Definition: modelsinc.h:70
float max_weapon_reserve
Definition: ship.h:1276
float min_lock_time
Definition: weapon.h:401
void init_semirand()
Initialize Semirand array. Doesn't have to be called.
Definition: staticrand.cpp:22
ai_info * Player_ai
Definition: ai.cpp:24
void ai_cleanup_rearm_mode(object *objp)
Definition: aicode.cpp:10491
int team
Definition: ship.h:606
void set_predicted_enemy_pos_turret(vec3d *predicted_enemy_pos, vec3d *gun_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, float weapon_speed, float time_enemy_in_range)
Definition: aicode.cpp:6344
int CurrentNav
Definition: autopilot.cpp:43
#define AIPF_FIX_LINKED_PRIMARY_BUG
Definition: ai_profiles.h:41
#define AIS_DOCK_4
Definition: ai.h:273
float vm_vec_mag_quick(const vec3d *v)
Definition: vecmat.cpp:371
int ship_engine_ok_to_warp(ship *sp)
Definition: ship.cpp:14783
matrix * vm_matrix_x_matrix(matrix *dest, const matrix *src0, const matrix *src1)
Definition: vecmat.cpp:1006
int depart_flags
Definition: model.h:543
char * get_name()
Definition: waypoint.cpp:84
float glide_strafe_percent[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:115
char name[NAME_LENGTH]
Definition: weapon.h:322
int iff_get_attacker_mask(int attackee_team)
Definition: iff_defs.cpp:583
void mission_log_add_entry(int type, char *pname, char *sname, int info_index)
Definition: missionlog.cpp:184
float ship_get_max_speed(ship *shipp)
Definition: ship.cpp:17279
void ai_sentrygun()
Definition: aicode.cpp:13019
vec3d vmd_z_vector
Definition: vecmat.cpp:27
vec3d View_position
Definition: 3dsetup.cpp:20
int num_primary_banks
Definition: ship.h:99
object * homing_object
Definition: weapon.h:177
int objnum
Definition: ship.h:537
#define MAX_IGNORE_NEW_OBJECTS
Definition: ai.h:327
int type
Definition: ai.h:332
control_info ai_override_ci
Definition: ai.h:549
int Game_mode
Definition: systemvars.cpp:24
#define AIG_TYPE_EVENT_WING
Definition: ai.h:98
#define F1_0
Definition: fix.h:15
#define AIM_BAY_EMERGE
Definition: ai.h:183
vec3d rotvel
Definition: physics.h:78
dock attach
Definition: gamesnd.h:86
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
vec3d mins
Definition: model.h:746
int game_type
Definition: missionparse.h:138
#define MISSION_FLAG_FULLNEB
Definition: missionparse.h:70
#define AI_PATH_MODE_ALT1
Definition: ai_profiles.h:74
int compute_num_homing_objects(object *target_objp)
Definition: aicode.cpp:5919
int secondary_select_timestamp
Definition: ai.h:509
float ai_glide_strafe_percent[NUM_SKILL_LEVELS]
Definition: ai.h:223
float frand_range(float min, float max)
Return a floating point number in the range min..max.
Definition: floating.cpp:50
int static_rand_alt()
Get a random integer between 1 and RND_MAX.
Definition: staticrand.cpp:176
int set_target_objnum(ai_info *aip, int objnum)
Definition: aicode.cpp:1250
int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
Definition: aicode.cpp:6108
char Game_current_mission_filename[MAX_FILENAME_LEN]
Definition: fredstubs.cpp:26
polymodel * model_get(int model_num)
Definition: modelread.cpp:3134
ubyte bay_doors_launched_from
Definition: ship.h:780
int obj_team(object *objp)
Definition: object.cpp:1843
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
submodel_instance_info * sii
Definition: model.h:94
void update_min_max(float val, float *min, float *max)
Definition: aicode.cpp:4104
#define SIF_SUPERCAP
Definition: ship.h:901
float nearest_locked_distance
Definition: ai.h:487
void ai_good_secondary_time(int team, int weapon_index, int max_fire_count, char *shipname)
Definition: aicode.cpp:326
int shockwave_object
Definition: ai.h:517
float shield_get_strength(object *objp)
ship_weapon weapons
Definition: ship.h:658
#define SM_SS_UL
Definition: aicode.cpp:99
#define AIORF_FORWARD
Definition: ai.h:131
float ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:124
ship_subsys * set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
Definition: aicode.cpp:1289
SCP_vector< game_snd > Snds
Definition: gamesnd.cpp:19
#define DELAY_TARGET_TIME
Definition: aicode.cpp:261
bool ai_class_autoscale
Definition: ai.h:240
float fire_wait
Definition: weapon.h:359
float time_enemy_near
Definition: ai.h:418
float vm_vec_mag(const vec3d *v)
Definition: vecmat.cpp:325
#define SF2_NO_DISABLED_SELF_DESTRUCT
Definition: ship.h:511
#define WIF_LOCKED_HOMING
Definition: weapon.h:130
#define AIORF_FULL
Definition: ai.h:124
float flFrametime
Definition: fredstubs.cpp:22
#define AIPF_SMART_SECONDARY_WEAPON_SELECTION
Definition: ai_profiles.h:24
int weapon_index
Definition: aicode.cpp:272
void validate_mode_submode(ai_info *aip)
Definition: aicode.cpp:13927
float ai_endangered_by_weapon(ai_info *aip)
Definition: aicode.cpp:6584
float stalemate_time_thresh[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:117
void ai_add_ship_goal_player(int type, int mode, int submode, char *shipname, ai_info *aip)
Definition: aigoals.cpp:757
#define SF2_NO_SUBSPACE_DRIVE
Definition: ship.h:487
vec3d desired_vel
Definition: physics.h:70
Definition: weapon.h:163
#define AIM_SAFETY
Definition: ai.h:179
GLuint index
Definition: Glext.h:5608
void ai_waypoints()
Definition: aicode.cpp:4556
#define SM_ATTACK
Definition: ai.h:247
float ship_get_warpout_speed(object *objp)
Definition: ship.cpp:17306
fix last_hit_target_time
Definition: ai.h:422
int collide_predict_large_ship(object *objp, float distance)
Definition: objcollide.cpp:967
void change_acceleration(ai_info *aip, float delta_accel)
Definition: aicode.cpp:1377
#define AI_GOAL_DOCK
Definition: aigoals.h:30
int avoid_player(object *objp, vec3d *goal_pos)
Definition: aicode.cpp:6772
int active_goal
Definition: ai.h:413
physics_info phys_info
Definition: object.h:157
vec3d avoid_goal_point
Definition: ai.h:531
int mission_do_departure(object *objp, bool goal_is_to_warp)
#define WIF_HUGE
Definition: weapon.h:64
#define MAX_WINGS
Definition: globals.h:50
int path_subsystem_next_check
Definition: ai.h:391
#define MAX_SHIPS
Definition: globals.h:37
#define MISSION_FLAG_USE_AP_CINEMATICS
Definition: missionparse.h:89
void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
Definition: aicode.cpp:12159
#define WT_HYPERSPACE
Definition: ship.h:1111
int id
Definition: model.h:732
float ai_get_away_chance[NUM_SKILL_LEVELS]
Definition: ai.h:238
#define SM_SS_LL
Definition: aicode.cpp:102
#define SM_SS_DONE
Definition: aicode.cpp:104
int previous_submode
Definition: ai.h:395
float turn_time_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:112
void ai_ignore_object(object *ignorer, object *ignored, int ignore_new)
Definition: aicode.cpp:2440
int model_num
Definition: ship.h:1189
float ship_max_shield_strength
Definition: ship.h:596
float best_dot_to_enemy
Definition: ai.h:396
#define AITTV_IGNORE_BANK
Definition: ai.h:119
int ai_profile_flags2
Definition: ai.h:232
#define UNUSED_OBJNUM
Definition: object.h:51
int next_primary_fire_stamp[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:113
void parse_int_list(int *ilist, int size)
Definition: parselo.cpp:4194
#define AI_PATH_MODE_NORMAL
Definition: ai_profiles.h:73
#define MSS_FLAG2_DESTROYED_ROTATION
Definition: model.h:142
void render_path_points(object *objp)
Definition: aicode.cpp:6230
float max_hits
Definition: ship.h:320
int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
Definition: aicode.cpp:15516
int class_type
Definition: ship.h:1167
#define STEALTH_MAX_VIEW_DIST
Definition: aicode.cpp:190
#define MAX_REPAIR_SPEED
Definition: aicode.cpp:182
int ai_chance_to_use_missiles_on_plr[NUM_SKILL_LEVELS]
Definition: ai.h:227
int path_start
Definition: ai.h:378
void init_aip_from_class_and_profile(ai_info *aip, ai_class *aicp, ai_profile_t *profile)
Definition: aicode.cpp:14490
#define AIF_TRYING_UNSUCCESSFULLY_TO_WARP
Definition: ai.h:65
void mabs_pick_goal_point(object *objp, object *big_objp, vec3d *collision_point, vec3d *avoid_pos)
Definition: aicode.cpp:6889
#define SIF_CRUISER
Definition: ship.h:887
int model_instance_num
Definition: ship.h:802
float vm_vec_normalize_quick(vec3d *src)
Definition: vecmat.cpp:529
#define INSTRUCTOR_SHIP_NAME
Definition: aicode.cpp:81
#define SM_GET_BEHIND
Definition: ai.h:253
int next_secondary_fire_stamp[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:115
float ai_glide_attack_percent[NUM_SKILL_LEVELS]
Definition: ai.h:221
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
#define PF_GLIDING
Definition: physics.h:32
#define MESSAGE_INSTRUCTOR_ATTACK
waypoint_list * wp_list
Definition: ai.h:372
Assert(pm!=NULL)
uint flags2
Definition: model.h:170
int target_objnum
Definition: ai.h:339
#define STI_AI_AUTO_ATTACKS
Definition: ship.h:1005
int submode
Definition: ai.h:394
vec3d prev_goal_point
Definition: ai.h:429
void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags)
Definition: aicode.cpp:3215
#define SM_GET_AWAY
Definition: ai.h:254
void _cdecl void void _cdecl void _cdecl void _cdecl WarningEx(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dist_to_enemy, float enemy_radius)
Definition: aicode.cpp:7576
int detail[MAX_MODEL_DETAIL_LEVELS]
Definition: model.h:738
int ai_set_attack_subsystem(object *objp, int subnum)
Definition: aicode.cpp:7359
int path_goal_dist
Definition: ai.h:390
Definition: pstypes.h:88
#define mprintf(args)
Definition: pstypes.h:238
#define MISSION_FLAG_SUPPORT_REPAIRS_HULL
Definition: missionparse.h:74
#define TRIGGER_TYPE_DOCKING_STAGE_1
Definition: modelanim.h:28
int ai_index
Definition: ship.h:538
ship_weapon weapons
Definition: ship.h:362
#define AIORF_FULL_LAT
Definition: ai.h:128
#define MESSAGE_SUPPORT_KILLED
w_bank * gun_banks
Definition: model.h:772
int force_warp_time
Definition: ai.h:515
int ship_fire_primary(object *obj, int stream_weapons, int force)
Definition: ship.cpp:10760
int is_training_mission()
fix afterburner_stop_time
Definition: ai.h:503
void ai_emerge_bay_path_cleanup(ai_info *aip)
Definition: aicode.cpp:12737
#define NUM_SKILL_LEVELS
Definition: systemvars.h:150
#define OBJ_ASTEROID
Definition: object.h:44
void training_fail()
hull_check p0
Definition: lua.cpp:5051
int ai_class
Definition: ai.h:369
float srotation_time
Definition: ship.h:1199
int ship_stop_fire_primary(object *obj)
Definition: ship.cpp:10711
float link_energy_levels_always[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:94
float target_time
Definition: ai.h:350
#define AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS
Definition: ai_profiles.h:43
#define DEFAULT_WING_Z_DELTA
Definition: aicode.cpp:11291
float dock_move_towards_point(object *objp, vec3d *start, vec3d *finish, float speed_scale, float other_obj_speed=0.0f, rotating_dockpoint_info *rdinfo=NULL)
Definition: aicode.cpp:8905
void multi_maybe_send_repair_info(object *dest_objp, object *source_objp, int code)
Definition: multiutil.cpp:1551
float ai_endangered_time(object *ship_objp, object *weapon_objp)
Definition: aicode.cpp:6565
float bay_arrive_speed_mult
Definition: ai_profiles.h:158
void ai_add_goal_ship_internal(ai_info *aip, int goal_type, char *name, int docker_point, int dockee_point, int immediate)
Definition: aigoals.cpp:1244
pnode Path_points[MAX_PATH_POINTS]
Definition: aicode.cpp:263
object * En_objp
Definition: aicode.cpp:170
#define LOG_SHIP_UNDOCKED
Definition: missionlog.h:28
#define SM_EVADE_SQUIGGLE
Definition: ai.h:248
void ai_form_on_wing(object *objp, object *goal_objp)
Definition: aicode.cpp:3290
int get_nearest_bbox_point(object *ship_objp, vec3d *start, vec3d *box_pt)
Definition: ship.cpp:18664
int secondary_bank_start_ammo[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:135
#define AIS_UNDOCK_2
Definition: ai.h:277
struct vec3d::@225::@227 xyz
int path_cur
Definition: ai.h:379
int ai_profile_flags2
Definition: ai.h:464
float afterburner_fuel
Definition: ship.h:648
int next_rearm_request_timestamp
Definition: ai.h:507
#define AIGF_DEPART_SOUND_PLAYED
Definition: ai.h:111
CButton * team
float bay_depart_speed_mult
Definition: ai_profiles.h:159
void static_randvec(int num, vec3d *vp)
[To be described]
Definition: staticrand.cpp:111
float link_ammo_levels_maybe[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:99
float max_lethality
Definition: aicode.cpp:14879
object * get_wing_leader(int wingnum)
Definition: aicode.cpp:11251
void ai_warp_out(object *objp)
Definition: aicode.cpp:13368
vec3d max_vel
Definition: physics.h:49
GLclampf f
Definition: Glext.h:7097
SCP_vector< huge_fire_info > Ai_huge_fire_info
Definition: aicode.cpp:277
float damp
Definition: ship.h:1193
float y
Definition: pstypes.h:107
#define MAX_PATH_POINTS
Definition: ai.h:322
#define MAX_OBJECTS
Definition: globals.h:83
int * splines
Definition: model.h:528
void parse_aitbl()
Definition: aicode.cpp:784
void mission_goal_fail_all()
dock_instance * next
Definition: objectdock.h:19
void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
Definition: aicode.cpp:2875
#define TRUE
Definition: pstypes.h:399
ship_subsys * ss
Definition: aicode.cpp:11073
int find_ignore_new_object_index(ai_info *aip, int objnum)
Definition: aicode.cpp:1877
void ai_start_fly_to_ship(object *objp, int shipnum)
Definition: aicode.cpp:3186
bool can_glide
Definition: ship.h:1406
void set_accel_for_target_speed(object *objp, float tspeed)
Definition: aicode.cpp:1400
matrix * vm_copy_transpose(matrix *dest, const matrix *src)
Definition: vecmat.cpp:984
bool dock_check_find_direct_docked_object(object *objp, object *other_objp)
Definition: objectdock.cpp:91
ai_info Ai_info[MAX_AI_INFO]
Definition: ai.cpp:23
float compute_time_to_enemy(float dist_to_enemy, object *pobjp)
Definition: aicode.cpp:3403
float afterburner_fuel_capacity
Definition: ship.h:1283
vec3d * vm_vec_rotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:933
fix best_dot_to_time
Definition: ai.h:398
#define Assertion(expr, msg,...)
Definition: clang.h:41
#define REPAIR_INFO_COMPLETE
Definition: multi.h:312
int mrs_subsystem(ship *shipp, int type)
Definition: aicode.cpp:13139
float factor
Definition: lua.cpp:440
int dock_find_dockpoint_used_by_object(object *objp, object *other_objp)
Definition: objectdock.cpp:116
float lead_scale
Definition: ai.h:468
#define SM_SS_BOX2
Definition: aicode.cpp:103
float ai_link_energy_levels_always
Definition: ai.h:446
#define SF2_NAVPOINT_CARRY
Definition: ship.h:488
object obj_used_list
Definition: object.cpp:53
int find_parent_rotating_submodel(polymodel *pm, int dock_index)
Definition: aicode.cpp:8994
#define f2fl(fx)
Definition: floating.h:37
EModelAnimationPosition bay_doors_status
Definition: ship.h:776
#define STI_AI_GUARDS_ATTACK
Definition: ship.h:1007
void render_all_subsys_paths(object *objp)
Definition: aicode.cpp:6190
int big_recover_timestamp
Definition: ai.h:537
#define CMEASURE_WAIT
Definition: cmeasure.h:20
int AI_watch_object
Definition: aicode.cpp:199
vec3d maxs
Definition: model.h:746
#define AI_GOAL_DISARM_SHIP
Definition: aigoals.h:40
int ai_formation()
Definition: aicode.cpp:11639
fix path_next_check_time
Definition: ai.h:389
void ai_do_objects_undocked_stuff(object *docker, object *dockee)
Definition: aicode.cpp:3054
Definition: ship.h:1516
fix t2
Definition: animplay.cpp:37
char ** Ai_class_names
Definition: aicode.cpp:268
int current_primary_bank
Definition: ship.h:106
vec3d guard_vec
Definition: ai.h:485
int target_signature
Definition: ai.h:340
void ai_safety_circle_spot(object *objp)
Definition: aicode.cpp:4195
ship_subsys * targeted_subsys
Definition: ai.h:472
#define SF_ARRIVING
Definition: ship.h:453
void ai_ignore_wing(object *ignorer, int wingnum)
Definition: aicode.cpp:2485
void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
Definition: aicode.cpp:5939
void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
Definition: aicode.cpp:12350
#define UNINITIALIZED_VALUE
Definition: aicode.cpp:79
int n_quadrants
Definition: object.h:158
int ai_good_time_to_rearm(object *objp)
Definition: aicode.cpp:313
float ai_turret_max_aim_update_delay
Definition: ai.h:462
float max_speed
Definition: weapon.h:354
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: Glext.h:5156
int burst_flags
Definition: weapon.h:534
#define AIF_SEEK_LOCK
Definition: ai.h:42
int vector_object_collision(vec3d *start_pos, vec3d *end_pos, object *objp, float radius_scale)
Definition: objcollide.cpp:713
Definition: ai.h:329
float dock_calc_docked_speed(object *objp)
Definition: objectdock.cpp:269
#define AIM_NONE
Definition: ai.h:175
float weapon_energy
Definition: ship.h:640
uint flags
Definition: ship.h:644
#define MAX_SHIP_BAY_PATHS
Definition: model.h:538
int ship_fire_secondary(object *obj, int allow_swarm)
Definition: ship.cpp:11799
void ai_maybe_self_destruct(object *objp, ai_info *aip)
Definition: aicode.cpp:13813
#define AIS_UNDOCK_4
Definition: ai.h:279
#define SM_BIG_APPROACH
Definition: ai.h:260
#define MAX_UNDOCK_ABORT_SPEED
Definition: aicode.cpp:183
#define SIF_NO_SHIP_TYPE
Definition: ship.h:894
#define WIF_HOMING_ASPECT
Definition: weapon.h:45
void ai_clear_ship_goals(ai_info *aip)
Definition: aigoals.cpp:251
float ai_stalemate_time_thresh
Definition: ai.h:458
void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
Definition: aiturret.cpp:2219
int g3_draw_line(vertex *p0, vertex *p1)
Definition: 3ddraw.cpp:112
float weapon_range
Definition: weapon.h:390
#define MAX_SHIPS_PER_WING
Definition: globals.h:52
char dockee_point[NAME_LENGTH]
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
#define OF_PLAYER_SHIP
Definition: object.h:109
int mp_index
Definition: ai.h:388
int aas_1(object *objp, ai_info *aip, vec3d *safe_pos)
Definition: aicode.cpp:13568
#define AIPF_USE_ADDITIVE_WEAPON_VELOCITY
Definition: ai_profiles.h:34
void ai_set_guard_vec(object *objp, object *guard_objp)
Definition: aicode.cpp:7411
object * objp
Definition: lua.cpp:3105
int ship_navigation_ok_to_warp(ship *sp)
Definition: ship.cpp:14809
int parent_submodel
Definition: model.h:417
float lifeleft
Definition: weapon.h:169
void ai_add_rearm_goal(object *requester_objp, object *support_objp)
Definition: aicode.cpp:15421
int ai_select_primary_weapon_OLD(object *objp, object *other_objp, int flags)
Definition: aicode.cpp:5223
#define SM_AVOID
Definition: ai.h:252
GLenum GLenum GLenum GLenum GLenum scale
Definition: Glext.h:8503
#define OBJ_OBSERVER
Definition: object.h:43
int current_secondary_bank
Definition: ship.h:107
GLfloat GLfloat GLfloat v2
Definition: Glext.h:5640
void ai_dock()
Definition: aicode.cpp:10613
int subtype
Definition: weapon.h:326
#define Int3()
Definition: pstypes.h:292
#define MESSAGE_HAMMER_SWINE
vec3d G_predicted_pos
Definition: aicode.cpp:5212
vec3d max_rotvel
Definition: physics.h:52
float beam_get_ship_damage(beam *b, object *objp)
Definition: beam.cpp:3592
void ai_chase()
Definition: aicode.cpp:8148
char stamp[STAMP_STRING_LENGTH]
Definition: fredview.cpp:74
#define AIF_KAMIKAZE
Definition: ai.h:55
ship_bay_t * ship_bay
Definition: model.h:776
int required_string_either(char *str1, char *str2)
Checks for one of two required strings.
Definition: parselo.cpp:673
int Nebula_sec_range
Definition: hudlock.cpp:307
int ai_formation_object_get_slotnum(int objnum, object *objp)
Definition: aicode.cpp:3332
int Debug_render_wing_phantoms
Definition: aicode.cpp:11446
#define AI_GOAL_NONE
Definition: ai.h:194
void ai_cruiser_chase()
Definition: aicode.cpp:7987
ship * shipp
Definition: lua.cpp:9162
int support_ship_objnum
Definition: ai.h:479
void process_friendly_hit_message(int message, object *objp)
Definition: aicode.cpp:14608
void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vec3d *player_pos, vec3d *enemy_pos)
Definition: aicode.cpp:3469
#define AIP_FLAG2
Definition: ai_profiles.h:18
#define STEALTH_NOT_IN_FRUSTUM
Definition: aicode.cpp:906
float ai_max_aim_update_delay
Definition: ai.h:461
int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
Definition: aicode.cpp:348
vec3d pos
Definition: object.h:152
float emp_intensity
Definition: ship.h:717
int objects_will_collide(object *A, object *B, float duration, float radius_scale)
Definition: objcollide.cpp:672
#define AIF_USE_STATIC_PATH
Definition: ai.h:46
fix resume_goal_time
Definition: ai.h:425
vec3d * p0
Definition: model.h:1114
vec3d view_pos
Definition: fredrender.cpp:103
#define SIF_GAS_MINER
Definition: ship.h:909
#define AI_UPDATE_DOCK
Definition: multi.h:342
#define SUBSYSTEM_GAS_COLLECT
Definition: model.h:61
void ship_get_global_turret_info(object *objp, model_subsystem *tp, vec3d *gpos, vec3d *gvec)
Definition: aiturret.cpp:1303
int ai_flags
Definition: ai.h:330
#define TRIGGER_TYPE_DOCKING_STAGE_3
Definition: modelanim.h:30
#define NEXT_REARM_TIMESTAMP
Definition: aicode.cpp:85
SCP_list< waypoint_list > Waypoint_lists
Definition: waypoint.cpp:9
#define IS_VEC_NULL(v)
Definition: vecmat.h:28
float vm_vec_mag_squared(const vec3d *v)
Definition: vecmat.cpp:339
float ship_fire_secondary_delay_scale_friendly[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:128
void ai_rearm_repair(object *objp, int docker_index, object *goal_objp, int dockee_index)
Definition: aicode.cpp:15493
float ai_glide_attack_percent
Definition: ai.h:454
#define SIF_AFTERBURNER
Definition: ship.h:879
vec3d last_predicted_enemy_pos
Definition: ai.h:416
vec3d pnt[MAX_SLOTS]
Definition: model.h:434
vec3d submodel_pos
Definition: ai.h:559
int signature
Definition: object.h:145
int subobj_num
Definition: model.h:175
int ai_issue_rearm_request(object *requester_objp)
Definition: aicode.cpp:15446
void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp)
Definition: aicode.cpp:2257
int ai_need_new_target(object *pl_objp, int target_objnum)
Definition: aicode.cpp:13850
int AI_FrameCount
Definition: aicode.cpp:198
void ai_big_guard()
Definition: aicode.cpp:9804
int model_num
Definition: model.h:1110
GLenum type
Definition: Gl.h:1492
float ai_turn_time_scale
Definition: ai.h:453
float ai_random_sidethrust_percent
Definition: ai.h:457
dock_bay * docking_bays
Definition: model.h:774
#define MAX_EMP_INACCURACY
Definition: aicode.cpp:186
#define SM_SS_BOX0
Definition: aicode.cpp:97
#define AIF_FORMATION_OBJECT
Definition: ai.h:43
float ai_cmeasure_fire_chance[NUM_SKILL_LEVELS]
Definition: ai.h:207
#define AIPF_ALLOW_VERTICAL_DODGE
Definition: ai_profiles.h:49
#define AITTV_FAST
Definition: ai.h:117
#define DCF(function_name, help_text)
The potent DCF macro, used to define new debug commands for the console.
Definition: console.h:60
#define AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS
Definition: ai_profiles.h:26
#define WIF2_CAPITAL_PLUS
Definition: weapon.h:107
#define CLAMP(x, min, max)
Definition: pstypes.h:488
#define AIM_WAYPOINTS
Definition: ai.h:173
#define AIORF_HEADING
Definition: ai.h:127
void find_adjusted_dockpoint_info(vec3d *global_p0, vec3d *global_p1, vec3d *global_p0_norm, object *objp, polymodel *pm, int modelnum, int submodel, int dock_index)
Definition: aicode.cpp:9025
#define AIP_FLAG
Definition: ai_profiles.h:17
fix ai_predict_position_delay
Definition: ai.h:447
void ship_actually_depart(int shipnum, int method)
Definition: ship.cpp:7748
iff_info Iff_info[MAX_IFFS]
Definition: iff_defs.cpp:20
int iff_get_attackee_mask(int attacker_team)
Definition: iff_defs.cpp:561
int goal_check_time
Definition: ai.h:414
void ai_big_chase()
Definition: aibig.cpp:851
#define AIG_TYPE_PLAYER_SHIP
Definition: ai.h:99
#define AIPF_NO_MIN_DOCK_SPEED_CAP
Definition: ai_profiles.h:31
int num_ships_attacking(int target_objnum)
Definition: aicode.cpp:9346
int weapon_info_index
Definition: weapon.h:164
void ai_balance_shield(object *objp)
Definition: aicode.cpp:12271
#define SUBSYSTEM_COMMUNICATION
Definition: model.h:57
float lifetime
Definition: weapon.h:382
void gr_set_color(int r, int g, int b)
Definition: 2d.cpp:1188
void ai_do_safety(object *objp)
Definition: aicode.cpp:3273
int get_available_secondary_weapons(object *objp, int *outlist, int *outbanklist)
Definition: ship.cpp:12667
SCP_vector< waypoint > & get_waypoints()
Definition: waypoint.cpp:89
void ai_chase_big_approach_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
Definition: aicode.cpp:7813
int path_length
Definition: ai.h:380
#define AI_ACTIVE_GOAL_DYNAMIC
Definition: ai.h:196
int objnum
Definition: ship.h:1483
void ai_evade_object(object *evader, object *evaded)
Definition: aicode.cpp:2362
int rearm_first_missile
Definition: ai.h:499
int ai_profile_flags
Definition: ai.h:463
float ai_random_sidethrust_percent[NUM_SKILL_LEVELS]
Definition: ai.h:224
ship_subsys subsys_list
Definition: ship.h:630
int abort_rearm_timestamp
Definition: ai.h:539
#define CIRCLE_STRAFE_MAX_DIST
Definition: aicode.cpp:87
GLenum GLenum GLvoid * row
Definition: Glext.h:5241
player Players[MAX_PLAYERS]
int objnum
Definition: weapon.h:561
#define SM_SF_BEHIND
Definition: aicode.cpp:92
#define SIF_CAPITAL
Definition: ship.h:889
void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
Definition: aicode.cpp:14651
#define AIS_WARP_1
Definition: ai.h:304
#define AIM_GUARD
Definition: ai.h:171
int object_is_docked(object *objp)
Definition: object.cpp:2019
void model_find_submodel_offset(vec3d *outpnt, int model_num, int sub_model_num)
Definition: modelread.cpp:4041
int Num_alloced_ai_classes
Definition: aicode.cpp:196
vec3d pos
Definition: ai.h:317
int mission_remove_scheduled_repair(object *objp)
vec3d homing_pos
Definition: weapon.h:179
#define SIF_BOMBER
Definition: ship.h:886
vec3d path_create_pos
Definition: ai.h:386
int friendly_hits
Definition: player.h:129
fix next_predict_pos_time
Definition: ai.h:403
#define AIM_BE_REARMED
Definition: ai.h:178
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define MAX_SHIP_SECONDARY_BANKS
Definition: globals.h:63
#define AIS_DOCK_0
Definition: ai.h:269
void model_instance_find_world_point(vec3d *outpnt, vec3d *mpnt, int model_instance_num, int submodel_num, const matrix *objorient, const vec3d *objpos)
Definition: modelread.cpp:4135
matrix * vm_transpose(matrix *m)
Definition: vecmat.cpp:971
void add_path_point(vec3d *pos, int path_num, int path_index, int modify_index)
Definition: aicode.cpp:2512
vec3d big_attack_point
Definition: ai.h:524
int instance
Definition: object.h:150
int stealth_last_cheat_visible_stamp
Definition: ai.h:343
void send_ai_info_update_packet(object *objp, char what, object *other_objp)
Definition: multimsgs.cpp:4943
#define AIS_WARP_4
Definition: ai.h:307
#define AIF_TEMPORARY_IGNORE
Definition: ai.h:44
int waypoint_speed_cap
Definition: ai.h:375
float ai_turret_max_aim_update_delay[NUM_SKILL_LEVELS]
Definition: ai.h:229
void afterburners_stop(object *objp, int key_released)
fix friendly_last_hit_time
Definition: player.h:131
#define MESSAGE_INSTRUCTOR_HIT
int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
Definition: aicode.cpp:5296
matrix * vm_vector_2_matrix(matrix *m, const vec3d *fvec, const vec3d *uvec, const vec3d *rvec)
Definition: vecmat.cpp:850
int ok_to_target_timestamp
Definition: ai.h:521
GLenum GLint * range
Definition: Glext.h:7096
GLintptr offset
Definition: Glext.h:5497
Definition: player.h:85
int path_objnum
Definition: ai.h:383
float ship_get_subsystem_strength(ship *shipp, int type)
Definition: ship.cpp:13446
void hud_support_view_stop(int stop_now)
Stop displaying the support view pop-up.
Definition: hud.cpp:2551
void ai_get_dock_goal_indexes(object *objp, ai_info *aip, ai_goal *aigp, object *goal_objp, int &docker_index, int &dockee_index)
Definition: aicode.cpp:10360
int repair_penalty[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:142
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
const float PI2
Definition: pstypes.h:305
net_player_info p_info
Definition: multi.h:473
float ai_circle_strafe_percent
Definition: ai.h:455
vec3d last_pos
Definition: object.h:155
float gwlr_1(object *objp, ai_info *aip)
Definition: aicode.cpp:11329
float ai_matrix_dist(matrix *mat1, matrix *mat2)
Definition: aicode.cpp:3562
float stealth_sweep_box_size
Definition: ai.h:345
void ai_update_lethality(object *pship_obj, object *other_obj, float damage)
Definition: aicode.cpp:14881
ship_subsys * ship_get_indexed_subsys(ship *sp, int index, vec3d *attacker_pos)
Definition: ship.cpp:13340
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
void evade_ship()
Definition: aicode.cpp:5087
float pitch
Definition: physics.h:100
dock_instance * dock_list
Definition: object.h:166
int danger_weapon_objnum
Definition: ai.h:482
void ship_maybe_tell_about_low_ammo(ship *sp)
Definition: ship.cpp:15837
#define SIF_BIG_SHIP
Definition: ship.h:944
int type
Definition: ai.h:138
int flags
Definition: model.h:1116
vec3d desired_rotvel
Definition: physics.h:71
#define CHASE_CIRCLE_DIST
Definition: aicode.cpp:12184
struct matrix::@228::@230 vec
#define AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE
Definition: ai_profiles.h:42
#define SIF_NAVBUOY
Definition: ship.h:891
int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time)
Definition: aicode.cpp:6955
float ai_path()
Definition: aicode.cpp:4087
int attacker_objnum
Definition: ai.h:353
object * trial_objp
Definition: aicode.cpp:1912
#define MAX_AI_BEHAVIORS
Definition: ai.h:189
#define BIG_GUARD_RADIUS
Definition: aicode.cpp:177
int Num_weapons
Definition: weapons.cpp:107
#define AIM_STAY_NEAR
Definition: ai.h:169
char dockee[NAME_LENGTH]
void ai_cleanup_dock_mode_objective(object *objp)
Definition: aicode.cpp:10481
float ship_max_hull_strength
Definition: ship.h:597
int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
Definition: aicode.cpp:2029
#define AIORF_ROLL
Definition: ai.h:125
void vm_vec_scale(vec3d *dest, float s)
Definition: vecmat.cpp:248
int timestamp_until(int stamp)
Definition: timer.cpp:242
float ai_get_away_chance
Definition: ai.h:435
int submode_parm1
Definition: ai.h:402
#define nprintf(args)
Definition: pstypes.h:239
void ai_chase_ct()
Definition: aicode.cpp:6505
void ai_cruiser_chase_set_goal_pos(vec3d *goal_pos, object *pl_objp, object *en_objp)
Definition: aicode.cpp:7940
#define GM_MULTIPLAYER
Definition: systemvars.h:18
int g3_draw_sphere(vertex *pnt, float rad)
Definition: 3ddraw.cpp:475
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
Definition: aicode.cpp:5993
#define AIS_STRAFE_POSITION
Definition: ai.h:292
int compact_ignore_new_objects(ai_info *aip, int force=0)
Definition: aicode.cpp:2389
int primary_bank_ammo[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:121
#define SIF_AVOID_SHOCKWAVE
Definition: ship.h:952
void ai_stealth_sweep()
Definition: aicode.cpp:7095
#define AIPF_ALLOW_RAPID_SECONDARY_DUMBFIRE
Definition: ai_profiles.h:25
#define WIF_SWARM
Definition: weapon.h:59
void ai_stay_near()
Definition: aicode.cpp:10535
#define SW_FLAG_TURRET_LOCK
Definition: ship.h:95
void parse_float_list(float *plist, int size)
Definition: parselo.cpp:4182
#define SHIP_DEPARTED_BAY
Definition: ship.h:1625
char name[NAME_LENGTH]
Definition: ship.h:1517
#define SM_BIG_PARALLEL
Definition: ai.h:262
void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
Definition: aicode.cpp:1056
int secondary_bank_capacity[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:136
#define SM_SS_UR
Definition: aicode.cpp:101
int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
Definition: aicode.cpp:9614
#define AIF_USE_EXIT_PATH
Definition: ai.h:45
int Debug_k
Definition: aicode.cpp:3353
#define TRIGGER_TYPE_DOCKED
Definition: modelanim.h:31
vec3d * p1
Definition: model.h:1115
int ai_fire_primary_weapon(object *objp)
Definition: aicode.cpp:5626
int flags
Definition: ship.h:1227
void ai_do_stay_near(object *objp, object *other_objp, float dist)
Definition: aicode.cpp:3254
#define DEFAULT_WING_X_DELTA
Definition: aicode.cpp:11289
int num_paths
Definition: model.h:540
ai_profile_t * ai_profile
Definition: missionparse.h:168
#define AIF_AVOID_SHOCKWAVE_WEAPON
Definition: ai.h:51
int self_destruct_timestamp
Definition: ai.h:520
void ai_debug_render_stuff()
Definition: aicode.cpp:11081
float get_hull_pct(object *objp)
Definition: object.cpp:271
#define SUBSYS_PATH_DIST
Definition: ai.h:569
void dock_undock_objects(object *objp1, object *objp2)
Definition: objectdock.cpp:621
#define SIF_SMALL_SHIP
Definition: ship.h:943
void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
Definition: aicode.cpp:7700
#define strnicmp(s1, s2, n)
Definition: config.h:272
int wingnum
Definition: ship.h:623
#define AIPF2_FIX_AI_PATH_ORDER_BUG
Definition: ai_profiles.h:67
int is_ignore_object(ai_info *aip, int objnum, int just_the_original=0)
Definition: aicode.cpp:1891
void guard_object_was_hit(object *guard_objp, object *hitter_objp)
Definition: aicode.cpp:9471
#define DOA_UNDOCK_2
Definition: ai.h:76
float ai_accuracy
Definition: ai.h:432
#define AIPF2_ALL_SHIPS_MANAGE_SHIELDS
Definition: ai_profiles.h:57
int bay_doors_anim_done_time
Definition: ship.h:775
int ai_rearm_timestamp
Definition: iff_defs.h:50
int mode_time
Definition: ai.h:338
float speed
Definition: physics.h:79
float compute_incoming_payload(object *target_objp)
Definition: aicode.cpp:5966
float ai_get_weapon_dist(ship_weapon *swp)
Definition: aicode.cpp:6288
int maybe_resume_previous_mode(object *objp, ai_info *aip)
Definition: aicode.cpp:4757
int num_secondary_banks
Definition: ship.h:100
#define AIPF2_NO_SPECIAL_PLAYER_AVOID
Definition: ai_profiles.h:55
ai_goal goals[MAX_AI_GOALS]
Definition: ai.h:412
#define OBJ_WEAPON
Definition: object.h:33
int snd_play_3d(game_snd *gs, vec3d *source_pos, vec3d *listen_pos, float radius, vec3d *source_vel, int looping, float vol_scale, int priority, vec3d *sound_fvec, float range_factor, int force, bool is_ambient)
Definition: sound.cpp:594
int rand_chance(float frametime, float chance)
Call this in the frame interval to get TRUE chance times per second.
Definition: floating.cpp:66
int mode
Definition: ai.h:336
void ai_transfer_shield(object *objp, int quadrant_num)
Definition: aicode.cpp:12242
float time_enemy_in_range
Definition: ai.h:417
void remove_farthest_attacker(int objnum)
Definition: aicode.cpp:9384
float primary_ammo_burst_mult[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:100
float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip, bool burst)
Definition: aicode.cpp:7785
void ship_maybe_ask_for_help(ship *sp)
Definition: ship.cpp:15668
int shipnum
Definition: ai.h:331
float ai_path_1()
Definition: aicode.cpp:3924
void accelerate_ship(ai_info *aip, float accel)
Definition: aicode.cpp:1370
int stealth_last_visible_stamp
Definition: ai.h:344
#define AIDO_UNDOCK
Definition: ai.h:83
#define AIF_BIG_SHIP_COLLIDE_RECOVER_1
Definition: ai.h:59
void ai_init()
Definition: aicode.cpp:828
int ai_avoid_shockwave(object *objp, ai_info *aip)
Definition: aicode.cpp:13711
void set_aic_flag(ai_class *aicp, char *name, int flag, int type)
Definition: aicode.cpp:575
void stuff_string(char *outstr, int type, int len, char *terminators)
Definition: parselo.cpp:1189
#define TRIGGER_TYPE_DOCK_BAY_DOOR
Definition: modelanim.h:34
#define WP_LASER
Definition: weapon.h:27
#define MAX_WEAPON_TYPES
Definition: globals.h:73
int ship_get_SIF(ship *shipp)
Definition: ship.cpp:16096
ship_subsys * last_subsys_target
Definition: ai.h:473
float hull_strength
Definition: object.h:160
float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
Definition: aicode.cpp:1558
GLfloat v0
Definition: Glext.h:5638
#define PD_FORWARD
Definition: ai.h:298
float get_skill_stealth_dist_scaler()
Definition: aicode.cpp:913
#define MESSAGE_REPAIR_ABORTED
fix best_dot_from_time
Definition: ai.h:399
float ai_in_range_time
Definition: ai.h:441
int ship_get_random_player_wing_ship(int flags, float max_dist, int persona_index, int get_first, int multi_team)
Definition: ship.cpp:14508
float ai_link_ammo_levels_maybe
Definition: ai.h:442
vec3d big_collision_normal
Definition: ai.h:534
#define OBJ_DEBRIS
Definition: object.h:37
void vm_vec_sub2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:187
float turn_towards_tangent(object *objp, vec3d *point, float radius)
Definition: aicode.cpp:1533
int parent
Definition: object.h:147
void hud_lock_reset(float lock_time_scale)
Definition: hudlock.cpp:271
float ai_ship_fire_delay_scale_friendly
Definition: ai.h:449
float ai_ship_fire_secondary_delay_scale_hostile
Definition: ai.h:452
#define CF_TYPE_TABLES
Definition: cfile.h:50
int ai_mode
Definition: ai.h:136
#define AIORF_SIDEWAYS
Definition: ai.h:130
int n_docks
Definition: model.h:770
#define MC_SUBMODEL
Definition: model.h:1178
#define SM_SUPER_ATTACK
Definition: ai.h:251
#define SM_EVADE_WEAPON
Definition: ai.h:255
#define AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY
Definition: ai_profiles.h:27
float forward
Definition: physics.h:105
fix next_aim_pos_time
Definition: ai.h:408
#define REPAIR_INFO_ONWAY
Definition: multi.h:310
void hud_support_view_abort()
Definition: hud.cpp:2566
submodel_instance * submodel
Definition: model.h:100
float prev_dot_to_goal
Definition: ai.h:427
int ai_find_closest_depart_path(ai_info *aip, polymodel *pm, int allowed_path_mask)
Definition: aicode.cpp:12824
#define MAX_AI_GOALS
Definition: ai.h:91
fix path_next_create_time
Definition: ai.h:385
#define AISS_2
Definition: ai.h:87
void init_ai_system()
Definition: aicode.cpp:14458
#define SF_HIDDEN_FROM_SENSORS
Definition: ship.h:464
void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1)
Definition: aicode.cpp:2621
int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
Definition: aicode.cpp:7679
void project_point_to_perimeter(vec3d *perim_point, vec3d *pos, float radius, vec3d *vp)
Definition: aicode.cpp:1424
void do_random_sidethrust(ai_info *aip, ship_info *sip)
Definition: aicode.cpp:6609
#define OF_PROTECTED
Definition: object.h:108
#define SIF_SENTRYGUN
Definition: ship.h:892
#define vm_vec_negate(v)
Definition: vecmat.h:70
#define SUBSYSTEM_TURRET
Definition: model.h:54
void force_avoid_player_check(object *objp, ai_info *aip)
Definition: aicode.cpp:2244
int danger_weapon_signature
Definition: ai.h:483
float ai_ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS]
Definition: ai.h:216
mc_info * collide
Definition: lua.cpp:4532
void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
Definition: aicode.cpp:2229
float current_max_speed
Definition: ship.h:641
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:97
void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
Definition: aicode.cpp:11996
char * Mode_text[MAX_AI_BEHAVIORS]
Definition: aicode.cpp:108
hull_check p1
Definition: lua.cpp:5052
vec3d norm[MAX_DOCK_SLOTS]
Definition: model.h:531
void ai_dock_with_object(object *docker, int docker_index, object *dockee, int dockee_index, int dock_type)
Definition: aicode.cpp:3098
int required_string(const char *pstr)
Definition: parselo.cpp:468
void ai_disband_formation(int wingnum)
Definition: aicode.cpp:11556
int static_rand_timed(int num, int modulus)
Definition: aicode.cpp:4808
void bisect_chord(vec3d *p0, vec3d *p1, vec3d *centerp, float radius)
Definition: aicode.cpp:2534
int engine_recharge_index
Definition: ship.h:639
void ai_stay_still(object *still_objp, vec3d *view_pos)
Definition: aicode.cpp:2982
#define SF_PRIMARY_LINKED
Definition: ship.h:458
#define fl_abs(fl)
Definition: floating.h:31
float newval
Definition: lua.cpp:354
float ai_link_energy_levels_always[NUM_SKILL_LEVELS]
Definition: ai.h:213
#define AIS_DOCK_3
Definition: ai.h:272
void hud_support_view_start()
Start displaying the support view pop-up. This will remain up until hud_support_view_stop() is called...
Definition: hud.cpp:2540
#define AIS_WARP_2
Definition: ai.h:305
int secondary_bank_weapons[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:104
#define AIPF2_AI_AIMS_FROM_SHIP_CENTER
Definition: ai_profiles.h:60
fix last_hit_time
Definition: ai.h:420
void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
Definition: aicode.cpp:6752
char * target_name
Definition: ai.h:143
int nverts
Definition: model.h:418
GLdouble s
Definition: Glext.h:5321
float ai_stalemate_dist_thresh
Definition: ai.h:459
#define MAX_AI_DEBUG_RENDER_STUFF
Definition: aicode.cpp:11071
int model_anim_get_time_type(ship_subsys *pss, int animation_type, int subtype)
Definition: modelanim.cpp:740
int ai_near_full_strength(object *objp)
Definition: aicode.cpp:6604
#define AIM_CHASE
Definition: ai.h:166
ship_subsys * ship_return_next_subsys(ship *shipp, int type, vec3d *attacker_pos)
Definition: ship.cpp:14945
#define AIG_TYPE_PLAYER_WING
Definition: ai.h:100
float vm_vec_normalized_dir(vec3d *dest, const vec3d *end, const vec3d *start)
Definition: vecmat.cpp:591
int shield_manage_timestamp
Definition: ai.h:519
SCP_map< int, int > autopilot_wings
Definition: autopilot.cpp:57
int bay_doors_wanting_open
Definition: ship.h:777
object * dock_get_first_docked_object(object *objp)
Definition: objectdock.cpp:47
float vm_vec_dist(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:355
int asteroid_count()
Definition: asteroid.cpp:1473
void ship_primary_changed(ship *sp)
Definition: ship.cpp:15996
float ai_ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS]
Definition: ai.h:217
int optional_string(const char *pstr)
Definition: parselo.cpp:539
#define PTRDIFF_T_ARG
Definition: clang.h:62
int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
Definition: aicode.cpp:13887
int current_count
Definition: ship.h:1530
void slide_face_ship()
Definition: aicode.cpp:5019
#define AIS_GUARD_PATROL
Definition: ai.h:282
float get_skill_stealth_dot_scaler()
Definition: aicode.cpp:941
int lssm_stage
Definition: weapon.h:216
char name[MAX_NAME_LEN]
Definition: model.h:415
#define AI_CLASS_INCREMENT
Definition: aicode.cpp:783
float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
Definition: aicode.cpp:1017
float friendly_damage
Definition: player.h:130
weapon_info * ai_get_weapon(ship_weapon *swp)
Definition: aicode.cpp:6321
float ai_link_ammo_levels_always
Definition: ai.h:443
float ai_get_weapon_speed(ship_weapon *swp)
Definition: aicode.cpp:6304
#define AIM_WARP_OUT
Definition: ai.h:186
#define MAX_GUARD_DIST
Definition: aicode.cpp:176
void ai_announce_ship_dying(object *dying_objp)
Definition: aicode.cpp:13506
void ai_bay_depart()
Definition: aicode.cpp:12956
float current_hits
Definition: ship.h:319
int flags2
Definition: ship.h:1228
Definition: model.h:407
#define REARM_BREAKOFF_DELAY
Definition: aicode.cpp:173
float ai_evasion[NUM_SKILL_LEVELS]
Definition: ai.h:201
Definition: ship.h:534
int wi_flags3
Definition: weapon.h:386
float ai_turn_time_scale[NUM_SKILL_LEVELS]
Definition: ai.h:220
#define SIF_BALLISTIC_PRIMARIES
Definition: ship.h:880
char Mission_filename[80]
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
Definition: parselo.cpp:1995
#define AIF_FREE_AFTERBURNER_USE
Definition: ai.h:67
#define AIORF_UP
Definition: ai.h:129
#define AISS_1
Definition: ai.h:86
char * Strafe_submode_text[5]
Definition: aicode.cpp:154
void ai_set_guard_object(object *objp, object *other_objp)
Definition: aicode.cpp:7532
ai_flag_name Ai_flag_names[]
Definition: aicode.cpp:203
int combine_flags(int base_flags, int override_flags, int override_set)
Definition: aicode.cpp:14463
int max_allowed_player_homers[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:87
#define AIF_UNLOAD_SECONDARIES
Definition: ai.h:48
float G_collision_time
Definition: aicode.cpp:5211
int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
Definition: aicode.cpp:3509
float radius
Definition: model.h:1117
dock detach
Definition: gamesnd.h:87
float ai_max_aim_update_delay[NUM_SKILL_LEVELS]
Definition: ai.h:228
#define BIGNUM
Definition: aicode.cpp:3351
float slide_accel
Definition: ship.h:1203
#define AIPF_BIG_SHIPS_CAN_ATTACK_BEAM_TURRETS_ON_UNTARGETED_SHIPS
Definition: ai_profiles.h:22
#define delta
Definition: fvi.cpp:418
int turret_num_firing_points
Definition: model.h:191
float ai_circle_strafe_percent[NUM_SKILL_LEVELS]
Definition: ai.h:222
#define MESSAGE_PRIORITY_LOW
int will_collide_with_big_ship(object *objp, vec3d *goal_point, object *big_objp, vec3d *collision_point, float delta_time)
Definition: aicode.cpp:6822
int pp_collide(vec3d *curpos, vec3d *goalpos, object *goalobjp, float radius)
Definition: objcollide.cpp:926
bool AutoPilotEngaged
Definition: autopilot.cpp:40
#define AIF_AWAITING_REPAIR
Definition: ai.h:39
int secondary_bank_ammo[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:134
int maybe_avoid_player(object *objp, vec3d *goal_pos)
Definition: aicode.cpp:2911
vec3d vmd_y_vector
Definition: vecmat.cpp:26
#define REPAIR_INFO_END
Definition: multi.h:303
GLdouble GLdouble t
Definition: Glext.h:5329
#define MC_CHECK_SPHERELINE
Definition: model.h:1177
#define MIN_DIST_TO_WAYPOINT_GOAL
Definition: aicode.cpp:175
void ai_chase_fly_away(object *objp, ai_info *aip)
Definition: aicode.cpp:7643
int targeted_subsys_parent
Definition: ai.h:474
void set_primary_weapon_linkage(object *objp)
Definition: aicode.cpp:5496
void set_goal_dock_orient(matrix *dom, vec3d *docker_p0, vec3d *docker_p1, vec3d *docker_p0_norm, matrix *docker_orient, vec3d *dockee_p0, vec3d *dockee_p1, vec3d *dockee_p0_norm, matrix *dockee_orient)
Definition: aicode.cpp:8953
int ai_chance_to_use_missiles_on_plr
Definition: ai.h:460
int maybe_request_support(object *objp)
Definition: aicode.cpp:13182
#define SUBSYSTEM_WEAPONS
Definition: model.h:58
int submode_parm0
Definition: ai.h:401
void ai_big_strafe()
Definition: aibig.cpp:1638
int max_attackers[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:88
float ai_primary_ammo_burst_mult[NUM_SKILL_LEVELS]
Definition: ai.h:211
void dock_dock_objects(object *objp1, int dockpoint1, object *objp2, int dockpoint2)
Definition: objectdock.cpp:602
vec3d * vm_vec_unrotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:959
#define SM_CONTINUOUS_TURN
Definition: ai.h:246
int ignore_new_objnums[MAX_IGNORE_NEW_OBJECTS]
Definition: ai.h:366
#define AIORF_PITCH
Definition: ai.h:126
beam Beams[MAX_BEAMS]
Definition: beam.cpp:60
float vm_vec_dist_squared(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:344
float ai_link_ammo_levels_maybe[NUM_SKILL_LEVELS]
Definition: ai.h:209
int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_mask, vec3d *pos, vec3d *fvec)
Definition: aicode.cpp:12632
#define SHIELD_BALANCE_RATE
Definition: aicode.cpp:12237
pnode * Ppfp
Definition: aicode.cpp:264
void free_ai_stuff()
Definition: aicode.cpp:516
#define AIF_AVOIDING_BIG_SHIP
Definition: ai.h:58
int iff_x_attacks_y(int team_x, int team_y)
Definition: iff_defs.cpp:605
#define AI_GOAL_FLY_TO_SHIP
Definition: aigoals.h:56
int get_subsystem_pos(vec3d *pos, object *objp, ship_subsys *subsysp)
Definition: ship.cpp:12975
float get_wing_largest_radius(object *objp, int formation_object_flag)
Definition: aicode.cpp:11374
vec3d rotation_time
Definition: ship.h:1198
bool ship_useful_for_departure(int shipnum, int path_mask)
Definition: ship.cpp:17448
const size_t INVALID_WAYPOINT_POSITION
Definition: waypoint.cpp:16
char name[NAME_LENGTH]
Definition: ship.h:1163
int wp_flags
Definition: ai.h:374
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
for(int idx=0;idx< i;idx++)
Definition: multi_pxo.cpp:472
float vm_vec_copy_normalize_quick(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:512
#define SM_SS_LR
Definition: aicode.cpp:98
long fix
Definition: pstypes.h:54
GLclampd n
Definition: Glext.h:7286
#define AIM_DOCK
Definition: ai.h:174
#define AITTV_VIA_SEXP
Definition: ai.h:118
#define WF_NAV_CARRY
Definition: ship.h:1513
unsigned char ubyte
Definition: pstypes.h:62
#define MISS_SHIELDS
Definition: shiphit.h:23
void shield_add_strength(object *objp, float delta)
vec3d afterburner_max_vel
Definition: physics.h:50
int wi_flags
Definition: weapon.h:384
#define vm_vec_zero(v)
Definition: vecmat.h:37
uint flags
Definition: physics.h:37
int Mission_all_attack
Definition: aicode.cpp:200
int find_nearby_threat(int objnum, int enemy_team_mask, float range, int *count)
Definition: aicode.cpp:2092
float primary_bank_fof_cooldown[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:128
float afterburner_recharge_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:107
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
void get_wing_delta(vec3d *_delta_vec, int wing_index)
Definition: aicode.cpp:11299
int previous_mode
Definition: ai.h:337
void ai_guard()
Definition: aicode.cpp:9983
#define DOA_UNDOCK_1
Definition: ai.h:75
#define ZERO_VECTOR
Definition: vecmat.h:60
#define SIF_DRYDOCK
Definition: ship.h:902
float Energy_levels[NUM_ENERGY_LEVELS]
Definition: hudets.cpp:26
#define OBJ_INDEX(objp)
Definition: object.h:235
#define MULTI_TEAM
Definition: multi.h:655
float get_wing_lowest_av_ab_speed(object *objp)
Definition: aicode.cpp:1769
SCP_map< SCP_string, path_metadata > pathMetadata
Definition: ship.h:1453
vec3d hit_point_world
Definition: model.h:1124
ship * Player_ship
Definition: ship.cpp:124
int ship_get_ship_for_departure(int team)
Definition: ship.cpp:17477
#define vm_vec_make(v, _x, _y, _z)
Definition: vecmat.h:48
float dist
Definition: aicode.cpp:6882
void get_absolute_wing_pos(vec3d *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
Definition: aicode.cpp:11409
int mission_is_repair_scheduled(object *objp)
vec3d * vm_vec_avg3(vec3d *dest, const vec3d *src0, const vec3d *src1, const vec3d *src2)
Definition: vecmat.cpp:228
int ai_override_flags
Definition: ai.h:548
void stuff_boolean(int *i, bool a_to_eol)
Definition: parselo.cpp:2519
void ai_maybe_depart(object *objp)
Definition: aicode.cpp:13313
int primary_select_timestamp
Definition: ai.h:508
#define AIF_AVOIDING_SMALL_SHIP
Definition: ai.h:57
int ship_find_repair_ship(object *requester_obj, object **ship_we_found)
Definition: ship.cpp:14008
matrix orient
Definition: object.h:153
#define SIF2_NO_PRIMARY_LINKING
Definition: ship.h:928
#define AIS_STRAFE_AVOID
Definition: ai.h:289
int wi_flags2
Definition: weapon.h:385
int Ai_firing_enabled
Definition: aicode.cpp:194
#define LOG_SHIP_DOCKED
Definition: missionlog.h:26
int path_dir
Definition: ai.h:381
#define NOX(s)
Definition: pstypes.h:473
int artillery_sig
Definition: ai.h:543
float ai_evasion
Definition: ai.h:432
#define MESSAGE_REPAIR_DONE
int is_support_allowed(object *objp, bool do_simple_check)
Definition: ship.cpp:16749
char * ai_get_goal_target_name(const char *name, int *index)
Definition: ai.cpp:85
float gwlr_object_1(object *objp, ai_info *aip)
Definition: aicode.cpp:11353
GLuint start
Definition: Gl.h:1502
#define AIM_SENTRYGUN
Definition: ai.h:185
void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
Definition: aicode.cpp:894
#define HULL_DAMAGE_THRESHOLD_PERCENT
Definition: ship.h:49
void ai_big_strafe_maybe_attack_turret(object *ship_objp, object *weapon_objp)
Definition: aibig.cpp:1778
void vm_vec_copy_scale(vec3d *dest, const vec3d *src, float s)
Definition: vecmat.cpp:257
int Ai_goal_signature
Definition: aigoals.cpp:58
#define WIF_BOMB
Definition: weapon.h:63
#define WBF_FAST_FIRING
Definition: weapon.h:149
#define OBJ_SHIP
Definition: object.h:32
void garbage_collect_path_points()
Definition: aicode.cpp:407
#define AIS_DOCK_4A
Definition: ai.h:274
GLfloat GLfloat v1
Definition: Glext.h:5639
#define DOA_APPROACH
Definition: ai.h:73
void fds_aux(float *dot, float *tts, vec3d *pos, float dtime, object *objp)
Definition: aicode.cpp:3444
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
int ai_override_timestamp
Definition: ai.h:550
float bank
Definition: physics.h:104
int rearm_first_ballistic_primary
Definition: ai.h:500
int ai_aburn_use_factor
Definition: ai.h:433
#define AIF_ATTACK_SLOWLY
Definition: ai.h:53
vec3d prev_fvec
Definition: physics.h:82
float max_speed
Definition: ship.h:1230
#define AIS_STRAFE_RETREAT2
Definition: ai.h:291
void dc_stuff_float(float *f)
Stuffs a float to the given variable.
GLbitfield flags
Definition: Glext.h:6722
void vm_vec_rand_vec_quick(vec3d *rvec)
Definition: vecmat.cpp:1379
#define STEALTH_VIEW_CONE_DOT
Definition: aicode.cpp:191
#define AIPF_SMART_AFTERBURNER_MANAGEMENT
Definition: ai_profiles.h:40
#define WBF_RANDOM_LENGTH
Definition: weapon.h:150
float ship_get_exp_outer_rad(object *ship_objp)
Definition: ship.cpp:16970
#define vm_malloc(size)
Definition: pstypes.h:547
float ai_patience[NUM_SKILL_LEVELS]
Definition: ai.h:203
#define WIF2_PIERCE_SHIELDS
Definition: weapon.h:85
#define SIF_HUGE_SHIP
Definition: ship.h:945
#define SF2_NO_BUILTIN_MESSAGES
Definition: ship.h:491
int turret_animation_done_time
Definition: ship.h:352
#define AIF_FORMATION
Definition: ai.h:70
void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1=-1, int priority2=-1, int wif2_priority1=-1, int wif2_priority2=-1)
Definition: aicode.cpp:5801
void ai_chase_attack(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, float dist_to_enemy, int modelnum)
Definition: aicode.cpp:7227
void reset_parse(char *text)
Definition: parselo.cpp:3305
float max_aim_update_delay[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:119
#define FRIENDLY_DAMAGE_THRESHOLD
Definition: aicode.cpp:14604
int guard_wingnum
Definition: ai.h:360
#define AIF_BEING_REPAIRED
Definition: ai.h:40
#define SM_FLY_AWAY
Definition: ai.h:256
GLuint const GLchar * name
Definition: Glext.h:5608
#define REPAIR_INFO_QUEUE
Definition: multi.h:305
#define AISS_3
Definition: ai.h:88
float ai_cmeasure_fire_chance
Definition: ai.h:440
float ai_link_ammo_levels_always[NUM_SKILL_LEVELS]
Definition: ai.h:210
GLdouble GLdouble right
Definition: Glext.h:10330
object * Pl_objp
Definition: aicode.cpp:169
bool ai_class_autoscale
Definition: ai.h:437
float awacs_get_level(object *target, ship *viewer, int use_awacs)
Definition: awacs.cpp:168
int Player_use_ai
GLuint GLfloat * val
Definition: Glext.h:6741
float x
Definition: pstypes.h:107
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
void physics_read_flying_controls(matrix *orient, physics_info *pi, control_info *ci, float sim_time, vec3d *wash_rot)
Definition: physics.cpp:449
int ignore_expire_timestamp
Definition: ai.h:505
#define SUBSYSTEM_ENGINE
Definition: model.h:53
vec3d vel
Definition: physics.h:77
#define AIM_EVADE
Definition: ai.h:167
#define vm_realloc(ptr, size)
Definition: pstypes.h:551
float fspeed
Definition: physics.h:80
vec3d stealth_velocity
Definition: ai.h:347
fix t1
Definition: animplay.cpp:37
#define MISSION_TYPE_TRAINING
Definition: missionparse.h:63
#define DOA_DOCK
Definition: ai.h:74
int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vec3d *goal_point, vec3d *collision_point, float *distance, float delta_time)
Definition: aicode.cpp:6846
#define SIF_SUPPORT
Definition: ship.h:878
float ai_in_range_time[NUM_SKILL_LEVELS]
Definition: ai.h:208
#define SM_SS_BOX1
Definition: aicode.cpp:100
void ai_level_init()
Definition: aicode.cpp:853
float turret_max_aim_update_delay[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:120
int num_hits
Definition: model.h:1121
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
#define AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY
Definition: ai_profiles.h:47
vec3d last_aim_enemy_vel
Definition: ai.h:410
void model_instance_find_world_dir(vec3d *out_dir, vec3d *in_dir, int model_instance_num, int submodel_num, const matrix *objorient)
Definition: modelread.cpp:4587
int last_objsig_hit
Definition: ai.h:504
void ai_control_info_check(object *obj, ai_info *aip)
Definition: aicode.cpp:14181
#define DOA_DOCK_STAY
Definition: ai.h:78
int Chaos_frame
Definition: aicode.cpp:11576
int pick_big_attack_point_timestamp
Definition: ai.h:526
int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold)
Definition: aicode.cpp:5765
float get_shield_pct(object *objp)
Definition: object.cpp:308
#define SF_DEPARTING
Definition: ship.h:475
#define REPAIR_INFO_KILLED
Definition: multi.h:311
void ai_big_ship(object *objp)
Definition: aibig.cpp:1156
#define SM_SS_SET_GOAL
Definition: aicode.cpp:96
o
Definition: lua.cpp:442
#define MESSAGE_PRIORITY_HIGH
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)
Definition: aicode.cpp:1131
LOCAL int ai_inited
Definition: aicode.cpp:821
void process_subobjects(int objnum)
Definition: aicode.cpp:11120
if(aifft_max_checks<=0)
Definition: aiturret.cpp:1581
int scan_for_enemy_timestamp
Definition: ai.h:511
void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
Definition: aicode.cpp:15554
void afterburners_start(object *objp)
Definition: afterburner.cpp:68
int path_index
Definition: ai.h:319
float circle_strafe_percent[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:114
int create_object_hash(object *objp)
Definition: aicode.cpp:493
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
#define SM_ATTACK_FOREVER
Definition: ai.h:257
float damage_this_burst
Definition: player.h:146
GLuint GLuint num
Definition: Glext.h:9089
#define AIPF_SMART_PRIMARY_WEAPON_SELECTION
Definition: ai_profiles.h:23
void turn_towards_point(object *objp, vec3d *point, vec3d *slide_vec, float bank_override)
Definition: aicode.cpp:1491
void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1)
Definition: aicode.cpp:2800
int hitter_signature
Definition: ai.h:424
float in_range_time[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:90
#define AIS_UNDOCK_0
Definition: ai.h:275
int path_num
Definition: ai.h:318
float get_wing_lowest_max_speed(object *objp)
Definition: aicode.cpp:1719
void parse_ai_class()
Definition: aicode.cpp:596
float best_dot_from_enemy
Definition: ai.h:397
int avoid_ship_num
Definition: ai.h:530
int ship_launch_countermeasure(object *objp, int rand_val)
Definition: ship.cpp:10421
int Num_ai_classes
Definition: aicode.cpp:195
polymodel_instance * model_get_instance(int model_instance_num)
Definition: modelread.cpp:3154
#define REPAIR_INFO_ABORT
Definition: multi.h:306
float ai_shockwave_evade_chance[NUM_SKILL_LEVELS]
Definition: ai.h:237
int warp_out_timestamp
Definition: ai.h:506
#define AIS_WARP_3
Definition: ai.h:306
int goal_signature
Definition: ai.h:356
#define AIS_WARP_5
Definition: ai.h:308
float Wing_y_scale
Definition: aicode.cpp:11393
#define MAX_AI_INFO
Definition: ai.h:564
vec3d stealth_last_pos
Definition: ai.h:346
void ai_set_evade_object(object *objp, object *other_objp)
Definition: aicode.cpp:7505
vec3d * vm_vec_normal(vec3d *dest, const vec3d *p0, const vec3d *p1, const vec3d *p2)
Definition: vecmat.cpp:626
void render_all_ship_bay_paths(object *objp)
Definition: aicode.cpp:6145
void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
Definition: aicode.cpp:1923
float vm_vec_dist_quick(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:417
int will_collide_pp(vec3d *p0, vec3d *p1, float radius, object *big_objp, vec3d *collision_point)
Definition: aicode.cpp:6792
#define NAME_LENGTH
Definition: globals.h:15
float frand()
Return random value in range 0.0..1.0- (1.0- means the closest number less than 1.0)
Definition: floating.cpp:35
int artillery_objnum
Definition: ai.h:542
int hash(unsigned int curval, int newval)
Definition: aicode.cpp:477
#define SF_SECONDARY_DUAL_FIRE
Definition: ship.h:459
#define AIGF_DOCK_INDEXES_VALID
Definition: ai.h:114
#define AIS_GUARD_ATTACK
Definition: ai.h:283
void ai_safety_pick_spot(object *objp)
Definition: aicode.cpp:4147
#define WP_MISSILE
Definition: weapon.h:28
int num_allies_rearming(object *objp)
Definition: aicode.cpp:13155
void ai_most_massive_object_of_its_wing_of_all_docked_objects_helper(object *objp, dock_function_info *infop)
Definition: aicode.cpp:11609
#define AI_GOAL_DISABLE_SHIP
Definition: aigoals.h:39
void ai_chase_circle(object *objp)
Definition: aicode.cpp:12186
float ai_safety_goto_spot(object *objp)
Definition: aicode.cpp:4172
float lssm_lock_range
Definition: weapon.h:492
float ai_path_0()
Definition: aicode.cpp:3780
void ai_object_init(object *obj, int ai_index)
Definition: aicode.cpp:1330
float ai_ship_fire_delay_scale_hostile
Definition: ai.h:450
void vm_matrix_interpolate(const matrix *goal_orient, const matrix *curr_orient, const vec3d *w_in, float delta_t, matrix *next_orient, vec3d *w_out, const vec3d *vel_limit, const vec3d *acc_limit, int no_overshoot)
Definition: vecmat.cpp:1859
float ai_secondary_range_mult[NUM_SKILL_LEVELS]
Definition: ai.h:239
Definition: pstypes.h:106
void vm_vec_boxscale(vec2d *vec, float scale)
Definition: vecmat.cpp:2572
void mission_bring_in_support_ship(object *requester_objp)
#define SUBSYSTEM_SENSORS
Definition: model.h:59
control_info AI_ci
Definition: aicode.cpp:167
#define SM_STEALTH_FIND
Definition: ai.h:258
void ai_ship_destroy(int shipnum, int method)
Definition: aicode.cpp:15234
#define SUBSYSTEM_UNKNOWN
Definition: model.h:63
int submodel
Definition: lua.cpp:5007
void ai_chase_big_parallel_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
Definition: aicode.cpp:7874
#define STEALTH_IN_FRUSTUM
Definition: aicode.cpp:907
float ai_courage[NUM_SKILL_LEVELS]
Definition: ai.h:202
int behavior
Definition: ai.h:335
void ai_chase_ga(ai_info *aip, ship_info *sip)
Definition: aicode.cpp:7319
float ship_fire_secondary_delay_scale_hostile[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:127
int enemy_wing
Definition: ai.h:352
player * Player
void ai_execute_behavior(ai_info *aip)
Definition: aicode.cpp:13029
#define MESSAGE_TIME_ANYTIME
int max_fire_count
Definition: aicode.cpp:273
#define MC_CHECK_MODEL
Definition: model.h:1169
void message_send_builtin_to_player(int type, ship *shipp, int priority, int timing, int group, int delay, int multi_target, int multi_team_filter)
GLenum target
Definition: Glext.h:6872
float link_ammo_levels_always[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:98
void ai_safety()
Definition: aicode.cpp:4210
void avoid_ship()
Definition: aicode.cpp:4686
struct dock_function_info::@258 parameter_variables
float glide_attack_percent[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:113
struct dock_function_info::@258 maintained_variables
#define OBJ_SHOCKWAVE
Definition: object.h:41
void ship_maybe_tell_about_rearm(ship *sp)
Definition: ship.cpp:15913
#define WIF_BEAM
Definition: weapon.h:76
void joy_ff_docked()
Definition: joy-unix.cpp:595
float heading
Definition: physics.h:102
#define AIM_PATH
Definition: ai.h:177
void ai_mission_wing_goal_complete(int wingnum, ai_goal *remove_goalp)
Definition: aigoals.cpp:300
void init_ai_object(int objnum)
Definition: aicode.cpp:14300
int ship_do_rearm_frame(object *objp, float frametime)
Definition: ship.cpp:13664
ship_subsys * ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos)
Definition: ship.cpp:14963
int asteroid_collide_objnum(object *asteroid_objp)
Definition: asteroid.cpp:1781
void ai_cleanup_dock_mode_subjective(object *objp)
Definition: aicode.cpp:10417
int find_enemy(int objnum, float range, int max_attackers)
Definition: aicode.cpp:2186
object * objp_value
Definition: objectdock.h:38
int guard_signature
Definition: ai.h:359
int ai_acquire_depart_path(object *pl_objp, int parent_objnum, int allowed_path_mask)
Definition: aicode.cpp:12883
int ai_abort_rearm_request(object *requester_objp)
Definition: aicode.cpp:15338
void ai_deathroll_start(object *dying_objp)
Definition: aicode.cpp:15299
void ai_evade()
Definition: aicode.cpp:5205
float vm_vec_copy_normalize(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:427
int choose_enemy_timestamp
Definition: ai.h:513
fix predict_position_delay[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:89
void modify_model_path_points(object *objp)
Definition: aicode.cpp:3533
ship_obj Ship_obj_list
Definition: ship.cpp:162
int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vec3d *min_vec, vec3d *max_vec)
Definition: aicode.cpp:4115
void dock_evaluate_all_docked_objects(object *objp, dock_function_info *infop, void(*function)(object *, dock_function_info *))
Definition: objectdock.cpp:285
#define fl_sqrt(fl)
Definition: floating.h:29
void ship_secondary_changed(ship *sp)
Definition: ship.cpp:16053
#define AIM_EVADE_WEAPON
Definition: ai.h:180
int ship_info_index
Definition: ship.h:539
#define MAX_ENEMY_DISTANCE
Definition: ai.h:192
void ai_chase_es(ai_info *aip, ship_info *sip)
Definition: aicode.cpp:7285
GLfloat GLfloat p
Definition: Glext.h:8373
int collide
Definition: aicode.cpp:6883
#define MULTIPLAYER_MASTER
Definition: multi.h:130
#define AIPF2_ALLOW_PRIMARY_LINK_AT_START
Definition: ai_profiles.h:61
float vertical
Definition: physics.h:101
#define F_NAME
Definition: parselo.h:34
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
Definition: ai.h:198
GLenum src
Definition: Glext.h:5917
int ai_profile_flags
Definition: ai.h:230
void adjust_accel_for_docking(ai_info *aip)
Definition: aicode.cpp:1346
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define LOCATION
Definition: pstypes.h:245
#define SIF_TRANSPORT
Definition: ship.h:890
float cmeasure_fire_chance[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:104
float shield_factor
Definition: weapon.h:378
vec3d * vm_vec_avg(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:217
void render_wing_phantoms_all()
Definition: aicode.cpp:11490
vec3d afterburner_max_vel
Definition: ship.h:1281
float compute_collision_time(vec3d *targpos, vec3d *targvel, vec3d *attackpos, float weapon_speed)
Definition: aicode.cpp:3361
#define SIF_AWACS
Definition: ship.h:910
#define WIF_SPAWN
Definition: weapon.h:47
int bay_doors_parent_shipnum
Definition: ship.h:782
void ai_do_default_behavior(object *obj)
Definition: aicode.cpp:14564
#define SF2_AFTERBURNER_LOCKED
Definition: ship.h:499
#define AIF_NO_DYNAMIC
Definition: ai.h:56
#define SIF_FIGHTER
Definition: ship.h:885
void ai_fly_in_formation(int wingnum)
Definition: aicode.cpp:11532
int num_turrets_attacking(object *turret_parent, int target_objnum)
Definition: aicode.cpp:2137
float burst_delay
Definition: weapon.h:533
void ship_apply_local_damage(object *ship_objp, object *other_obj, vec3d *hitpos, float damage, int quadrant, bool create_spark, int submodel_num, vec3d *hit_normal)
Definition: shiphit.cpp:2359
fix last_warning_message_time
Definition: player.h:132
void ai_do_objects_docked_stuff(object *docker, int docker_point, object *dockee, int dockee_point, bool update_clients)
Definition: aicode.cpp:3008
int static_rand(int num)
Return a pseudo random 32 bit value given a reasonably small number.
Definition: staticrand.cpp:38
int nearest_locked_object
Definition: ai.h:486
#define WIF_HOMING
Definition: weapon.h:129
#define timestamp_elapsed(stamp)
Definition: timer.h:102
int Ai_last_arrive_path
Definition: aicode.cpp:279
SCP_vector< float > shield_quadrant
Definition: object.h:159
int static_rand_range(int num, int min, int max)
Return a random integer within a range. Note: min and max are inclusive.
Definition: staticrand.cpp:78
int vm_vec_cmp(const vec3d *a, const vec3d *b)
Definition: vecmat.cpp:1415
float Player_rearm_eta
Definition: hud.cpp:179
void ai_bay_emerge()
Definition: aicode.cpp:12748
float ai_ship_fire_secondary_delay_scale_hostile[NUM_SKILL_LEVELS]
Definition: ai.h:219
int path_goal_obj_hash
Definition: ai.h:384
#define OF_SHOULD_BE_DEAD
Definition: object.h:106
union ai_goal::@0 docker
void debug_find_guard_object()
Definition: aicode.cpp:9326
#define AIF_REPAIR_OBSTRUCTED
Definition: ai.h:54
void reset_ai_class_names()
Definition: aicode.cpp:772
int LockAPConv
Definition: autopilot.cpp:50
void get_absolute_wing_pos_autopilot(vec3d *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
Definition: aicode.cpp:11432
int check_danger_weapon_objnum
Definition: aicode.cpp:1919
GLenum GLenum GLvoid GLvoid * column
Definition: Glext.h:5241
#define WIF_PUNCTURE
Definition: weapon.h:49
#define MAX_FORMATION_ROWS
Definition: aicode.cpp:11294
char docker[NAME_LENGTH]
void shipfx_warpout_start(object *objp)
Definition: shipfx.cpp:794
#define MAX_WEAPONS
Definition: globals.h:71
hull_check pos
Definition: lua.cpp:5050
float Wing_scale
Definition: aicode.cpp:11394
#define MISSION_FLAG_NO_BUILTIN_COMMAND
Definition: missionparse.h:86
int Game_skill_level
Definition: fredstubs.cpp:170
int ship_is_visible_by_team(object *target, ship *viewer)
Definition: awacs.cpp:442
GLsizei GLsizei GLuint * obj
Definition: Glext.h:5619
int model_instance_num
Definition: model.h:1109
float ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:123
float ai_shield_manage_delay
Definition: ai.h:448
int Iff_traitor
Definition: iff_defs.cpp:22
void ai_frame(int objnum)
Definition: aicode.cpp:13970
int n_guns
Definition: model.h:768
#define AIS_CHASE_CIRCLESTRAFE
Definition: ai.h:266
#define MAX_EVADE_TIME
Definition: aicode.cpp:179
float vm_vec_dot(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:312
vec3d pos
Definition: model.h:408
char * Submode_text[]
Definition: aicode.cpp:133
int num_spline_paths
Definition: model.h:527
GLenum GLuint GLenum GLsizei length
Definition: Glext.h:5156
#define AICODE_SMALL_MAGNITUDE
Definition: aicode.cpp:83
#define SM_EVADE
Definition: ai.h:250
#define SIF_NOT_FLYABLE
Definition: ship.h:946
int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
Definition: aicode.cpp:966
int wing
Definition: ai.h:333
void ai_process_mission_orders(int objnum, ai_info *aip)
Definition: aigoals.cpp:1977
bool is_object_stealth_ship(object *objp)
Definition: aicode.cpp:879
float stay_near_distance
Definition: ai.h:469
int ignore_new_signatures[MAX_IGNORE_NEW_OBJECTS]
Definition: ai.h:367
void vm_vec_scale_sub2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:293
void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
Definition: aicode.cpp:4872
float sideways
Definition: physics.h:103
int ai_maybe_limit_attackers(int attacked_objnum)
Definition: aicode.cpp:9449
float ai_shield_manage_delay[NUM_SKILL_LEVELS]
Definition: ai.h:215
void ai_manage_bay_doors(object *pl_objp, ai_info *aip, bool done)
Definition: aicode.cpp:12581
#define AIS_UNDOCK_1
Definition: ai.h:276
int guard_objnum
Definition: ai.h:358
GLint GLsizei count
Definition: Gl.h:1491
#define SM_STEALTH_SWEEP
Definition: ai.h:259
void ai_big_subsys_path_cleanup(ai_info *aip)
Definition: aibig.cpp:351
SCP_vector< int > ai_actively_pursues
Definition: ship.h:1040
GLsizei GLsizei GLchar * source
Definition: Glext.h:5625
int get_wing_index(object *objp, int wingnum)
Definition: aicode.cpp:11230
#define SUBSYSTEM_SOLAR
Definition: model.h:60
mp_vert * verts
Definition: model.h:419
#define AIS_CHASE_GLIDEATTACK
Definition: ai.h:265
void get_tangent_point(vec3d *tan1, vec3d *p0, vec3d *centerp, vec3d *p1, float radius)
Definition: aicode.cpp:1457
int support_ship_signature
Definition: ai.h:480
matrix * A
Definition: lua.cpp:444
#define i2f(a)
Definition: fix.h:23
int game_get_default_skill_level()
Definition: fredstubs.cpp:190
#define AIF_AVOID_SHOCKWAVE
Definition: ai.h:69
float dock_orient_and_approach(object *docker_objp, int docker_index, object *dockee_objp, int dockee_index, int dock_mode, rotating_dockpoint_info *rdinfo)
Definition: aicode.cpp:9070
#define TRIGGER_TYPE_TURRET_FIRING
Definition: modelanim.h:36
float shield_manage_delay[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:91
object * Player_obj
Definition: object.cpp:56
#define AIF_FORMATION_WING
Definition: ai.h:38
int might_hit_teammate(object *firing_objp)
Definition: aicode.cpp:6120
float ai_ship_fire_secondary_delay_scale_friendly[NUM_SKILL_LEVELS]
Definition: ai.h:218
void player_stop_repair_sound()
#define SUBSYSTEM_ACTIVATION
Definition: model.h:62
float get_cylinder_points(object *other_objp, object *cyl_objp, vec3d *axis_pt, vec3d *r_vec, float *radius)
Definition: aicode.cpp:9771
#define SF_DEPART_DOCKBAY
Definition: ship.h:450
void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
Definition: aicode.cpp:15575
#define WPF_REPEAT
Definition: ai.h:295
#define AIM_AVOID
Definition: ai.h:172
#define WIF_HOMING_JAVELIN
Definition: weapon.h:55
float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag, int override_hash=0)
Definition: aicode.cpp:3579
Definition: ai.h:134
uint flags2
Definition: ship.h:645
int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
Definition: aicode.cpp:4825
float ai_stalemate_dist_thresh[NUM_SKILL_LEVELS]
Definition: ai.h:226
#define AI_GOAL_REARM_REPAIR
Definition: aigoals.h:49
int maybe_hack_cruiser_chase_abort()
Definition: aicode.cpp:7965
vec3d prev_ramp_vel
Definition: physics.h:69
#define SF_DYING
Definition: ship.h:447
int temp
Definition: lua.cpp:4996
#define PF_AFTERBURNER_ON
Definition: physics.h:20
#define SM_EVADE_BRAKE
Definition: ai.h:249
int goal_objnum
Definition: ai.h:355
int ship_index[MAX_SHIPS_PER_WING]
Definition: ship.h:1531
int is_ignore_object_sub(int *ignore_objnum, int *ignore_signature, int objnum)
Definition: aicode.cpp:1843
#define WIF2_BALLISTIC
Definition: weapon.h:84
polymodel * pm
Definition: lua.cpp:1598
char name[NAME_LENGTH]
Definition: ai.h:199
#define AIS_DOCK_1
Definition: ai.h:270
void ai_still()
Definition: aicode.cpp:10518
vec3d big_recover_pos_1
Definition: ai.h:535
#define SM_BIG_CIRCLE
Definition: ai.h:261
void ai_fly_to_target_position(vec3d *target_pos, bool *pl_done_p=NULL, bool *pl_treat_as_ship_p=NULL)
Definition: aicode.cpp:4247
#define MAX(a, b)
Definition: pstypes.h:299
int Player_attacking_enabled
Definition: aicode.cpp:1638
#define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD
Definition: aicode.cpp:13501
int Num_iffs
Definition: iff_defs.cpp:19
void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
Definition: aicode.cpp:9663
int iff_matches_mask(int team, int mask)
Definition: iff_defs.cpp:628
void turn_away_from_point(object *objp, vec3d *point, float bank_override)
Definition: aicode.cpp:1513
void evade_weapon()
Definition: aicode.cpp:4904
#define LOG_WAYPOINTS_DONE
Definition: missionlog.h:36
int primary_bank_start_ammo[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:122
#define AIF_STEALTH_PURSUIT
Definition: ai.h:61
#define REPAIR_INFO_BEGIN
Definition: multi.h:302
int pp_collide_any(vec3d *curpos, vec3d *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
Definition: aicode.cpp:2842
float mass
Definition: physics.h:39
#define OBJ_BEAM
Definition: object.h:46
float ai_shockwave_evade_chance
Definition: ai.h:434
#define AIGF_TARGET_OWN_TEAM
Definition: ai.h:112
int ai_big_maybe_enter_strafe_mode(object *pl_objp, int weapon_objnum, int consider_target_only)
Definition: aibig.cpp:1704
int ship_name_lookup(const char *name, int inc_players)
Definition: ship.cpp:12900
int ai_await_repair_frame(object *objp, ai_info *aip)
Definition: aicode.cpp:13765
int ai_aburn_use_factor[NUM_SKILL_LEVELS]
Definition: ai.h:236
float vel_inherit_amount
Definition: weapon.h:356
vec3d * vm_vec_cross(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:645
int ignore_objnum
Definition: ai.h:362
#define GM_NORMAL
Definition: systemvars.h:19
int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
Definition: aicode.cpp:10584
#define AIPF_SMART_SHIELD_MANAGEMENT
Definition: ai_profiles.h:21
void ai_chase_big_circle_set_goal(vec3d *goal_pos, object *attack_objp, object *target_objp, float *accel)
Definition: aicode.cpp:7831
float random_sidethrust_percent[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:116
void call_doa(object *child, object *parent)
Definition: aicode.cpp:12072
#define AIPF2_AI_GUARDS_SPECIFIC_SHIP_IN_WING
Definition: ai_profiles.h:66
char * shipname
Definition: aicode.cpp:274
#define SF2_STEALTH
Definition: ship.h:485
uint flags
Definition: object.h:151
void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hitpos, int shield_quadrant, vec3d *hit_normal)
Definition: aicode.cpp:14934
float radius
Definition: object.h:154
float ai_secondary_range_mult
Definition: ai.h:436
model_path * paths
Definition: model.h:784
void big_ship_collide_recover_start(object *objp, object *big_objp, vec3d *collide_pos, vec3d *collision_normal)
Definition: aicode.cpp:14843
void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
Definition: aicode.cpp:9704
bool bay_doors_need_open
Definition: ship.h:781
int object_is_targetable(object *target, ship *viewer)
Definition: aicode.cpp:1643
void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
Definition: aicode.cpp:14782
mission The_mission
missile_obj Missile_obj_list
Definition: weapons.cpp:84
vec3d last_aim_enemy_pos
Definition: ai.h:409
matrix last_orient
Definition: object.h:156
#define AIF_BIG_SHIP_COLLIDE_RECOVER_2
Definition: ai.h:60
GLint level
Definition: Glext.h:5180
#define SF_TRIGGER_DOWN
Definition: ship.h:462
struct ai_render_stuff ai_render_stuff
#define WIF2_LOCAL_SSM
Definition: weapon.h:87
int turret_enemy_objnum
Definition: ship.h:337
int model_instance_num
Definition: lua.cpp:4996
int submodel_num
Definition: model.h:1111
float dock_calc_total_docked_mass(object *objp)
Definition: objectdock.cpp:152
#define AIF_AVOID_SHOCKWAVE_SHIP
Definition: ai.h:50
char type
Definition: object.h:146
ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF]
Definition: aicode.cpp:11077
#define AIS_DOCK_2
Definition: ai.h:271
#define AIM_BIGSHIP
Definition: ai.h:176
#define REARM_SOUND_DELAY
Definition: aicode.cpp:172
vec3d max_vel
Definition: ship.h:1196
fix ai_predict_position_delay[NUM_SKILL_LEVELS]
Definition: ai.h:214
shockwave_create_info shockwave
Definition: weapon.h:367
float damage
Definition: weapon.h:363
int ignore_signature
Definition: ai.h:363
int rearm_release_delay
Definition: ai.h:501
#define DOA_UNDOCK_3
Definition: ai.h:77
int hitter_objnum
Definition: ai.h:423
#define WIF3_FIGHTER_INTERCEPTABLE
Definition: weapon.h:123
#define STI_AI_ATTEMPT_BROADSIDE
Definition: ship.h:1006
int movement_type
Definition: model.h:326
#define REPAIR_INFO_BROKEN
Definition: multi.h:307
int firing_aspect_seeking_bomb(object *objp)
Definition: aicode.cpp:14813
#define FALSE
Definition: pstypes.h:400
#define SF_FROM_PLAYER_WING
Definition: ship.h:457
float find_nearest_point_on_line(vec3d *nearest_point, const vec3d *p0, const vec3d *p1, const vec3d *int_pnt)
Definition: vecmat.cpp:1209
const char * Skill_level_names(int level, int translate)
Definition: aicode.cpp:208
int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
Definition: aicode.cpp:6050
float afterburner_recover_rate
Definition: ship.h:1285
float ai_patience
Definition: ai.h:432
int multi_find_player_by_object(object *objp)
Definition: multiutil.cpp:500
char filename[FILESPEC_LENGTH]
Definition: model.h:734
#define wp(p)
Definition: modelsinc.h:69
#define LOCAL
Definition: pstypes.h:37
#define MISSION_FLAG_NO_TRAITOR
Definition: missionparse.h:72
#define AIM_GET_BEHIND
Definition: ai.h:168
void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
Definition: aicode.cpp:2706
#define AIS_STRAFE_ATTACK
Definition: ai.h:288
#define AIM_PLAY_DEAD
Definition: ai.h:182
void render_wing_phantoms(object *objp)
Definition: aicode.cpp:11448
net_player Net_players[MAX_PLAYERS]
Definition: multi.cpp:93
float armor_factor
Definition: weapon.h:378
int index
Definition: ai.h:154
void gameseq_post_event(int event)
int num_enemies_attacking(int objnum)
Definition: aicode.cpp:1676
void ai_manage_shield(object *objp, ai_info *aip)
Definition: aicode.cpp:12312
void set_predicted_enemy_pos(vec3d *predicted_enemy_pos, object *pobjp, vec3d *enemy_pos, vec3d *enemy_vel, ai_info *aip)
Definition: aicode.cpp:6397
vec3d pnt[MAX_DOCK_SLOTS]
Definition: model.h:530
void ai_stealth_find()
Definition: aicode.cpp:7010
matrix * orient
Definition: model.h:1112
int burst_counter[MAX_SHIP_PRIMARY_BANKS+MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:156
float ship_calculate_rearm_duration(object *objp)
Definition: ship.cpp:13537
#define stricmp(s1, s2)
Definition: config.h:271
#define AIDO_DOCK
Definition: ai.h:81
void vm_vec_add(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:159
const GLdouble * v
Definition: Glext.h:5322
float ship_get_exp_damage(object *objp)
Definition: ship.cpp:16949
float ai_link_energy_levels_maybe[NUM_SKILL_LEVELS]
Definition: ai.h:212
void attack_set_accel(ai_info *aip, ship_info *sip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
Definition: aicode.cpp:6637
int get_enemy_timestamp()
Definition: aicode.cpp:2174
#define OBJ_GHOST
Definition: object.h:39
void ship_do_submodel_rotation(ship *shipp, model_subsystem *psub, ship_subsys *pss)
Definition: ship.cpp:17553
void ai_init_secondary_info()
Definition: aicode.cpp:388
#define AIS_UNDOCK_3
Definition: ai.h:278
void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
Definition: aicode.cpp:12086
#define timestamp_valid(stamp)
Definition: timer.h:104
float stalemate_dist_thresh[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:118
float link_energy_levels_maybe[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:95
float Leader_chaos
Definition: aicode.cpp:11575
int cmeasure_count
Definition: ship.h:651
void ai_fly_to_ship()
Definition: aicode.cpp:4607
vec3d path_depart_orient
Definition: ai.h:392
void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vec3d *horz_vec_to_target, float *desired_separation, float *cur_separation)
Definition: aicode.cpp:7841
float AI_frametime
Definition: aicode.cpp:266
int flags
Definition: ai.h:139
NavPoint Navs[MAX_NAVPOINTS]
Definition: autopilot.cpp:45
int is_instructor(object *objp)
Definition: aicode.cpp:4895
float static_randf(int num)
Return a random float in 0.0f .. 1.0f- (ie, it will never return 1.0f).
Definition: staticrand.cpp:61
#define AIM_STILL
Definition: ai.h:170
ai_class * Ai_classes
Definition: aicode.cpp:193
int parent_sig
Definition: object.h:148
float ai_primary_ammo_burst_mult
Definition: ai.h:444
#define AIF_TARGET_COLLISION
Definition: ai.h:47
#define AIF_AVOID_SHOCKWAVE_STARTED
Definition: ai.h:52
char ship_name[NAME_LENGTH]
Definition: ship.h:604
#define AIM_FLY_TO_SHIP
Definition: ai.h:187
#define AIS_GUARD_STATIC
Definition: ai.h:285
void maybe_evade_dumbfire_weapon(ai_info *aip)
Definition: aicode.cpp:12456
#define LOG_PLAYER_ABORTED_REARM
Definition: missionlog.h:35
vec3d goal_point
Definition: ai.h:428
void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
Definition: aicode.cpp:9590
float afterburner_burn_rate
Definition: ship.h:1284
#define SF2_NO_SECONDARY_LOCKON
Definition: ship.h:510
vec3d docker_point
Definition: ai.h:555
float ai_courage
Definition: ai.h:432
char docker_point[NAME_LENGTH]
dock depart retros
Definition: gamesnd.h:88
void create_path_to_point(vec3d *curpos, vec3d *goalpos, object *curobjp, object *goalobjp, int subsys_path)
Definition: aicode.cpp:2571
void physics_ship_init(object *objp)
Definition: ship.cpp:5366
int myrand()
Definition: systemvars.cpp:102
float static_randf_range(int num, float min, float max)
Return a random float within a range. Note: min and max are inclusive.
Definition: staticrand.cpp:95
SCP_vector< ship_type_info > Ship_types
Definition: ship.cpp:168
#define IFFF_SUPPORT_ALLOWED
Definition: iff_defs.h:25
float ai_link_energy_levels_maybe
Definition: ai.h:445
void vm_forward_interpolate(const vec3d *goal_f, const matrix *orient, const vec3d *w_in, float delta_t, float delta_bank, matrix *next_orient, vec3d *w_out, const vec3d *vel_limit, const vec3d *acc_limit, int no_overshoot)
Definition: vecmat.cpp:2061
#define AIM_STRAFE
Definition: ai.h:181
void ai_do_objects_repairing_stuff(object *repaired_objp, object *repair_objp, int how)
Definition: aicode.cpp:10186
matrix path_create_orient
Definition: ai.h:387
#define fl2f(fl)
Definition: floating.h:38
object * Autopilot_flight_leader
Definition: aicode.cpp:291
int Last_ai_obj
Definition: aicode.cpp:14230
#define MISSION_FLAG_SUBSPACE
Definition: missionparse.h:68
int burst_shots
Definition: weapon.h:532
char name[MAX_NAME_LEN]
Definition: model.h:171
#define MAX_BURST_DAMAGE
Definition: ai.h:572
#define SIF_CORVETTE
Definition: ship.h:908
void init_ai_class(ai_class *aicp)
Definition: aicode.cpp:529
#define OBJ_RECALC_PAIRS(obj_to_reset)
Definition: object.h:274
int formation_is_leader_chaotic(object *objp)
Definition: aicode.cpp:11580
int num_slots
Definition: model.h:525
void ai_set_mode_warp_out(object *objp, ai_info *aip)
Definition: aicode.cpp:13301
int current_target_is_locked
Definition: ai.h:490
float aspect_locked_time
Definition: ai.h:476
#define MESSAGE_OOPS
void update_ai_stealth_info_with_error(ai_info *aip)
Definition: aicode.cpp:1039
int path_indexes[MAX_SHIP_BAY_PATHS]
Definition: model.h:541
int flags
Definition: ship.h:1556
#define MESSAGE_TIME_SOON
float forward_thrust
Definition: physics.h:72
scoring_struct stats
Definition: player.h:127
#define AI_UPDATE_UNDOCK
Definition: multi.h:343
float vm_vec_normalize(vec3d *v)
Definition: vecmat.cpp:460
int previous_target_objnum
Definition: ai.h:341
vec3d pos
Definition: aicode.cpp:6884
int Player_num
#define SM_SF_BAIL
Definition: aicode.cpp:93
void compute_desired_rvec(vec3d *rvec, vec3d *goal_pos, vec3d *cur_pos)
Definition: aicode.cpp:6995
void ai_guard_find_nearby_object()
Definition: aicode.cpp:9744
Definition: ai.h:316
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)
Definition: aicode.cpp:3643
void ai_remove_ship_goal(ai_info *aip, int index)
Definition: aigoals.cpp:231
int Num_AI_debug_render_stuff
Definition: aicode.cpp:11079
int last_hit_quadrant
Definition: ai.h:421
void ai_set_rearm_status(int team, int time)
Definition: aicode.cpp:300