2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
15 * AI code that does interesting stuff
18 * Revision 1.11 2006/04/26 19:45:22 taylor
19 * fix a FS1 docked speed issue, should keep it in line with the original now
21 * Revision 1.10 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.9 2004/09/20 01:31:44 theoddone33
31 * Revision 1.8 2003/08/03 16:10:30 taylor
32 * cleanup; compile warning fixes
34 * Revision 1.7 2003/05/25 02:30:43 taylor
37 * Revision 1.6 2002/07/13 19:47:02 theoddone33
38 * Fix some more warnings
40 * Change demo building, edit Makefile if you want the demo.
42 * Revision 1.5 2002/06/17 06:33:10 relnev
43 * ryan's struct patch for gcc 2.95
45 * Revision 1.4 2002/06/09 04:41:26 relnev
46 * added copyright header
48 * Revision 1.3 2002/06/01 07:12:34 relnev
49 * a few NDEBUG updates.
51 * removed a few warnings.
53 * Revision 1.2 2002/05/03 13:34:33 theoddone33
56 * Revision 1.1.1.1 2002/05/03 03:28:10 root
60 * 107 9/15/99 4:42a Mikek
61 * Make any big ship attacking Colossus, or Colossus attacking any large
62 * ship not use big cruiser movement code.
64 * 106 9/15/99 3:28a Jimb
65 * Make all big ships in sm3-08 not do cruiser chase code when attacking
66 * Colossus. Added so Beast doesn't swerve away from Colossus.
68 * 105 9/14/99 4:18p Andsager
69 * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
70 * begin circling colossus.
72 * 104 9/08/99 10:44p Andsager
73 * Make HUGE ships not die when warping out, after warp effect started.
75 * 103 9/03/99 11:40p Mikek
76 * Comment out an annoying nprintf().
78 * 102 9/01/99 11:26p Dave
79 * Fixed release build warnings.
81 * 101 9/01/99 9:12p Mikek
82 * Make it a boatload harder to become a traitor from hitting a large
85 * 100 9/01/99 4:01p Andsager
86 * Make sure BIG|HUGE ships do not respond to shockwaves
88 * 99 9/01/99 10:09a Dave
91 * 98 8/31/99 4:24p Andsager
92 * Reduce collisions when attacking big ships.
94 * 97 8/31/99 7:33a Mikek
95 * Improvements in formation flying, less silly behavior, especially when
96 * leader is moving very slowly.
98 * 96 8/31/99 5:48a Mikek
99 * Making ships not overshoot so much in formation flying. Intermediate
102 * 95 8/30/99 12:03a Mikek
103 * Make guard behavior much less annoying. Guarders don't get quite so
104 * close and they try to avoid striking the target they are guarding.
106 * 94 8/29/99 4:18p Andsager
107 * New "burst" limit for friendly damage. Also credit more damage done
108 * against large friendly ships.
110 * 93 8/28/99 7:29p Dave
111 * Fixed wingmen persona messaging. Make sure locked turrets don't count
112 * towards the # attacking a player.
114 * 92 8/26/99 10:46p Andsager
115 * Apply shockwave damage to lethality.
117 * 91 8/26/99 8:52p Dave
118 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
120 * 90 8/26/99 5:14p Andsager
122 * 89 8/24/99 8:55p Dave
123 * Make sure nondimming pixels work properly in tech menu.
125 * 88 8/23/99 6:21p Jefff
126 * added "no traitor" option to missions (and fred)
128 * 87 8/20/99 3:36p Andsager
129 * Make sure we don;t miss stealth sweep points.
131 * 86 8/16/99 8:21a Andsager
134 * 85 8/16/99 8:19a Andsager
135 * Add project_point_onto_bbox() to fvi and include in aicode
137 * 84 8/15/99 1:30p Dave
138 * Removed some bounding box code because of link errors. Assuming needed
139 * function just needs to get checked in by DaveA.
141 * 83 8/15/99 11:59a Andsager
142 * For targing big/huge ships, find nearest distance to bbox, not center.
144 * 82 8/13/99 2:20p Andsager
145 * Add speed modification to chances turret will find stealth ship
147 * 81 8/13/99 10:49a Andsager
148 * Knossos and HUGE ship warp out. HUGE ship warp in. Stealth search
149 * modes dont collide big ships.
151 * 80 8/10/99 5:02p Andsager
152 * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
154 * 79 8/10/99 11:58a Andsager
155 * Allow turrets to sometimes see stealth.
157 * 78 7/31/99 2:57p Dave
158 * Scaled flak aim and jitter by weapon subsystem strength.
160 * 77 7/27/99 10:33p Andsager
161 * improve ai for attacking stealth. reduced jitter in aim. reduced
162 * error in position when avoiding. skill level support for attacking
163 * stealth. Made target error same for team vs. team.
165 * 76 7/27/99 10:49a Andsager
166 * Make turret fire rate independent of team for HUGE turrets, and also
167 * for mult team vs. team.
169 * 75 7/26/99 12:14p Andsager
170 * Apply cap to how much slower a transport flies with cargo. Remove
171 * limit on waypoint speed for training. Enemy ai get stealth exact pos
174 * 74 7/20/99 1:49p Dave
175 * Peter Drake build. Fixed some release build warnings.
177 * 73 7/19/99 2:13p Dave
178 * Added some new strings for Heiko.
180 * 72 7/19/99 12:02p Andsager
181 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
182 * only blow up subsystem if its strength is > 0
184 * 71 7/15/99 9:20a Andsager
185 * FS2_DEMO initial checkin
187 * 70 7/14/99 1:44p Andsager
188 * modify ai_guard for BIG ships to circle around the long axis
190 * 69 7/09/99 5:54p Dave
191 * Seperated cruiser types into individual types. Added tons of new
192 * briefing icons. Campaign screen.
194 * 68 7/08/99 4:32p Andsager
195 * fix bug with turret-tagged-only
197 * 67 7/08/99 12:06p Andsager
198 * Add turret-tagged-only and turret-tagged-clear sexp.
200 * 66 7/02/99 3:49p Andsager
201 * Remove debug code. Allow targeting of stealth from any weapon it
204 * 65 7/02/99 2:01p Andsager
205 * Fix bug where big ship tries to evade dumpfire weapon.
207 * 64 7/02/99 10:58a Andsager
208 * Put in big ship - big ship attack mode. Modify stealth sweep ai.
210 * 63 6/30/99 5:53p Dave
211 * Put in new anti-camper code.
213 * 62 6/28/99 3:22p Anoop
214 * Fix turret optimization, where ship may not have any valid subsystems
217 * 61 6/25/99 5:56p Andsager
218 * First real pass on stealth ai.
220 * 60 6/25/99 3:08p Dave
221 * Multiple flyby sounds.
223 * 59 6/25/99 1:12p Danw
224 * DKA: Make sure big ship has subsystems before trying to target them.
226 * 58 6/25/99 10:56a Johnson
227 * Fixed dumb ai code.
229 * 57 6/24/99 5:15p Dave
230 * Make sure stride is always at least one for checking turret subsystem
233 * 56 6/24/99 4:59p Dave
234 * Significant speedups to turret firing.
236 * 55 6/23/99 5:51p Andsager
237 * Add waypoint-cap-speed. Checkin stealth ai - inactive.
239 * 54 6/16/99 10:21a Dave
240 * Added send-message-list sexpression.
242 * 53 6/15/99 9:25a Andsager
243 * Make guard and dynamic chase (who hit you) work with stealth
245 * 52 6/14/99 3:21p Andsager
246 * Allow collisions between ship and its debris. Fix up collision pairs
247 * when large ship is warping out.
249 * 51 6/14/99 10:45a Dave
250 * Made beam weapons specify accuracy by skill level in the weapons.tbl
252 * 50 6/03/99 8:11a Andsager
254 * 49 6/02/99 5:41p Andsager
255 * Reduce range of secondary weapons not fired from turrets in nebula.
256 * Reduce range of beams fired from turrrets in nebula
258 * 48 6/02/99 3:23p Andsager
259 * Make AI aware of team visibility. Allow player targeting with team
260 * visibility info. Make stealth ships not targetable by AI in nebula
263 * 47 6/02/99 12:52p Andsager
264 * Added team-wide ship visibility. Implemented for player.
266 * 46 6/01/99 8:35p Dave
267 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
268 * awacs-set-radius sexpression.
270 * 45 5/28/99 5:35p Andsager
271 * Make ai nebula aware
273 * 44 5/24/99 9:55a Dave
274 * Fixed stream weapon ai firing problem. ick.
276 * 43 5/20/99 7:00p Dave
277 * Added alternate type names for ships. Changed swarm missile table
280 * 42 5/18/99 1:30p Dave
281 * Added muzzle flash table stuff.
283 * 41 5/12/99 2:55p Andsager
284 * Implemented level 2 tag as priority in turret object selection
286 * 40 5/12/99 10:42a Andsager
287 * Fix turret bug allowing HUGE turrets to fire at fighters
289 * 39 5/06/99 11:46a Andsager
290 * Bug fixes. Don't get into illegal strafe submode. Don't choose turret
291 * enemy objnum for beam protected.
293 * 38 5/03/99 10:50p Andsager
294 * Make Asteroid_obj_list. Change get_nearest_turret_objnum() to use
295 * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
298 * 37 4/29/99 2:29p Dave
299 * Made flak work much better in multiplayer.
301 * 36 4/28/99 11:36p Dave
302 * Tweaked up subspace missile strike a bit,
304 * 35 4/28/99 3:11p Andsager
305 * Stagger turret weapon fire times. Make turrets smarter when target is
306 * protected or beam protected. Add weaopn range to weapon info struct.
308 * 34 4/26/99 10:58a Andsager
309 * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
311 * 33 4/23/99 12:12p Andsager
312 * Modify wing positions when player is wing leader to prevent some
315 * 32 4/23/99 12:01p Johnson
316 * Added SIF_HUGE_SHIP
318 * 31 4/22/99 11:06p Dave
319 * Final pass at beam weapons. Solidified a lot of stuff. All that remains
320 * now is to tweak and fix bugs as they come up. No new beam weapon
323 * 30 4/20/99 6:39p Dave
324 * Almost done with artillery targeting. Added support for downloading
325 * images on the PXO screen.
327 * 29 4/20/99 3:40p Andsager
328 * Changes to big ship ai. Uses bounding box as limit where to fly to
331 * 28 4/16/99 5:54p Dave
332 * Support for on/off style "stream" weapons. Real early support for
333 * target-painting lasers.
335 * 27 4/02/99 9:55a Dave
336 * Added a few more options in the weapons.tbl for beam weapons. Attempt
337 * at putting "pain" packets into multiplayer.
339 * 26 3/28/99 5:58p Dave
340 * Added early demo code. Make objects move. Nice and framerate
341 * independant, but not much else. Don't use yet unless you're me :)
343 * 25 3/19/99 9:51a Dave
344 * Checkin to repair massive source safe crash. Also added support for
345 * pof-style nebulae, and some new weapons code.
347 * 24 3/08/99 7:03p Dave
348 * First run of new object update system. Looks very promising.
350 * 23 3/05/99 3:55p Anoop
351 * Handle some asserts properly.
353 * 22 3/04/99 6:09p Dave
354 * Added in sexpressions for firing beams and checking for if a ship is
357 * 21 3/02/99 9:25p Dave
358 * Added a bunch of model rendering debug code. Started work on fixing
359 * beam weapon wacky firing.
361 * 20 2/25/99 2:32p Anoop
362 * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
363 * check so that when the last point on the path is reached, it finishes.
365 * 19 2/19/99 2:11p Anoop
366 * Put in some nice handling code for wacky support ship problems (like no
369 * 18 2/17/99 2:11p Dave
370 * First full run of squad war. All freespace and tracker side stuff
373 * 17 2/11/99 5:22p Andsager
374 * Fixed bugs, generalized block Sexp_variables
376 * 16 1/29/99 5:07p Dave
377 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
380 * 15 1/29/99 2:25p Andsager
381 * Added turret_swarm_missiles
383 * 14 1/27/99 9:56a Dave
384 * Temporary checkin of beam weapons for Dan to make cool sounds.
386 * 13 1/24/99 11:37p Dave
387 * First full rev of beam weapons. Very customizable. Removed some bogus
388 * Int3()'s in low level net code.
390 * 12 1/21/99 10:44a Dave
391 * More beam weapon stuff. Put in warmdown time.
393 * 11 1/12/99 5:45p Dave
394 * Moved weapon pipeline in multiplayer to almost exclusively client side.
395 * Very good results. Bandwidth goes down, playability goes up for crappy
396 * connections. Fixed object update problem for ship subsystems.
398 * 10 1/08/99 2:08p Dave
399 * Fixed software rendering for pofview. Super early support for AWACS and
402 * 9 12/23/98 2:53p Andsager
403 * Added ship activation and gas collection subsystems, removed bridge
405 * 8 11/12/98 12:13a Dave
406 * Tidied code up for multiplayer test. Put in network support for flak
409 * 7 11/05/98 5:55p Dave
410 * Big pass at reducing #includes
412 * 6 10/26/98 9:42a Dave
413 * Early flak gun support.
415 * 5 10/23/98 3:51p Dave
416 * Full support for tstrings.tbl and foreign languages. All that remains
417 * is to make it active in Fred.
419 * 4 10/20/98 1:39p Andsager
420 * Make so sparks follow animated ship submodels. Modify
421 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
422 * submodel_num. Add submodel_num to multiplayer hit packet.
424 * 3 10/13/98 9:29a Dave
425 * Started neatening up freespace.h. Many variables renamed and
426 * reorganized. Added AlphaColors.[h,cpp]
428 * 2 10/07/98 10:53a Dave
431 * 1 10/07/98 10:51a Dave
437 // This module contains the actual AI code that does interesting stuff
438 // to objects. The code in Ai.cpp is just for bookeeping, allocating
439 // ai slots and linking them to ships.
443 #include "linklist.h"
452 #include "floating.h"
454 #include "freespace.h"
456 #include "missiongoals.h"
457 #include "missionlog.h"
462 #include "hudmessage.h"
463 #include "missionmessage.h"
464 #include "cmeasure.h"
465 #include "staticrand.h"
466 #include "multimsgs.h"
467 #include "afterburner.h"
472 #include "multiutil.h"
474 #include "objcollide.h"
475 #include "asteroid.h"
477 #include "missiontraining.h"
478 #include "gamesequence.h"
480 #include "localize.h"
485 #include "multi_team.h"
490 #pragma optimize("", off)
491 #pragma auto_inline(off)
494 #define UNINITIALIZED_VALUE -99999.9f
496 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
498 #define AICODE_SMALL_MAGNITUDE 0.001f // cosider a vector NULL if mag is less than this
500 #define NEXT_REARM_TIMESTAMP (60*1000) // Ships will re-request rearm, typically, after this long.
502 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR 0.8
504 // AIM_CHASE submode defines
506 #define SM_SF_AHEAD 0
507 #define SM_SF_BEHIND 1
511 #define SM_SS_SET_GOAL -1
523 const char *Mode_text[MAX_AI_BEHAVIORS] = {
547 // Submode text is only valid for CHASE mode.
548 const char *Submode_text[] = {
569 const char *Strafe_submode_text[5] = {
579 // Used for global ignore of objects. If an object appears in the Ignore_objects array,
580 // no one will attack it.
581 #define MAX_IGNORE_OBJECTS 16
587 ignore_object Ignore_objects[MAX_IGNORE_OBJECTS];
590 typedef struct eval_enemy_obj_struct {
591 int turret_parent_objnum; // parent of turret
592 float weapon_travel_dist; // max targeting range of turret weapon
594 int weapon_system_ok; // is the weapon subsystem of turret ship ok
595 int big_only_flag; // turret fires only at big and huge ships
598 ship_subsys *turret_subsys;
602 float nearest_attacker_dist; // nearest ship
603 int nearest_attacker_objnum;
605 float nearest_homing_bomb_dist; // nearest homing bomb
606 int nearest_homing_bomb_objnum;
608 float nearest_bomb_dist; // nearest non-homing bomb
609 int nearest_bomb_objnum;
611 float nearest_dist; // nearest ship attacking this turret
613 } eval_enemy_obj_struct;
621 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
623 // How close a turret has to be point at its target before it
624 // can fire. If the dot of the gun normal and the vector from gun
625 // to target is greater than this, the turret fires. The smaller
626 // the sloppier the shooting.
627 #define AICODE_TURRET_DUMBFIRE_ANGLE (0.8f)
628 #define AICODE_TURRET_HEATSEEK_ANGLE (0.7f)
629 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
631 #define REARM_SOUND_DELAY (3*F1_0) // Amount of time to delay rearm/repair after mode start
632 #define REARM_BREAKOFF_DELAY (3*F1_0) // Amount of time to wait after fully rearmed to breakoff.
634 #define MIN_DIST_TO_WAYPOINT_GOAL 5.0f
635 #define MAX_GUARD_DIST 250.0f
636 #define BIG_GUARD_RADIUS 500.0f
638 #define MAX_EVADE_TIME (15 * 1000) // Max time to evade a weapon.
640 // defines for repair ship stuff.
641 #define MAX_REPAIR_SPEED 25.0f
642 #define MAX_UNDOCK_ABORT_SPEED 2.0f
644 // defines for EMP effect stuff
645 #define MAX_EMP_INACCURACY 50.0f
647 // defines for stealth
648 #define MAX_STEALTH_INACCURACY 50.0f // at max view dist
649 #define STEALTH_MAX_VIEW_DIST 400 // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
650 #define STEALTH_VIEW_CONE_DOT 0.707 // (half angle of 45 degrees)
653 ai_class Ai_classes[MAX_AI_CLASSES];
654 int Ai_firing_enabled = 1;
657 int AI_FrameCount = 0;
658 int Ship_info_inited = 0;
659 int AI_watch_object = 0; // Debugging, object to spew debug info for.
660 int Num_waypoint_lists = 0;
661 int Mission_all_attack = 0; // !0 means all teams attack all teams.
663 const char *Skill_level_names(int level, int translate)
665 const char *str = NULL;
667 #if NUM_SKILL_LEVELS != 5
668 #error Number of skill levels is wrong!
674 str = XSTR("Very Easy", 469);
677 str = XSTR("Easy", 470);
680 str = XSTR("Medium", 471);
683 str = XSTR("Hard", 472);
686 str = XSTR("Insane", 473);
694 str = NOX("Very Easy");
716 #define DELAY_TARGET_TIME (12*1000) // time in milliseconds until a ship can target a new enemy after an order.
718 // Make enemy ships turn more slowly at lower skill levels.
719 float Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
721 // Maximum number of simultaneous homing weapons on player based on skill level.
722 int Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
724 // Number of ships that can attack another ship at a given skill level.
725 int Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
727 // How long until next predict position.
728 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
730 // AI ships link primary weapons if energy levels greater than the following amounts:
731 float Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f}; // always link
732 float Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f}; // link if hull strength low
734 // Seconds to add to time it takes to get enemy in range. Only for player's enemies.
735 float In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
737 // No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
738 // Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
739 float Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
741 // Chance a countermeasure will be fired based on skill level.
742 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f}; // Note, this gets scaled by ai_class
744 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
746 // accuracy we feed into the beam weapons based upon skill system
747 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
749 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
750 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
752 pnode Path_points[MAX_PATH_POINTS];
753 pnode *Ppfp; // Free pointer in path points.
757 char *Ai_class_names[MAX_AI_CLASSES];
759 // global for rearm status for teams
760 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
762 // globals for dealing with when to fire huge secondary weapons
763 #define MAX_HUGE_SECONDARY_INFO 10
772 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
774 int Ai_last_arrive_path; // index of ship_bay path used by last arrival from a fighter bay
776 // forward declarations
777 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
778 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
779 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
781 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time. This function
782 // sets the timestamp used to tell is it is a good time for this team to rearm. Once the timestamp
783 // is no longer valid, then rearming is not a "good time"
784 // not safe. Called from sexpression code.
785 void ai_set_rearm_status( int team, int time )
787 SDL_assert( time >= 0 );
791 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
794 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
797 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
800 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
803 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
811 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
812 // object to rearm. "safe" is currently defined by the mission designer using the good/bad
813 // time to rearm sexpressions. This status is currently team based. This function could
814 // be easily expended to further the definition of "safe"
815 int ai_good_time_to_rearm( object *objp )
819 SDL_assert(objp->type == OBJ_SHIP);
820 team = Ships[objp->instance].team;
825 status = timestamp_valid(Ai_friendly_rearm_timestamp);
828 status = timestamp_valid(Ai_hostile_rearm_timestamp);
831 status = timestamp_valid(Ai_neutral_rearm_timestamp);
834 status = timestamp_valid(Ai_traitor_rearm_timestamp);
837 status = timestamp_valid(Ai_unknown_rearm_timestamp);
847 // functions to deal with letting the ai know about good times to fire powerful secondary
850 // this function is entry point from sexpression code to set internal data for use by ai code.
851 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
855 // find an open slot to put this data
856 for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
857 if ( Ai_huge_fire_info[i].weapon_index == -1 )
861 SDL_assert( i < MAX_HUGE_SECONDARY_INFO ); // we've run out of room
863 Ai_huge_fire_info[i].weapon_index = weapon_index;
864 Ai_huge_fire_info[i].team = team;
865 Ai_huge_fire_info[i].max_fire_count = max_fire_count;
867 Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
870 // function called internally to the ai code to tell whether or not weapon_num can be fired
871 // from firer_objp at target_objp. This function will resolve the team for the firer.
873 // -1 -- when conditions don't allow firer to fire weapon_num on target_objp
874 // >=0 -- when conditions allow firer to fire. Return value is max number of weapon_nums
875 // which can be fired on target_objp
876 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
878 int i, firer_team, target_signature;
880 huge_fire_info *hfi = NULL;
882 SDL_assert( firer_objp->type == OBJ_SHIP );
883 firer_ship = &Ships[firer_objp->instance];
884 firer_team = firer_ship->team;
886 // get target object's signature and try to find it in the list.
887 target_signature = target_objp->signature;
888 for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
889 int ship_index, signature;
891 hfi = &Ai_huge_fire_info[i];
892 if ( hfi->weapon_index == -1 )
895 ship_index = ship_name_lookup( hfi->shipname );
896 if ( ship_index == -1 )
899 signature = Objects[Ships[ship_index].objnum].signature;
901 // sigatures, weapon_index, and team must match
902 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
906 // return -1 if not found
907 if ( i == MAX_HUGE_SECONDARY_INFO )
910 // otherwise, we can return the max number of weapons we can fire against target_objps
912 return hfi->max_fire_count;
915 // function to clear out secondary firing infomration between levels
916 void ai_init_secondary_info()
920 // clear out the data for dealing with when ai ships can fire huge secondary weapons
921 for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
922 Ai_huge_fire_info[i].weapon_index = -1;
923 Ai_huge_fire_info[i].team = -1;
924 Ai_huge_fire_info[i].max_fire_count = -1;
925 Ai_huge_fire_info[i].shipname = NULL;
930 // Garbage collect the Path_points buffer.
931 // Scans all objects, looking for used Path_points records.
932 // Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
933 // Updates Ppfp to point to first free record.
934 // This function is fairly fast. Its worst-case running time is proportional to
935 // 3*MAX_PATH_POINTS + MAX_OBJECTS
936 // Things to do to optimize this function:
937 // 1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
938 // 2. When pp_xlate is getting stuffed the first time, note highest index and use that
939 // instead of MAX_PATH_POINTS in following two for loops.
940 void garbage_collect_path_points()
943 int pp_xlate[MAX_PATH_POINTS];
947 // Scan all objects and create Path_points xlate table.
948 for (i=0; i<MAX_PATH_POINTS; i++)
951 // in pp_xlate, mark all used Path_point records
952 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
953 A = &Objects[so->objnum];
954 ship *shipp = &Ships[A->instance];
955 if (shipp->ai_index != -1) {
956 ai_info *aip = &Ai_info[shipp->ai_index];
958 if ((aip->path_length > 0) && (aip->path_start > -1)) {
960 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
961 SDL_assert(pp_xlate[i] == 0); // If this is not 0, then two paths use this point!
968 // Now, stuff xlate index in pp_xlate. This is the number to translate any path_start
969 // or path_cur index to.
971 for (i=0; i<MAX_PATH_POINTS; i++) {
979 // Update global Path_points free pointer.
980 Ppfp = &Path_points[xlt];
982 // Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
983 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
984 A = &Objects[so->objnum];
985 ship *shipp = &Ships[A->instance];
986 if (shipp->ai_index != -1) {
987 ai_info *aip = &Ai_info[shipp->ai_index];
989 if ((aip->path_length > 0) && (aip->path_start > -1)) {
990 SDL_assert(aip->path_start < MAX_PATH_POINTS);
991 aip->path_start = pp_xlate[aip->path_start];
993 SDL_assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
994 aip->path_cur = pp_xlate[aip->path_cur];
999 // Now, compress the buffer.
1000 for (i=0; i<MAX_PATH_POINTS; i++)
1001 if (i != pp_xlate[i])
1002 Path_points[pp_xlate[i]] = Path_points[i];
1006 // Hash two values together, return result.
1007 // Hash function: curval shifted right circular by one, newval xored in.
1008 int hash(unsigned int curval, int newval)
1010 int addval = curval & 1;
1014 curval |= 0x80000000;
1020 // Hash some information in an object together.
1021 // On 2/20/97, the information is position and orientation.
1022 int create_object_hash(object *objp)
1025 unsigned int hashval = 0;
1028 ip = (int *) &objp->orient;
1030 for (i=0; i<9; i++) {
1031 hashval = hash(hashval, *ip);
1035 ip = (int *) &objp->pos;
1037 for (i=0; i<3; i++) {
1038 hashval = hash(hashval, *ip);
1045 // Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1046 void parse_float_list(float *plist)
1050 for (i=0; i<NUM_SKILL_LEVELS; i++) {
1051 stuff_float(&plist[i]);
1055 void parse_ai_class()
1057 ai_class *aicp = &Ai_classes[Num_ai_classes];
1059 required_string("$Name:");
1060 stuff_string(aicp->name, F_NAME, NULL);
1062 Ai_class_names[Num_ai_classes] = aicp->name;
1064 required_string("$accuracy:");
1065 parse_float_list(aicp->ai_accuracy);
1067 required_string("$evasion:");
1068 parse_float_list(aicp->ai_evasion);
1070 required_string("$courage:");
1071 parse_float_list(aicp->ai_courage);
1073 required_string("$patience:");
1074 parse_float_list(aicp->ai_patience);
1079 // open localization
1082 read_file_text("ai.tbl");
1088 required_string("#AI Classes");
1090 while (required_string_either("#End", "$Name:")) {
1091 SDL_assert( Num_ai_classes < MAX_AI_CLASSES);
1098 // close localization
1102 LOCAL int ai_inited = 0;
1104 //========================= BOOK-KEEPING FUNCTIONS =======================
1106 // Called once at game start-up
1110 // Do the first time initialization stuff here
1113 if ((rval = setjmp(parse_abort)) != 0) {
1114 Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1127 // this inits the ai. You should be able to call this between
1128 // levels to reset everything.
1129 void ai_level_init()
1133 // Do the stuff to reset all ai stuff here
1134 for (i=0; i<MAX_AI_INFO ; i++) {
1135 Ai_info[i].shipnum = -1;
1137 Ai_goal_signature = 0;
1138 Ai_friendly_rearm_timestamp = timestamp(-1);
1139 Ai_hostile_rearm_timestamp = timestamp(-1);
1140 Ai_neutral_rearm_timestamp = timestamp(-1);
1141 Ai_traitor_rearm_timestamp = timestamp(-1);
1143 // clear out the stuff needed for AI firing powerful secondary weapons
1144 ai_init_secondary_info();
1146 Ai_last_arrive_path=0;
1150 // -----------------------------------------------------------------------------
1151 // Check if object is a stealth ship
1152 int is_object_stealth_ship(object* objp)
1154 if (objp->type == OBJ_SHIP) {
1155 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1164 // -----------------------------------------------------------------------------
1165 // Init necessary ai info for new stealth target
1166 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1168 SDL_assert(is_object_stealth_ship(stealth_objp));
1170 // set necessary ai info for new stealth target
1171 aip->stealth_last_pos = stealth_objp->pos;
1172 aip->stealth_velocity = stealth_objp->phys_info.vel;
1173 aip->stealth_last_visible_stamp = timestamp();
1176 // -----------------------------------------------------------------------------
1177 // Check whether Pl_objp can see a stealth ship object
1178 #define STEALTH_INVISIBLE 0
1179 #define STEALTH_VISIBLE 1
1180 #define STEALTH_FULLY_TARGETABLE 2
1182 float get_skill_stealth_dist_scaler()
1184 // return dist scaler based on skill level
1185 switch (Game_skill_level) {
1186 case 0: // very easy
1208 float get_skill_stealth_dot_scaler()
1210 // return multiplier on dot based on skill level
1211 switch (Game_skill_level) {
1212 case 0: // very easy
1234 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1237 vector vec_to_stealth;
1238 float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1240 SDL_assert(stealth_objp->type == OBJ_SHIP);
1241 shipp = &Ships[stealth_objp->instance];
1242 SDL_assert(viewer_objp->type == OBJ_SHIP);
1244 // check if stealth ship
1245 SDL_assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1247 // check if in neb and below awac level for visible
1248 if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1249 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1250 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1251 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.v.fvec, &vec_to_stealth) / dist_to_stealth;
1253 // get max dist at which stealth is visible
1254 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1256 // now check if within view frustrum
1257 float needed_dot_to_stealth;
1258 if (dist_to_stealth < 100) {
1259 needed_dot_to_stealth = 0.0f;
1261 needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1263 if (dot_to_stealth > needed_dot_to_stealth) {
1264 if (dist_to_stealth < max_stealth_dist) {
1265 return STEALTH_VISIBLE;
1269 // not within frustrum
1270 return STEALTH_INVISIBLE;
1273 // visible by awacs level
1274 return STEALTH_FULLY_TARGETABLE;
1279 // Compute dot product of direction vector and forward vector.
1280 // Direction vector is vector from one object to other object.
1281 // Forward vector is the forward vector of the ship.
1282 // If from_dot == NULL, don't fill it in.
1283 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1288 dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1290 *to_dot = vm_vec_dot(&objp->orient.v.fvec, &v2o);
1292 if (from_dot != NULL)
1293 *from_dot = - vm_vec_dot(&other_objp->orient.v.fvec, &v2o);
1298 // -----------------------------------------------------------------------------
1299 // update estimated stealth info
1300 // this is a "cheat" update
1301 // error increases with time not seen, true distance away, dot to enemey
1302 // this is done only if we can not see the stealth target
1303 // need to infer its position either by weapon fire pos or last know pos
1304 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1307 object *stealth_objp;
1309 float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1310 float pos_error, vel_error;
1311 vector error_vec, vec_to_stealth;
1312 float dist_to_stealth, dot_to_stealth;
1313 float delta_time, delta_capped;
1316 // make sure I am targeting a stealth ship
1317 SDL_assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1318 stealth_objp = &Objects[aip->target_objnum];
1321 ship = &Objects[Ships[aip->shipnum].objnum];
1323 // if update is due to weapon fire, get exact stealth position
1325 aip->stealth_last_pos = stealth_objp->pos;
1326 aip->stealth_velocity = stealth_objp->phys_info.vel;
1327 aip->stealth_last_visible_stamp = timestamp();
1331 // get time since last seen
1332 delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1334 // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1335 // only update if stealth info is "old"
1336 if ( (delta_time) < 0.5 ) {
1340 // find vec_to_stealth and dist
1341 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1342 dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1343 dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.v.fvec);
1346 delta_capped = delta_time;
1347 if (delta_time > 5.0) {
1348 delta_capped = 5.0f;
1351 // erorr_time_mult (for 0-5) -> (1-6)
1352 error_time_mult = (1.0f + delta_capped);
1354 // error_dot_mult (-1 to 1) -> (1-3)
1355 error_dot_mult = (2 - dot_to_stealth);
1357 // error_dist_mult (0-1000+) -> (1-4)
1358 error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1359 if (error_dist_mult < 1) {
1360 error_dist_mult = 1.0f;
1361 } else if (error_dist_mult > 4) {
1362 error_dist_mult = 4.0f;
1365 // multiply error out
1366 error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1368 float base_pos_error = 10;
1369 float base_vel_error = 2;
1371 // find the position and velocity error magnitude;
1372 pos_error = base_pos_error * error_mult;
1373 vel_error = base_vel_error * error_mult;
1375 // get an error that changes slowly over time
1376 static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1377 vm_vec_zero(&error_vec);
1379 // update pos and vel with error
1380 vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1382 // revise last "known" position to arrive at last pos with given error
1383 vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1384 vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1388 // Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1389 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1391 object *objp, *weapon_objp;
1393 float old_dist, new_dist;
1394 float old_dot, new_dot;
1395 object *old_weapon_objp = NULL;
1397 if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1401 objp = &Objects[attacked_objnum];
1403 // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1404 // an asteroid or bomb).
1405 if ( objp->type != OBJ_SHIP ) {
1409 weapon_objp = &Objects[weapon_objnum];
1411 aip = &Ai_info[Ships[objp->instance].ai_index];
1413 // if my taraget is a stealth ship and is not visible
1414 if (aip->target_objnum >= 0) {
1415 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1416 if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1417 // and the weapon is coming from that stealth ship
1418 if (weapon_objp->parent == aip->target_objnum) {
1419 // update my position estimate for stealth ship
1420 update_ai_stealth_info_with_error(aip/*, 1*/);
1426 if (aip->danger_weapon_objnum != -1) {
1427 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1428 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1431 aip->danger_weapon_objnum = -1;
1435 new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1437 if (aip->danger_weapon_objnum == -1) {
1438 if (new_dist < 1500.0f) {
1439 if (new_dot > 0.5f) {
1440 aip->danger_weapon_objnum = weapon_objnum;
1441 aip->danger_weapon_signature = weapon_objp->signature;
1445 SDL_assert(old_weapon_objp != NULL);
1446 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1448 if (old_dot < 0.5f) {
1449 aip->danger_weapon_objnum = -1;
1453 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1454 if (new_dist < old_dist) {
1455 aip->danger_weapon_objnum = weapon_objnum;
1456 aip->danger_weapon_signature = weapon_objp->signature;
1462 // If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1463 // (rvec defaults to NULL)
1464 void ai_turn_towards_vector(vector *dest, object *objp,
1465 float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1467 //matrix goal_orient;
1469 vector vel_in, vel_out, desired_fvec, src;
1472 vector vel_limit, acc_limit;
1475 // Don't allow a ship to turn if it has no engine strength.
1476 // AL 3-12-98: objp may not always be a ship!
1477 if ( objp->type == OBJ_SHIP ) {
1478 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1482 //nprintf(("AI", "Ship %s turning towards point %7.3f %7.3f %7.3f\n", Ships[objp->instance].ship_name, dest->x, dest->y, dest->z));
1483 pip = &objp->phys_info;
1485 vel_in = pip->rotvel;
1486 curr_orient = objp->orient;
1487 delta_time = flFrametime;
1489 SDL_assert(turn_time > 0.0f);
1491 // Scale turn_time based on skill level and team.
1492 if (!(flags & AITTV_FAST)){
1493 if (objp->type == OBJ_SHIP){
1494 if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1495 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1500 // Set max turn rate.
1501 vel_limit.xyz.x = 2*PI/turn_time;
1502 vel_limit.xyz.y = 2*PI/turn_time;
1503 vel_limit.xyz.z = 2*PI/turn_time;
1505 // Set rate at which ship can accelerate to its rotational velocity.
1506 // For now, weapons just go much faster.
1507 acc_limit = vel_limit;
1508 if (objp->type == OBJ_WEAPON)
1509 vm_vec_scale(&acc_limit, 8.0f);
1513 if (rel_pos != NULL) {
1515 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1516 vm_vec_add2(&src, &gun_point);
1519 vm_vec_normalized_dir(&desired_fvec, dest, &src);
1521 // Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1522 // to be moving towards goal rather than just pointing. So, if slide_vec is !NULL, try to
1523 // make ship move towards goal, not point at goal.
1524 if (slide_vec != NULL) {
1525 vm_vec_add2(&desired_fvec, slide_vec);
1526 vm_vec_normalize(&desired_fvec);
1529 // Should be more general case here. Currently, anything that is not a weapon will bank when it turns.
1530 if (objp->type == OBJ_WEAPON)
1532 else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) { // Theoretically, this will only happen for Shivans.
1533 delta_bank = bank_override;
1534 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1536 delta_bank = vm_vec_dot(&curr_orient.v.rvec, &objp->last_orient.v.rvec);
1537 delta_bank = 100.0f * (1.0f - delta_bank);
1538 if (vm_vec_dot(&objp->last_orient.v.fvec, &objp->orient.v.rvec) < 0.0f)
1539 delta_bank = -delta_bank;
1541 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1544 // Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1545 // that is causing ships to inexplicably rotate very far. If you hit the Int3(), set the next statement to be
1546 // the one marked "HERE". (Do this clicking the cursor there, then right clicking. Choose the right option.)
1547 // This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1548 // Note, you'll need to enable the Int3() about ten lines below.
1550 vector tvec = objp->orient.v.fvec;
1552 matrix objp_orient_copy;
1554 vel_in_copy = vel_in;
1555 objp_orient_copy = objp->orient;
1557 vel_in = vel_in_copy; // HERE
1558 objp->orient = objp_orient_copy;
1561 matrix out_orient, goal_orient;
1563 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1564 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1565 objp->orient = out_orient;
1567 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1570 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1571 if (delta_time < 0.25f && vm_vec_dot(&objp->orient.v.fvec, &tvec) < 0.1f)
1572 Int3(); // Get Andsager. A ship has turned too far in one frame.
1576 pip->rotvel = vel_out;
1579 void init_ship_info()
1583 if (Ship_info_inited)
1586 for (i=0; i<MAX_SHIP_TYPES; i++) {
1587 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1588 Ship_info[i].max_accel = Ship_info[i].max_vel.xyz.z;
1591 Ship_info_inited = 1;
1595 // Set aip->target_objnum to objnum
1596 // Update aip->previous_target_objnum.
1597 // If new target (objnum) is different than old target, reset target_time.
1598 int set_target_objnum(ai_info *aip, int objnum)
1601 char old_name[32], new_name[32];
1603 if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1604 return aip->target_objnum;
1606 if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1607 if (aip->target_objnum == -1)
1608 strcpy(old_name, "none");
1610 strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1613 strcpy(new_name, "none");
1615 strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1617 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1621 // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1623 if ( objnum >= 0 ) {
1624 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1625 if ( Objects[objnum].flags & OF_PROTECTED ) {
1626 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1627 //Int3(); // this should not happen
1628 return aip->target_objnum; // don't change targets
1634 if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1635 return aip->target_objnum;
1638 if (aip->target_objnum == objnum) {
1639 aip->previous_target_objnum = aip->target_objnum;
1641 aip->previous_target_objnum = aip->target_objnum;
1643 // ignore this assert if a multiplayer observer
1644 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1646 SDL_assert(objnum != Ships[aip->shipnum].objnum); // make sure not targeting self
1649 // if stealth target, init ai_info for stealth
1650 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1651 init_ai_stealth_info(aip, &Objects[objnum]);
1654 aip->target_objnum = objnum;
1655 aip->target_time = 0.0f;
1656 aip->target_signature = Objects[objnum].signature;
1657 // clear targeted subsystem
1658 set_targeted_subsys(aip, NULL, -1);
1661 return aip->target_objnum;
1664 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1666 // Make new_subsys the targeted subsystem of ship *aip.
1667 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1669 SDL_assert(aip != NULL);
1671 aip->last_subsys_target = aip->targeted_subsys;
1672 aip->targeted_subsys = new_subsys;
1673 aip->targeted_subsys_parent = parent_objnum;
1676 // Make new_subsys target
1677 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1678 if ( aip != Player_ai ) {
1679 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1680 ship_primary_changed(&Ships[aip->shipnum]); // AL: maybe send multiplayer information when AI ship changes primaries
1684 if ( aip == Player_ai ) {
1685 hud_lock_reset(0.5f);
1689 // Cleanup any subsys path information if it exists
1690 ai_big_subsys_path_cleanup(aip);
1693 return aip->targeted_subsys;
1696 // called to init the data for single ai object. At this point,
1697 // the ship and the object and the ai_info are are correctly
1698 // linked together. Ai_info[ai_index].shipnum is the only valid field
1700 // This is called right when the object is parsed, so you can't assume much
1701 // has been initialized. For example, wings, waypoints, goals are probably
1702 // not yet loaded. --MK, 10/8/96
1703 void ai_object_init(object * obj, int ai_index)
1706 SDL_assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1708 aip = &Ai_info[ai_index];
1710 aip->type = 0; // 0 means not in use.
1711 aip->wing = -1; // Member of what wing? -1 means none.
1712 aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1713 aip->behavior = AIM_NONE;
1716 // If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1717 void adjust_accel_for_docking(ai_info *aip)
1719 if (aip->dock_objnum != -1) {
1720 object *obj2p = &Objects[aip->dock_objnum];
1723 obj1p = &Objects[Ships[aip->shipnum].objnum];
1725 if (obj2p->signature == aip->dock_signature) {
1728 ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1730 // put cap on how much ship can slow down
1732 // FS1 can go slower, perhaps down to 0, but I'll cap it at .25 just in case
1733 if (ratio < 0.25f) {
1742 if (AI_ci.forward > ratio) {
1743 AI_ci.forward = ratio;
1749 // -------------------------------------------------------------------
1750 void accelerate_ship(ai_info *aip, float accel)
1752 aip->prev_accel = accel;
1753 AI_ci.forward = accel;
1754 adjust_accel_for_docking(aip);
1757 // --------------------------------------------------------------------------
1758 void change_acceleration(ai_info *aip, float delta_accel)
1762 if (delta_accel < 0.0f) {
1763 if (aip->prev_accel > 0.0f)
1764 aip->prev_accel = 0.0f;
1765 } else if (aip->prev_accel < 0.0f)
1766 aip->prev_accel = 0.0f;
1768 new_accel = aip->prev_accel + delta_accel * flFrametime;
1770 if (new_accel > 1.0f)
1772 else if (new_accel < -1.0f)
1775 aip->prev_accel = new_accel;
1777 AI_ci.forward = new_accel;
1778 adjust_accel_for_docking(aip);
1781 void set_accel_for_target_speed(object *objp, float tspeed)
1786 aip = &Ai_info[Ships[objp->instance].ai_index];
1788 max_speed = Ships[objp->instance].current_max_speed;
1790 AI_ci.forward = tspeed/max_speed;
1791 aip->prev_accel = AI_ci.forward;
1793 adjust_accel_for_docking(aip);
1796 // Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1797 // on the vector from the center of *objp through the point *vp.
1798 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1803 vm_vec_sub(&v1, vp, pos);
1804 mag = vm_vec_mag(&v1);
1807 Warning(LOCATION, "projectable point is at center of sphere.");
1808 (void) vm_vec_make(&v1, 0.0f, radius, 0.0f);
1810 vm_vec_normalize(&v1);
1811 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1814 vm_vec_add2(&v1, pos);
1818 // Stuff tan1 with tangent point on sphere. tan1 is point nearer to *p1
1819 // *p0 is point through which tangents pass.
1820 // *centerp is center of sphere.
1821 // *p1 is another point in space to define the plane in which tan1, tan2 reside.
1822 // radius is the radius of the sphere.
1823 // Note, this is a very approximate function just for AI.
1824 // Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1825 // contains the tangent point.
1826 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1828 vector dest_vec, v2c, perp_vec, temp_vec, v2;
1831 // Detect condition of point inside sphere.
1832 if (vm_vec_dist(p0, centerp) < radius)
1833 project_point_to_perimeter(tan1, centerp, radius, p0);
1835 vm_vec_normalized_dir(&v2c, centerp, p0);
1837 // Compute perpendicular vector using p0, centerp, p1
1838 vm_vec_normal(&temp_vec, p0, centerp, p1);
1839 vm_vec_sub(&v2, centerp, p0);
1840 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1842 vm_vec_normalize(&perp_vec);
1844 dist = vm_vec_dist_quick(p0, centerp);
1845 ratio = dist / radius;
1848 vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1850 vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1852 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1856 // --------------------------------------------------------------------------
1857 // Given an object and a point, turn towards the point, resulting in
1858 // approach behavior.
1859 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1862 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1864 // check if in formation and if not leader, don't change rotvel.xyz.z (bank to match leader elsewhere)
1865 if (aip->ai_flags & AIF_FORMATION) {
1866 if (&Objects[aip->goal_objnum] != objp) {
1867 float rotvel_z = objp->phys_info.rotvel.xyz.z;
1868 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1869 objp->phys_info.rotvel.xyz.z = rotvel_z;
1873 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1877 // --------------------------------------------------------------------------
1878 // Given an object and a point, turn away from the point, resulting in avoidance behavior.
1879 // Note: Turn away at full speed, not scaled down by skill level.
1880 void turn_away_from_point(object *objp, vector *point, float bank_override)
1882 vector opposite_point;
1884 vm_vec_sub(&opposite_point, &objp->pos, point);
1885 vm_vec_add2(&opposite_point, &objp->pos);
1887 ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1891 // --------------------------------------------------------------------------
1892 // Given an object and a point, turn tangent to the point, resulting in
1893 // a circling behavior.
1894 // Make object *objp turn around the point *point with a radius of radius.
1895 // Note that this isn't the same as following a circle of radius radius with
1896 // center *point, but it should be adequate.
1897 // Note that if you want to circle an object without hitting it, you should use
1898 // about twice that object's radius for radius, else you'll certainly bump into it.
1899 // Return dot product to goal point.
1900 float turn_towards_tangent(object *objp, vector *point, float radius)
1902 vector vec_to_point;
1904 vector perp_point; // point radius away from *point on vector to objp->pos
1905 vector up_vec, perp_vec;
1907 vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1908 vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1909 vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1911 vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1912 if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1913 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1915 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1918 // Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1919 turn_towards_point(objp, &goal_point, NULL, 0.0f);
1923 vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1924 return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1927 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1929 vector r_vec, theta_vec;
1930 vector center_vec, vec_on_cylinder, sph_r_vec;
1933 // find closest z of center objp
1934 vm_vec_sub(&sph_r_vec, &objp->pos, ¢er_objp->pos);
1935 center_obj_z = vm_vec_dotprod(&sph_r_vec, ¢er_objp->orient.v.fvec);
1937 // find pt on axis with closest z
1938 vm_vec_scale_add(¢er_vec, ¢er_objp->pos, ¢er_objp->orient.v.fvec, center_obj_z);
1941 vm_vec_sub(&r_vec, &objp->pos, ¢er_vec);
1942 // float r_mag = vm_vec_normalize_quick(&r_vec);
1943 // mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1944 SDL_assert( (vm_vec_dotprod(&r_vec, ¢er_objp->orient.v.fvec) < 0.0001));
1946 // get theta vec - perp to r_vec and z_vec
1947 vm_vec_crossprod(&theta_vec, ¢er_objp->orient.v.fvec, &r_vec);
1950 float mag = vm_vec_normalize(&theta_vec);
1951 SDL_assert(mag > 0.9999 && mag < 1.0001);
1955 vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1958 float dot = vm_vec_dotprod(&temp, ¢er_objp->orient.v.fvec);
1959 SDL_assert( dot >0.9999 && dot < 1.0001);
1962 // find pt on clylinder with closest z
1963 vm_vec_scale_add(&vec_on_cylinder, ¢er_vec, &r_vec, radius);
1965 vector goal_pt, v2g;
1966 vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1968 // Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1969 turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1971 vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1972 return vm_vec_dot(&objp->orient.v.fvec, &v2g);
1975 // Returns a point radius units away from *point that *objp should turn towards to orbit *point
1976 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1978 vector vec_to_point;
1979 vector perp_point; // point radius away from *point on vector to objp->pos
1980 vector up_vec, perp_vec;
1982 vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1983 vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.v.fvec);
1984 vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1985 vm_vec_normalize(&perp_vec);
1987 vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1989 if (vm_vec_dot(&objp->orient.v.fvec, &perp_vec) > 0.0f) {
1990 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1992 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1996 int Player_attacking_enabled = 1;
1998 // -----------------------------------------------------------------------------
1999 // Determine whether an object is targetable within a nebula
2000 int object_is_targetable(object *target, ship *viewer)
2002 int stealth_ship = 0;
2004 // if target is ship, check if visible by team
2005 if (target->type == OBJ_SHIP) {
2006 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
2007 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
2012 // for AI partially targetable works as fully targetable, except for stealth ship
2014 // if not team targetable, check if within frustrum
2015 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
2022 // if not fully targetable by team, check awacs level with viewer
2023 // allow targeting even if only only partially targetable to player
2024 float radar_return = awacs_get_level(target, viewer);
2025 if ( radar_return > 0.4 ) {
2032 // Return number of enemies attacking object objnum
2034 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
2035 int num_enemies_attacking(int objnum)
2045 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2046 objp = &Objects[so->objnum];
2047 SDL_assert(objp->instance != -1);
2048 sp = &Ships[objp->instance];
2050 if (Ai_info[sp->ai_index].target_objnum == objnum)
2053 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2054 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2056 // loop through all the subsystems, check if turret has objnum as a target
2057 ssp = GET_FIRST(&sp->subsys_list);
2058 while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2060 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2061 if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2065 ssp = GET_NEXT( ssp );
2073 // Get the team to fire on given an object.
2074 int get_enemy_team_mask(int objnum)
2076 int my_team, enemy_team_mask;
2078 my_team = Ships[Objects[objnum].instance].team;
2080 if (Mission_all_attack) {
2081 // All teams attack all teams.
2084 enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2087 enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2090 enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2093 enemy_team_mask = TEAM_HOSTILE;
2096 enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2099 enemy_team_mask = TEAM_HOSTILE;
2100 Int3(); // Illegal value for team!
2106 enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2109 enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2112 enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2115 enemy_team_mask = TEAM_HOSTILE;
2118 enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2121 enemy_team_mask = TEAM_HOSTILE;
2122 Int3(); // Illegal value for team!
2127 return enemy_team_mask;
2130 // Scan all the ships in *objp's wing.
2131 // Return the lowest maximum speed of a ship in the wing.
2132 // Current maximum speed (based on energy settings) is shipp->current_max_speed
2133 float get_wing_lowest_max_speed(object *objp)
2137 float lowest_max_speed;
2142 SDL_assert(objp->type == OBJ_SHIP);
2143 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2144 shipp = &Ships[objp->instance];
2145 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2146 aip = &Ai_info[shipp->ai_index];
2148 wingnum = aip->wing;
2150 lowest_max_speed = shipp->current_max_speed;
2152 if ( wingnum == -1 )
2153 return lowest_max_speed;
2155 SDL_assert(wingnum >= 0);
2157 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2158 o = &Objects[so->objnum];
2159 ship *oshipp = &Ships[o->instance];
2160 ai_info *oaip = &Ai_info[oshipp->ai_index];
2162 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2163 // Note: If a ship in the wing has a super low max speed, probably its engines are disabled. So, fly along and
2164 // ignore the poor guy.
2165 float cur_max = oshipp->current_max_speed;
2167 if (oaip->ai_flags & AIF_DOCKED) {
2168 if (oaip->dock_objnum > -1)
2169 if (Objects[oaip->dock_objnum].type == OBJ_SHIP)
2170 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2173 if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2174 lowest_max_speed = cur_max;
2179 return lowest_max_speed;
2183 // Tell everyone to ignore object objnum.
2184 void set_global_ignore_object(int objnum)
2188 SDL_assert(Objects[objnum].type == OBJ_SHIP);
2190 nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2192 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2193 if (Ignore_objects[i].objnum == -1) {
2194 Ignore_objects[i].objnum = objnum;
2195 Ignore_objects[i].signature = Objects[objnum].signature;
2200 if (i == MAX_IGNORE_OBJECTS) {
2201 // Couldn't find a free slot, but maybe one of these objects has died.
2202 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2203 int o = Ignore_objects[i].objnum;
2204 if (Objects[o].type != OBJ_SHIP)
2205 break; // Not a ship, so use this slot.
2206 if (Objects[o].signature != Ignore_objects[i].signature)
2207 break; // Signatures don't match, so use this slot.
2210 if (i != MAX_IGNORE_OBJECTS) {
2211 Ignore_objects[i].objnum = objnum;
2212 Ignore_objects[i].signature = Objects[objnum].signature;
2214 nprintf(("Warning", "Ignore_objects buffer full. Stealing a slot to ignore object #%i\n"));
2219 r = objnum % MAX_IGNORE_OBJECTS;
2221 Ignore_objects[r].objnum = objnum;
2222 Ignore_objects[r].signature = Objects[objnum].signature;
2229 // Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2231 // TRUE if objnum is aip->ignore_objnum (and signatures match)
2232 // or objnum is in ignore wing
2234 int is_ignore_object(ai_info *aip, int objnum)
2237 /* // First, scan all objects in global array of objects to be ignored.
2238 for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2239 if (Ignore_objects[i].objnum != -1)
2240 if (objnum == Ignore_objects[i].objnum)
2241 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2245 // Didn't find in global list. Now check
2246 if (aip->ignore_objnum == UNUSED_OBJNUM)
2247 return 0; // Not ignoring anything.
2248 else if (aip->ignore_objnum >= 0) { // This means it's ignoring an object, not a wing.
2249 if (aip->ignore_objnum == objnum) {
2250 if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2253 aip->ignore_objnum = UNUSED_OBJNUM;
2259 } else { // Ignoring a wing.
2260 Int3(); // Should never happen. I thought I removed this behavior! -- MK, 5/17/98
2262 /* int ignore_wingnum = -(aip->ignore_objnum + 1);
2264 SDL_assert(ignore_wingnum < MAX_WINGS);
2265 SDL_assert(aip->shipnum >= 0);
2266 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2270 // -----------------------------------------------------------------------------
2272 // given a ship with bounding box and a point, find the closest point on the bbox
2273 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2275 vector temp, rf_start;
2277 pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2279 // get start in ship rf
2280 vm_vec_sub(&temp, start, &ship_obj->pos);
2281 vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2284 int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2286 // get box_pt in world rf
2287 vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2288 vm_vec_add2(box_pt, &ship_obj->pos);
2294 typedef struct eval_nearest_objnum {
2297 int enemy_team_mask;
2303 int check_danger_weapon_objnum;
2304 } eval_nearest_objnum;
2307 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2310 ship_subsys *attacking_subsystem;
2312 aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2314 attacking_subsystem = aip->targeted_subsys;
2316 if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2317 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2319 if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2322 // If only supposed to attack ship in a specific wing, don't attack other ships.
2323 if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2326 // Don't keep firing at a ship that is in its death throes.
2327 if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2330 if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2333 if (eno->trial_objp->flags & OF_PROTECTED)
2336 if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2339 ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2341 if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2344 if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2348 // Allow targeting of stealth in nebula by his firing at me
2349 // This is done for a specific ship, not generally.
2350 if ( !eno->check_danger_weapon_objnum ) {
2351 // check if can be targeted if inside nebula
2352 if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2353 // check if stealth ship is visible, but not "targetable"
2354 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2360 // if objnum is BIG or HUGE, find distance to bbox
2361 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2363 // check if inside bbox
2364 int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2369 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2372 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2375 // Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2376 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2380 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2381 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2382 if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2383 dist *= (float) (num_attacking+2)/2.0f; // prevents lots of ships from attacking same target
2386 if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2387 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS; // Favor attacking non-players based on skill level.
2390 if (dist < eno->nearest_dist) {
2391 eno->nearest_dist = dist;
2392 eno->nearest_objnum = eno->trial_objp-Objects;
2402 // Given an object and an enemy team, return the index of the nearest enemy object.
2403 // Unless aip->targeted_subsys != NULL, don't allow to attack objects
2404 // with OF_PROTECTED bit set.
2405 // Ship must be within range "range".
2406 // Don't attack a ship that already has at least max_attackers attacking it.
2407 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2409 object *danger_weapon_objp;
2413 // initialize eno struct
2414 eval_nearest_objnum eno;
2415 eno.enemy_team_mask = enemy_team_mask;
2416 eno.enemy_wing = enemy_wing;
2417 eno.max_attackers = max_attackers;
2418 eno.objnum = objnum;
2420 eno.nearest_dist = range;
2421 eno.nearest_objnum = -1;
2422 eno.check_danger_weapon_objnum = 0;
2424 // go through the list of all ships and evaluate as potential targets
2425 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2426 eno.trial_objp = &Objects[so->objnum];
2427 evaluate_object_as_nearest_objnum(&eno);
2431 // check if danger_weapon_objnum has will show a stealth ship
2432 aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2433 if (aip->danger_weapon_objnum >= 0) {
2434 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2436 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2437 SDL_assert(danger_weapon_objp->type == OBJ_WEAPON);
2438 // check if parent is a ship
2439 if (danger_weapon_objp->parent >= 0) {
2440 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2441 // check if stealthy
2442 if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2443 // check if weapon is laser
2444 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2445 // check stealth ship by its laser fire
2446 eno.check_danger_weapon_objnum = 1;
2447 eno.trial_objp = &Objects[danger_weapon_objp->parent];
2448 evaluate_object_as_nearest_objnum(&eno);
2456 // If only looking for target in certain wing and couldn't find anything in
2457 // that wing, look for any object.
2458 if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2459 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2462 return eno.nearest_objnum;
2465 // Given an object and an enemy team, return the index of the nearest enemy object.
2466 // Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2467 // of enemies attacking.
2468 // It is used to find the nearest enemy to determine things like whether to rearm.
2469 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2477 nearest_objnum = -1;
2478 nearest_dist = range;
2480 aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2484 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2485 objp = &Objects[so->objnum];
2487 if ( OBJ_INDEX(objp) != objnum ) {
2488 if (Ships[objp->instance].flags & SF_DYING)
2491 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2494 if (Ships[objp->instance].team & enemy_team_mask) {
2497 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2502 if (dist < nearest_dist) {
2503 nearest_dist = dist;
2504 nearest_objnum = objp-Objects;
2511 return nearest_objnum;
2514 // return !0 if objp can be considered for a turret target, 0 otherwise
2515 // input: objp => object that turret is considering as an enemy
2516 // turret_parent => object index for ship that turret sits on
2517 int valid_turret_enemy(object *objp, object *turret_parent)
2519 if ( objp == turret_parent ) {
2523 if ( objp->type == OBJ_ASTEROID ) {
2527 if ( objp->type == OBJ_SHIP ) {
2529 shipp = &Ships[objp->instance];
2531 // don't fire at ships with protected bit set!!!
2532 if ( objp->flags & OF_PROTECTED ) {
2536 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2540 if (shipp->flags & SF_ARRIVING) {
2547 if ( objp->type == OBJ_WEAPON ) {
2548 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2549 if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2558 // return 1 if objp is in fov of the specified turret, tp. Otherwise return 0.
2559 // dist = distance from turret to center point of object
2560 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2564 vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2565 dot = vm_vec_dot(&v2e, tvec);
2567 dot += objp->radius / (dist + objp->radius);
2569 if ( dot >= tp->turret_fov ) {
2576 // return 1 if bomb_objp is headed towards ship_objp
2577 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2580 vector bomb_to_ship_vector;
2582 vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2583 dot = vm_vec_dot(&bomb_objp->orient.v.fvec, &bomb_to_ship_vector);
2592 // nubmer of live turrets with target_objnum
2593 int num_turrets_attacking(object *turret_parent, int target_objnum)
2598 shipp = &Ships[turret_parent->instance];
2600 SDL_assert(turret_parent->type == OBJ_SHIP);
2601 SDL_assert(Objects[target_objnum].type == OBJ_SHIP);
2603 for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2604 // check if subsys is alive
2605 if (ss->current_hits <= 0.0f) {
2609 // check if it's a turret
2610 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2614 // if the turret is locked
2615 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2619 // check if turret is targeting target_objnum
2620 if (ss->turret_enemy_objnum == target_objnum) {
2628 float Lethality_range_const = 2.0f;
2629 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2631 dc_get_arg(ARG_FLOAT);
2632 Lethality_range_const = Dc_arg_float;
2635 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2636 // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2637 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2640 // evaluate obj as posssible target for turret
2641 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2643 object *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2645 model_subsystem *tp = eeo->turret_subsys->system_info;
2648 // Don't look for bombs when weapon system is not ok
2649 if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2653 if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2658 if (!Player_attacking_enabled && (objp == Player_obj)) {
2663 if ( objp->type == OBJ_SHIP ) {
2664 shipp = &Ships[objp->instance];
2666 // check on enemy team
2667 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2671 // check if protected
2672 if (objp->flags & OF_PROTECTED) {
2676 // check if beam protected
2677 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2678 if (objp->flags & OF_BEAM_PROTECTED) {
2683 if (eeo->big_only_flag) {
2684 if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2689 // check if turret flagged to only target tagged ships
2690 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2694 // check if valid target in nebula
2695 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2696 // BYPASS ocassionally for stealth
2697 int try_anyway = FALSE;
2698 if ( is_object_stealth_ship(objp) ) {
2699 float turret_stealth_find_chance = 0.5f;
2700 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2701 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2715 // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2716 dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2721 // check if object is a bomb attacking the turret parent
2722 // check if bomb is homing on the turret parent ship
2723 if (objp->type == OBJ_WEAPON) {
2724 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2725 if ( dist < eeo->nearest_homing_bomb_dist ) {
2726 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2727 eeo->nearest_homing_bomb_dist = dist;
2728 eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2731 // if not homing, check if bomb is flying towards ship
2732 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2733 if ( dist < eeo->nearest_bomb_dist ) {
2734 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2735 eeo->nearest_bomb_dist = dist;
2736 eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2740 } // end weapon section
2742 // maybe recalculate dist for big or huge ship
2743 // if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2744 // fvi_ray_boundingbox(min, max, start, direction, hit);
2745 // dist = vm_vec_dist_quick(hit, tvec);
2748 // check for nearest attcker
2749 if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2750 ai_info *aip = &Ai_info[shipp->ai_index];
2752 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2753 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2; // prevents lots of ships from attacking same target
2754 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2755 dist *= (1.0f + 0.1f*num_att_turrets);
2757 // return if we're over the cap
2758 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2759 if (num_att_turrets > max_turrets) {
2763 // modify distance based on lethality of objp to my ship
2764 float active_lethality = aip->lethality;
2765 if (objp->flags & OF_PLAYER_SHIP) {
2766 active_lethality += Player_lethality_bump[Game_skill_level];
2769 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2771 // Make level 2 tagged ships more likely to be targeted
2772 if (shipp->level2_tag_left > 0.0f) {
2776 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2777 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2778 // A turret will always target a ship that is attacking itself... self-preservation!
2779 if ( aip->targeted_subsys == eeo->turret_subsys ) {
2780 dist *= 0.5f; // highest priority
2784 // maybe update nearest attacker
2785 if ( dist < eeo->nearest_attacker_dist ) {
2786 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2787 // nprintf(("AI", "Nearest enemy = %s, dist = %7.3f, dot = %6.3f, fov = %6.3f\n", Ships[objp->instance].ship_name, dist, vm_vec_dot(&v2e, tvec), tp->turret_fov));
2788 eeo->nearest_attacker_dist = dist;
2789 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2792 } // end ship section
2795 // check if object is an asteroid attacking the turret parent - taylor
2796 if (objp->type == OBJ_ASTEROID) {
2797 if ( eeo->turret_parent_objnum == asteroid_collide_objnum(objp) ) {
2798 // give priority to the closest asteroid *impact* (ms intervals)
2799 dist *= 0.9f + (0.01f * asteroid_time_to_impact(objp));
2801 if (dist < eeo->nearest_dist ) {
2802 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2803 eeo->nearest_dist = dist;
2804 eeo->nearest_objnum = OBJ_INDEX(objp);
2808 } // end asteroid selection
2812 // return 0 only if objnum is beam protected and turret is beam turret
2813 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2815 // check if turret has beam weapon
2816 model_subsystem *tp = turret_subsys->system_info;
2818 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2819 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2823 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2824 if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2834 // Given an object and an enemy team, return the index of the nearest enemy object.
2837 // turret_parent_objnum => parent objnum for the turret
2838 // turret_subsys => pointer to system_info for the turret subsystem
2839 // enemy_team_mask => OR'ed TEAM_ flags for the enemy of the turret parent ship
2840 // tpos => position of turret (world coords)
2841 // tvec => forward vector of turret (world coords)
2842 // current_enemy => objnum of current turret target
2843 int get_nearest_turret_objnum(int turret_parent_objnum, ship_subsys *turret_subsys, int enemy_team_mask, vector *tpos, vector *tvec, int current_enemy, int big_only_flag)
2845 float weapon_travel_dist;
2846 int weapon_system_ok;
2848 model_subsystem *tp;
2849 eval_enemy_obj_struct eeo;
2851 // list of stuff to go thru
2855 tp = turret_subsys->system_info;
2856 weapon_travel_dist = min(Weapon_info[tp->turret_weapon_type].lifetime * Weapon_info[tp->turret_weapon_type].max_speed, Weapon_info[tp->turret_weapon_type].weapon_range);
2858 // Set flag based on strength of weapons subsystem. If weapons subsystem is destroyed, don't let turrets fire at bombs
2859 weapon_system_ok = 0;
2860 if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2861 weapon_system_ok = 1;
2864 // Initialize eeo struct.
2865 eeo.turret_parent_objnum = turret_parent_objnum;
2866 eeo.weapon_system_ok = weapon_system_ok;
2867 eeo.weapon_travel_dist = weapon_travel_dist;
2868 eeo.big_only_flag = big_only_flag;
2869 eeo.enemy_team_mask = enemy_team_mask;
2870 eeo.current_enemy = current_enemy;
2873 eeo.turret_subsys = turret_subsys;
2875 eeo.nearest_attacker_dist = 99999.0f;
2876 eeo.nearest_attacker_objnum = -1;
2878 eeo.nearest_homing_bomb_dist = 99999.0f;
2879 eeo.nearest_homing_bomb_objnum = -1;
2881 eeo.nearest_bomb_dist = 99999.0f;
2882 eeo.nearest_bomb_objnum = -1;
2884 eeo.nearest_dist = 99999.0f;
2885 eeo.nearest_objnum = -1;
2889 for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2890 objp = &Objects[mo->objnum];
2891 evaluate_obj_as_target(objp, &eeo);
2894 if ( eeo.nearest_homing_bomb_objnum != -1 ) { // highest priority is an incoming homing bomb
2895 return eeo.nearest_homing_bomb_objnum;
2896 } else if ( eeo.nearest_bomb_objnum != -1 ) { // next highest priority is an incoming dumbfire bomb
2897 return eeo.nearest_bomb_objnum;
2902 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2903 objp = &Objects[so->objnum];
2904 evaluate_obj_as_target(objp, &eeo);
2907 SDL_assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2908 // next highest priority is attacking ship
2909 if ( eeo.nearest_attacker_objnum != -1 ) { // next highest priority is an attacking ship
2910 return eeo.nearest_attacker_objnum;
2914 #if !(defined(FS2_DEMO) || defined(FS1_DEMO))
2916 // Asteroid_obj_list
2917 for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2918 objp = &Objects[ao->objnum];
2919 evaluate_obj_as_target(objp, &eeo);
2923 return eeo.nearest_objnum; // lowest priority is the closest enemy objnum
2926 // Return timestamp until a ship can find an enemy.
2927 // Yes, no parameters. Based solely on skill level.
2928 int get_enemy_timestamp()
2930 return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2933 // -------------------------------------------------------------------
2934 // Return objnum if enemy found, else return -1;
2935 // Don't attack a ship that already has at least max_attackers attacking it.
2936 int find_enemy(int objnum, float range, int max_attackers)
2938 int enemy_team_mask;
2940 enemy_team_mask = get_enemy_team_mask(objnum);
2942 // if target_objnum != -1, use that as goal.
2943 ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2944 if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2945 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2946 if (aip->target_objnum != -1) {
2947 int target_objnum = aip->target_objnum;
2949 // DKA don't undo object as target in nebula missions.
2950 // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range. (BAD)
2951 if (Objects[target_objnum].signature == aip->target_signature) {
2952 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2953 if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2954 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2955 return target_objnum;
2959 aip->target_objnum = -1;
2960 aip->target_signature = -1;
2963 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2965 aip->target_objnum = -1;
2966 aip->target_signature = -1;
2972 int Use_parent_target = 0;
2973 DCF_BOOL(use_parent_target, Use_parent_target)
2975 // -------------------------------------------------------------------
2976 // Return objnum if enemy found, else return -1;
2979 // turret_subsys => pointer to turret subsystem
2980 // objnum => parent objnum for the turret
2981 // tpos => position of turret (world coords)
2982 // tvec => forward vector of turret (world coords)
2983 // current_enemy => objnum of current turret target
2984 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2986 int enemy_team_mask, enemy_objnum;
2987 model_subsystem *tp;
2990 tp = turret_subsys->system_info;
2991 enemy_team_mask = get_enemy_team_mask(objnum);
2993 // If a small ship and target_objnum != -1, use that as goal.
2994 ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2995 sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2997 if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2998 int target_objnum = aip->target_objnum;
3000 if (Objects[target_objnum].signature == aip->target_signature) {
3001 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
3002 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) { // check this flag as well.
3003 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
3004 return target_objnum;
3008 aip->target_objnum = -1;
3009 aip->target_signature = -1;
3011 // Not small or small with target objnum
3013 // maybe use aip->target_objnum as next target
3014 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
3016 //check if aip->target_objnum is valid target
3017 int target_flags = Objects[aip->target_objnum].flags;
3018 if ( target_flags & OF_PROTECTED ) {
3019 // AL 2-27-98: why is a protected ship being targeted?
3020 set_target_objnum(aip, -1);
3024 // maybe use ship target_objnum if valid for turret
3025 // check for beam weapon and beam protected
3026 if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
3027 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
3028 // check for huge weapon and huge ship
3029 if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
3030 // check for tagged only and tagged ship
3031 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
3032 // select new target if aip->target_objnum is out of field of view
3035 dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
3036 dot = vm_vec_dot(&v2e, tvec);
3037 // MODIFY FOR ATTACKING BIG SHIP
3038 // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
3040 return aip->target_objnum;
3049 enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
3050 if ( enemy_objnum >= 0 ) {
3051 SDL_assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
3052 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
3054 enemy_objnum = aip->target_objnum;
3058 return enemy_objnum;
3061 // If issued an order to a ship that's awaiting repair, abort that process.
3062 // However, do not abort process for an object that is currently being repaired -- let it finish.
3063 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3065 if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3068 if (aip->dock_objnum == -1) {
3071 repair_obj = &Objects[aip->dock_objnum];
3073 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3075 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP); // Might request again after 30 seconds.
3078 void force_avoid_player_check(object *objp, ai_info *aip)
3080 if (Ships[objp->instance].team == Player_ship->team){
3081 aip->avoid_check_timestamp = timestamp(0); // Force a check for collision next frame.
3085 // --------------------------------------------------------------------------
3086 // Set *attacked as object to attack for object *attacker
3087 // If attacked == NULL, then attack any enemy object.
3088 // Attack point *rel_pos on object. This is for supporting attacking subsystems.
3089 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3093 SDL_assert(attacker != NULL);
3094 SDL_assert(attacker->instance != -1);
3095 SDL_assert(Ships[attacker->instance].ai_index != -1);
3097 aip = &Ai_info[Ships[attacker->instance].ai_index];
3098 force_avoid_player_check(attacker, aip);
3100 aip->ok_to_target_timestamp = timestamp(0); // Guarantee we can target.
3102 // if (!SDL_strncasecmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3103 // aip->ai_flags |= AIF_KAMIKAZE;
3104 // aip->ai_flags |= AIF_NO_DYNAMIC;
3107 if (attacker == attacked) {
3108 Int3(); // Bogus! Who tried to get me to attack myself! Trace out and fix!
3112 // Only set to chase if a fighter or bomber, otherwise just return.
3113 if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3114 // nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3116 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3119 // This is how "engage enemy" gets processed
3120 if (attacked == NULL) {
3121 aip->choose_enemy_timestamp = timestamp(0);
3123 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3125 // check if we can see atacked in nebula
3126 if (aip->target_objnum != attacked - Objects) {
3127 aip->aspect_locked_time = 0.0f;
3129 set_target_objnum(aip, attacked - Objects);
3132 ai_set_goal_maybe_abort_dock(attacker, aip);
3133 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
3135 if (is_ignore_object(aip, aip->target_objnum)) {
3136 aip->ignore_objnum = UNUSED_OBJNUM;
3139 aip->mode = AIM_CHASE;
3140 aip->submode = SM_ATTACK; // AL 12-15-97: need to set submode? I got an assert() where submode was bogus
3141 // for AIM_CHASE... it may have been not set correctly here
3143 set_targeted_subsys(aip, NULL, -1);
3144 if (aip->target_objnum != -1) {
3145 //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3146 Objects[aip->target_objnum].flags &= ~OF_PROTECTED; // If ship had been protected, unprotect it.
3149 Int3(); // Not supported yet!
3153 // --------------------------------------------------------------------------
3154 // Set *attacked as object to attack for object *attacker
3155 // Attack point *rel_pos on object. This is for supporting attacking subsystems.
3156 void ai_attack_wing(object *attacker, int wingnum, int priority)
3160 SDL_assert(attacker != NULL);
3161 SDL_assert(attacker->instance != -1);
3162 SDL_assert(Ships[attacker->instance].ai_index != -1);
3164 aip = &Ai_info[Ships[attacker->instance].ai_index];
3166 aip->enemy_wing = wingnum;
3167 aip->mode = AIM_CHASE;
3168 aip->submode = SM_ATTACK; // AL 12-15-97: need to set submode? I got an assert() where submode was bogus
3169 // for AIM_CHASE... it may have been not set correctly here
3171 aip->ok_to_target_timestamp = timestamp(0); // Guarantee we can target.
3173 int count = Wings[wingnum].current_count;
3177 index = (int) (frand() * count);
3182 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3184 ai_set_goal_maybe_abort_dock(attacker, aip);
3185 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
3189 // --------------------------------------------------------------------------
3190 // Set *evaded as object for *evader to evade.
3191 void ai_evade_object(object *evader, object *evaded, int priority)
3195 SDL_assert(evader != NULL);
3196 SDL_assert(evaded != NULL);
3197 SDL_assert(evader->instance != -1);
3198 SDL_assert(Ships[evader->instance].ai_index != -1);
3200 if (evaded == evader) {
3201 Int3(); // Bogus! Who tried to get me to evade myself! Trace out and fix!
3205 aip = &Ai_info[Ships[evader->instance].ai_index];
3207 set_target_objnum(aip, evaded - Objects);
3208 aip->mode = AIM_EVADE;
3212 // Ignore some object without changing mode.
3213 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3217 SDL_assert(ignorer != NULL);
3218 SDL_assert(ignored != NULL);
3219 SDL_assert(ignorer->instance != -1);
3220 SDL_assert(Ships[ignorer->instance].ai_index != -1);
3221 SDL_assert(ignorer != ignored);
3223 aip = &Ai_info[Ships[ignorer->instance].ai_index];
3225 // MK, 5/17/98, removing ignoring of wings.
3226 // It's too confusing. It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3227 /* if (Ships[ignored->instance].wingnum > -1) {
3230 wingnum = Ships[ignored->instance].wingnum;
3231 aip->ignore_objnum = -(wingnum+1);
3232 // set protected bit for each ship in a wing
3233 // MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3234 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3237 objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3238 if (objp != ignored) {
3239 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3243 Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3248 aip->ignore_objnum = ignored - Objects;
3249 aip->ignore_signature = ignored->signature;
3250 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3251 ignored->flags |= OF_PROTECTED; // set protected bit of ignored ship.
3256 // Ignore some object without changing mode.
3257 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3261 SDL_assert(ignorer != NULL);
3262 SDL_assert(ignorer->instance != -1);
3263 SDL_assert(Ships[ignorer->instance].ai_index != -1);
3264 SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3266 aip = &Ai_info[Ships[ignorer->instance].ai_index];
3268 aip->ignore_objnum = -(wingnum +1);
3269 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3273 // Add a path point in the global buffer Path_points.
3274 // modify_index = index in Path_points at which to store path point.
3275 // If modify_index == -1, then create a new point.
3276 // If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3277 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3281 if (modify_index == -1) {
3282 SDL_assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3286 SDL_assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3287 pnp = &Path_points[modify_index];
3291 pnp->path_num = path_num;
3292 pnp->path_index = path_index;
3295 // Given two points on a sphere, the center of the sphere and the radius, return a
3296 // point on the vector through the midpoint of the chord on the sphere.
3297 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3302 vm_vec_add(&tvec, p0, p1);
3303 vm_vec_sub2(&tvec, centerp);
3304 vm_vec_sub2(&tvec, centerp);
3305 if (vm_vec_mag_quick(&tvec) < 0.1f) {
3306 vm_vec_sub(&tvec, p0, p1);
3307 if (fl_abs(tvec.xyz.x) <= fl_abs(tvec.xyz.z)){
3308 tvec.xyz.x = -tvec.xyz.z;
3310 tvec.xyz.y = -tvec.xyz.x;
3314 vm_vec_normalize(&tvec);
3315 vm_vec_scale(&tvec, radius);
3316 vm_vec_add(&new_pnt, centerp, &tvec);
3318 add_path_point(&new_pnt, -1, -1, -1);
3321 // Create a path from the current position to a goal position.
3322 // The current position is in the current object and the goal position is
3323 // in the goal object.
3324 // It is ok to intersect the current object, but not the goal object.
3325 // This function is useful for creating a path to an initial point near a large
3328 // input: subsys_path: optional param (default 0), indicates this is a path to a subsystem
3329 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3331 // If can't cast vector to goalpos, then create an intermediate point.
3332 if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3336 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3337 // trying to avoid. This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3338 // want ships to reach their path destination without flying to points that sit on the radius of
3340 radius = goalobjp->radius;
3342 if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3343 radius = SUBSYS_PATH_DIST;
3347 // The intermediate point is at the intersection of:
3348 // tangent to *goalobjp sphere at point *goalpos
3349 // tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3350 // Note, there are two tangents through *curpos, unless *curpos is on the
3351 // sphere. The tangent that causes the nearer intersection (to *goalpos) is chosen.
3352 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3354 // If we can't reach tan1 from curpos, insert a new point.
3355 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3356 bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3358 add_path_point(&tan1, -1, -1, -1);
3360 // If we can't reach goalpos from tan1, insert a new point.
3361 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3362 bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3367 // Given an object and a model path, globalize the points on the model
3368 // and copy into the global path list.
3369 // If pnp != NULL, then modify, in place, the path points. This is used to create new
3370 // globalized points when the base object has moved.
3371 // input: randomize_pnt => optional parameter (default value -1), add random vector in sphere to this path point
3372 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3377 int pp_index; // index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3378 int start_index, finish_index;
3380 // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3382 // Initialize pp_index.
3383 // If pnp == NULL, that means we're creating new points. If not NULL, then modify in place.
3385 pp_index = -1; // This tells add_path_point to create a new point.
3387 pp_index = 0; // pp_index will get assigned to index in Path_points to reuse.
3389 vm_copy_transpose_matrix(&m, &objp->orient);
3393 finish_index = min(count, mp->nverts);
3395 SDL_assert(dir == -1); // direction must be up by 1 or down by 1 and it's neither!
3396 start_index = mp->nverts-1;
3397 finish_index = max(-1, mp->nverts-1-count);
3401 for (i=start_index; i != finish_index; i += dir) {
3402 // Globalize the point.
3403 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3404 vm_vec_add2(&v1, &objp->pos);
3406 if ( randomize_pnt == i ) {
3408 static_randvec(OBJ_INDEX(objp), &v_rand);
3409 vm_vec_scale(&v_rand, 30.0f);
3410 vm_vec_add2(&v1, &v_rand);
3414 pp_index = pnp-Path_points + offset;
3416 add_path_point(&v1, path_num, i, pp_index);
3422 // For pl_objp, create a path along path path_num into mobjp.
3423 // The tricky part of this problem is creating the entry to the first point on the
3424 // predefined path. The points on this entry path are based on the location of Pl_objp
3425 // relative to the start of the path.
3428 // subsys_path: optional param (default 0), indicating this is a path to a subsystem
3429 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3431 ship *shipp = &Ships[pl_objp->instance];
3432 ai_info *aip = &Ai_info[shipp->ai_index];
3434 ship_info *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3435 polymodel *pm = model_get(osip->modelnum);
3438 pnode *ppfp_start = Ppfp;
3442 SDL_assert(path_num >= 0);
3444 // Do garbage collection if necessary.
3445 if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3446 garbage_collect_path_points();
3450 aip->path_start = Ppfp - Path_points;
3451 SDL_assert(path_num < pm->n_paths);
3453 mp = &pm->paths[path_num];
3454 num_points = mp->nverts;
3456 SDL_assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3458 vm_copy_transpose_matrix(&m, &mobjp->orient);
3459 vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3460 vm_vec_add2(&gp0, &mobjp->pos);
3462 if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3463 vector perim_point1;
3464 vector perim_point2;
3466 perim_point2 = pl_objp->pos;
3468 // If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3469 // Assume it can fly "straight" out to the bounding sphere.
3470 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3471 project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3472 add_path_point(&perim_point2, path_num, -1, -1);
3475 // If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3476 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3477 project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3478 create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3479 add_path_point(&perim_point1, path_num, -1, -1);
3480 } else { // The predefined path extends outside the sphere. Create path to that point.
3481 create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3485 // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3486 if ( subsys_path ) {
3487 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3489 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3492 aip->path_cur = aip->path_start;
3493 aip->path_dir = PD_FORWARD;
3494 aip->path_objnum = mobjp-Objects;
3495 aip->mp_index = path_num;
3496 aip->path_length = Ppfp - ppfp_start;
3497 aip->path_next_check_time = timestamp(1);
3499 aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3501 aip->path_next_create_time = timestamp(1000); // OK to try to create one second later
3502 aip->path_create_pos = pl_objp->pos;
3503 aip->path_create_orient = pl_objp->orient;
3505 aip->ai_flags &= ~AIF_USE_EXIT_PATH; // ensure this flag is cleared
3508 // For pl_objp, create a path along path path_num into mobjp.
3509 // The tricky part of this problem is creating the entry to the first point on the
3510 // predefined path. The points on this entry path are based on the location of pl_objp
3511 // relative to the start of the path.
3512 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3514 ship *shipp = &Ships[pl_objp->instance];
3515 ai_info *aip = &Ai_info[shipp->ai_index];
3517 ship_info *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3518 polymodel *pm = model_get(osip->modelnum);
3521 pnode *ppfp_start = Ppfp;
3523 aip->path_start = Ppfp - Path_points;
3524 SDL_assert(path_num < pm->n_paths);
3526 mp = &pm->paths[path_num];
3527 num_points = mp->nverts;
3529 SDL_assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3531 copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3533 aip->path_cur = aip->path_start;
3534 aip->path_dir = PD_FORWARD;
3535 aip->path_objnum = mobjp-Objects;
3536 aip->mp_index = path_num;
3537 aip->path_length = Ppfp - ppfp_start;
3538 aip->path_next_check_time = timestamp(1);
3540 aip->ai_flags |= AIF_USE_EXIT_PATH; // mark as exit path, referenced in maybe
3543 // Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3545 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3549 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3550 object *objp = &Objects[so->objnum];
3552 if (big_only_flag) {
3553 if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3557 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3558 if (pp_collide(curpos, goalpos, objp, radius))
3559 return OBJ_INDEX(objp);
3566 // Used to create docking paths and other pre-defined paths through ships.
3567 // Creates a path in absolute space.
3568 // Create a path into the object objnum.
3571 // pl_objp: object that will use the path
3572 // objnum: Object to find path to.
3573 // path_num: model path index to use
3574 // exit_flag: true means this is an exit path in the model
3575 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3577 // ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3578 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3580 ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3582 SDL_assert(path_num >= 0);
3584 // This is test code, find an object with paths.
3586 object *objp = &Objects[objnum];
3588 if (objp->type == OBJ_SHIP) {
3591 ship *shipp = &Ships[objp->instance];
3592 pm = model_get( shipp->modelnum );
3593 SDL_assert(pm->n_paths > path_num);
3594 aip->goal_objnum = objp-Objects;
3595 aip->goal_signature = objp->signature;
3597 create_model_exit_path(pl_objp, objp, path_num);
3599 create_model_path(pl_objp, objp, path_num, subsys_path);
3606 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3608 // Maybe make *objp avoid a player object.
3609 // For now, 4/6/98, only check Player_obj.
3610 // If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3611 // Set aip->avoid_goal_point
3612 int maybe_avoid_player(object *objp, vector *goal_pos)
3615 vector cur_pos, new_goal_pos;
3616 object *player_objp;
3617 vector n_vec_to_goal, n_vec_to_player;
3619 aip = &Ai_info[Ships[objp->instance].ai_index];
3621 if (!timestamp_elapsed(aip->avoid_check_timestamp))
3624 player_objp = Player_obj;
3628 // How far two ships could be apart and still collide within one second.
3629 speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3633 obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3635 if (obj_obj_dist > speed_time*2.0f)
3638 cur_pos = objp->pos;
3640 new_goal_pos = *goal_pos;
3642 float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3643 vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3645 if (dist > speed_time*2.0f) {
3646 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3649 if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3650 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3654 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3655 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3656 vm_vec_copy_scale(&avoid_vec, &objp->orient.v.rvec, frand()-0.5f);
3657 vm_vec_scale_add2(&avoid_vec, &objp->orient.v.uvec, frand()-0.5f);
3658 vm_vec_normalize(&avoid_vec);
3661 vm_vec_normalize(&avoid_vec);
3662 vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3663 vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3666 // Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3667 // should fly in to avoid the player while still approaching its goal.
3668 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3670 aip->avoid_check_timestamp = timestamp(1000);
3674 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3675 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3681 // Make object *still_objp enter AIM_STILL mode.
3682 // Make it point at view_pos.
3683 void ai_stay_still(object *still_objp, vector *view_pos)
3688 SDL_assert(still_objp->type == OBJ_SHIP);
3689 SDL_assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3691 shipp = &Ships[still_objp->instance];
3692 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3694 aip = &Ai_info[shipp->ai_index];
3696 aip->mode = AIM_STILL;
3698 // If view_pos not NULL, point at that point. Else, point at a point directly in front of ship. Ie, don't turn.
3699 if (view_pos != NULL)
3700 aip->goal_point = *view_pos;
3702 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.v.fvec, 100.0f);
3705 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3706 // when two objects have completed docking. used because we can dock object initially at misison load
3707 // time (meaning that ai_dock() might never get called). docker has docked with dockee (i.e. docker
3708 // would be a freighter and dockee would be a cargo).
3709 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3711 ai_info *aip, *other_aip;
3713 aip = &Ai_info[Ships[docker->instance].ai_index];
3714 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3716 // set the flags and dock_objnum for both objects
3717 aip->ai_flags |= AIF_DOCKED;
3718 aip->dock_objnum = OBJ_INDEX(dockee);
3719 other_aip->ai_flags |= AIF_DOCKED;
3720 other_aip->dock_objnum = OBJ_INDEX(docker);
3721 aip->dock_signature = dockee->signature;
3722 other_aip->dock_signature = docker->signature;
3724 // add multiplayer hook here to deal with docked objects. We need to only send information
3725 // about the object that is docking. Both flags will get updated.
3726 if ( MULTIPLAYER_MASTER )
3727 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3731 // code which is called when objects become undocked. Equivalent of above function.
3732 // dockee might not be valid since this code can get called to cleanup after a ship
3734 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3736 ai_info *aip, *other_aip;
3738 // add multiplayer hook here to deal with undocked objects. Do it before we
3739 // do anything else. We don't need to send info for both objects, since we can find
3740 // it be dock_objnum
3741 if ( MULTIPLAYER_MASTER )
3742 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3744 aip = &Ai_info[Ships[docker->instance].ai_index];
3746 // set the flags and dock_objnum for both objects
3747 aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3748 aip->dock_objnum = -1;
3750 if ( dockee != NULL ) {
3751 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3752 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3753 other_aip->dock_objnum = -1;
3759 // --------------------------------------------------------------------------
3760 // Interface from goals code to AI.
3761 // Cause *docker to dock with *dockee.
3762 // priority is priority of goal from goals code.
3764 // AIDO_DOCK set goal of docking
3765 // AIDO_DOCK_NOW immediately dock, used for ships that need to be docked at mission start
3766 // AIDO_UNDOCK set goal of undocking
3767 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3771 ai_info *dockee_aip;
3773 SDL_assert(docker != NULL);
3774 SDL_assert(dockee != NULL);
3775 SDL_assert(docker->instance != -1);
3776 SDL_assert(Ships[docker->instance].ai_index != -1);
3777 SDL_assert(Ships[dockee->instance].ai_index != -1);
3778 SDL_assert( docker_index != -1 );
3779 SDL_assert( dockee_index != -1 );
3781 aip = &Ai_info[Ships[docker->instance].ai_index];
3783 if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3785 int docker_index2, dockee_index2;
3787 SDL_assert(aip->dock_objnum > -1);
3788 dockee2 = &Objects[aip->dock_objnum];
3789 docker_index2 = aip->dock_index;
3790 dockee_index2 = aip->dockee_index;
3791 // MWA -- 2/9/98. use the goal code to undock the ships since goals might need to get removed
3792 // and that code will do it properly. I'd actually be surprised if we got into this code anymore
3793 // since the outer layer goal code should deal with this issue....but who knows...
3794 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3797 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3798 nprintf(("AI", "Ship %s told to dock with %s, but it was already docked with %s.\n", Ships[docker->instance].ship_name, Ships[dockee->instance].ship_name, Ships[Objects[aip->dock_objnum].instance].ship_name));
3799 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3803 dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3805 aip->goal_objnum = dockee - Objects;
3806 aip->goal_signature = dockee->signature;
3808 aip->mode = AIM_DOCK;
3810 switch (dock_type) {
3812 aip->submode = AIS_DOCK_0;
3815 aip->submode = AIS_DOCK_3A;
3818 aip->submode = AIS_UNDOCK_0;
3821 Int3(); // Bogus dock_type.
3824 aip->submode_start_time = Missiontime;
3825 aip->dock_index = docker_index;
3826 aip->dockee_index = dockee_index;
3828 dockee_aip->dock_index = dockee_index;
3829 dockee_aip->dockee_index = docker_index;
3831 // get the path number to the docking point on the dockee. Each docking point contains a list
3832 // of paths that the point can be reached by. Pick the first path in the path list for now.
3833 // We only want to do this stuff if we are docking!!! Be sure to set the path index
3834 if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3835 pm = model_get( Ships[dockee->instance].modelnum );
3836 SDL_assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3838 // only set the dock path index if we are docking. undocking will assume that dock_path_index
3839 // already set from some other docking command
3840 aip->dock_path_index = dockee_index;
3841 dockee_aip->dock_path_index = docker_index;
3844 if (dock_type != AIDO_DOCK_NOW) {
3846 // Note: Second parameter is dock path index. This should be specified as an
3847 // _input_ to this function and passed through. The path index should be already
3848 // set for the undock function
3849 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3850 ai_find_path(docker, dockee-Objects, path_num, 0);
3851 // ai_find_path(dockee-Objects, dockee_index, 0);
3853 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3854 //aip->dock_objnum = OBJ_INDEX(dockee);
3855 ai_do_objects_docked_stuff( docker, dockee );
3860 // Cause a ship to fly its waypoints.
3862 // WPF_REPEAT Set -> repeat waypoints.
3863 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3867 SDL_assert(waypoint_list_index < Num_waypoint_lists);
3869 //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3870 aip = &Ai_info[Ships[objp->instance].ai_index];
3872 if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3875 aip->ai_flags |= AIF_FORMATION_WING;
3876 aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3877 aip->wp_list = waypoint_list_index;
3879 aip->wp_flags = wp_flags;
3880 aip->mode = AIM_WAYPOINTS;
3882 SDL_assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3885 // Make *objp stay within dist units of *other_objp
3886 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3890 SDL_assert(objp != other_objp); // Bogus! Told to stay near self.
3891 SDL_assert(objp->type == OBJ_SHIP);
3892 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3894 aip = &Ai_info[Ships[objp->instance].ai_index];
3896 aip->mode = AIM_STAY_NEAR;
3898 aip->stay_near_distance = dist;
3899 aip->goal_objnum = other_objp-Objects;
3900 aip->goal_signature = other_objp->signature;
3904 // Make object *objp form on wing of object *goal_objp
3905 void ai_form_on_wing(object *objp, object *goal_objp)
3911 // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3912 // out for this case.
3913 if ( Game_mode & GM_MULTIPLAYER ) {
3914 if ( objp == goal_objp ) {
3919 SDL_assert(objp != goal_objp); // Bogus! Told to form on own's wing!
3921 shipp = &Ships[objp->instance];
3922 sip = &Ship_info[shipp->ship_info_index];
3924 // Only fighters or bombers allowed to form on wing.
3925 if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3926 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3930 aip = &Ai_info[Ships[objp->instance].ai_index];
3932 aip->ai_flags &= ~AIF_FORMATION_WING;
3933 aip->ai_flags |= AIF_FORMATION_OBJECT;
3935 aip->goal_objnum = goal_objp-Objects;
3936 ai_set_goal_maybe_abort_dock(objp, aip);
3937 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4); // Super extra long time until can target another ship.
3941 // Given an object and an object on whose wing to form, return slot to use.
3943 // This function is called per object in formation per frame. Should store slot in ai_info struct.
3944 int ai_formation_object_get_slotnum(int objnum, object *objp)
3946 int slotnum = 1; // Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3949 for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3952 else if (o->type == OBJ_SHIP)
3953 if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3954 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3958 SDL_assert(o != END_OF_LIST(&obj_used_list)); // Didn't find objp in list of used ships. Impossible!
3963 #define BIGNUM 100000.0f
3967 // Given an attacker's position and a target's position and velocity, compute the time of
3968 // intersection of a weapon fired by the attacker with speed weapon_speed.
3969 // Return this value. Return value of 0.0f means no collision is possible.
3970 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3972 vector vec_to_target;
3977 vm_vec_sub(&vec_to_target, targpos, attackpos);
3978 pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3979 vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3980 discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3982 if (discrim > 0.0f) {
3983 float t1, t2, t_solve;
3985 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3986 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3992 if ((t2 > 0.0f) && (t2 < t_solve))
3995 if (t_solve < BIGNUM-1.0f) {
3996 return t_solve + Debug_k * flFrametime;
4004 // --------------------------------------------------------------------------
4005 // If far away, use player's speed.
4006 // If in between, lerp between player and laser speed
4007 // If close, use laser speed.
4008 // Want to know how much time it will take to get to the enemy.
4009 // This function doesn't account for the fact that by the time the player
4010 // (or his laser) gets to the current enemy position, the enemy will have moved.
4011 // This is dealt with in polish_predicted_enemy_pos.
4012 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
4014 float time_to_enemy;
4015 float pl_speed = pobjp->phys_info.speed;
4016 float max_laser_distance, max_laser_speed;
4017 int bank_num, weapon_num;
4018 ship *shipp = &Ships[pobjp->instance];
4020 bank_num = shipp->weapons.current_primary_bank;
4021 weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
4022 max_laser_speed = Weapon_info[weapon_num].max_speed;
4023 max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
4025 // If pretty far away, use player's speed to predict position, else
4026 // use laser's speed because when close, we care more about hitting
4027 // with a laser than about causing ship:ship rendezvous.
4028 if (dist_to_enemy > 1.5 * max_laser_distance) {
4029 if (pl_speed > 0.0f)
4030 time_to_enemy = dist_to_enemy/pl_speed;
4032 time_to_enemy = 1.0f;
4033 } else if (dist_to_enemy > 1.1*max_laser_distance) {
4034 if (pl_speed > 0.1f) {
4037 scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
4039 time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
4041 time_to_enemy = 2.0f;
4043 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
4045 // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
4046 return time_to_enemy + flFrametime;
4049 // Stuff *dot and *tts.
4050 // *dot is always computed. If dot is less than zero, the magnitude is
4051 // incorrect, not having been divided by distance.
4052 // If *dot is > 0.0f, then tts is computed. This is the time it will take object
4053 // *objp to get to *pos, assuming it moves right at it.
4054 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
4058 vm_vec_sub(&v2s, pos, &objp->pos);
4059 *dot = vm_vec_dot(&v2s, &objp->orient.v.fvec);
4064 dist = vm_vec_dist(&objp->pos, pos);
4071 if (objp->phys_info.speed > 0.1f)
4072 *tts = dist / objp->phys_info.speed;
4074 *tts = dist * 100.0f;
4079 // Return index of weapon that could hit object *sobjp within dtime seconds.
4080 // Actual time until impact returned in *atime.
4081 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4083 object *objp, *best_objp = NULL;
4084 float best_tts = 1000.0f;
4086 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4087 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4089 // vector psp; // Predicted ship position.
4091 // Get dot and time to current ship position.
4092 fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4094 // If dot and tts are in plausible range, do more expensive stuff.
4096 // float dot_from_sobjp;
4099 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4100 // dot_from_sobjp = vm_vec_dot(&sobjp->orient.v.fvec, &v2e);
4101 // if (dot_from_sobjp >= dot_threshhold)
4103 if (tts < best_tts) {
4114 if (best_objp != NULL)
4115 return best_objp-Objects;
4121 // --------------------------------------------------------------------------
4122 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4124 *player_pos = pl_objp->pos;
4126 if (aip->next_predict_pos_time > Missiontime) {
4127 *enemy_pos = aip->last_predicted_enemy_pos;
4129 *enemy_pos = en_objp->pos;
4131 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4132 aip->last_predicted_enemy_pos = *enemy_pos;
4138 // --------------------------------------------------------------------------
4139 int find_nearest_waypoint(object *objp)
4142 float dist, min_dist, dot;
4148 shipp = &Ships[objp->instance];
4149 wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4150 SDL_assert(wp_listnum > 0);
4151 wpl = &Waypoint_lists[wp_listnum];
4153 min_dist = 999999.0f;
4156 for (i=0; i<wpl->count; i++) {
4157 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4158 dot = vm_vec_dot_to_point(&objp->orient.v.fvec, &objp->pos, &wpl->waypoints[i]);
4159 dist = (float) (dist * (1.25 - dot));
4160 if (dist < min_dist) {
4166 SDL_assert(min_ind != -1);
4171 // Given an ai_info struct, by reading current goal and path information,
4172 // extract base path information and return in pmp and pmpv.
4173 // Return true if found, else return false.
4174 // false means the current point is not on the original path.
4175 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4177 pnode *pn = &Path_points[path_cur];
4178 ship_info *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4179 polymodel *pm = model_get(sip->modelnum);
4180 //static int debug_last_index = -1; // no longer used
4184 if (pn->path_num != -1) {
4185 *pmp = &pm->paths[pn->path_num];
4186 if (pn->path_index != -1)
4187 *pmpv = &(*pmp)->verts[pn->path_index];
4193 /* if (debug_last_index != *pmpv-(*pmp)->verts) {
4194 debug_last_index = *pmpv-(*pmp)->verts;
4195 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4196 for (int i=0; i<(*pmpv)->nturrets; i++) {
4197 nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4199 nprintf(("AI", "\n"));
4205 // Modify, in place, the points in a global model path.
4206 // Only modify those points that are defined in the model path. Don't modify the
4207 // leadin points, such as those that are necessary to get the model on the path.
4208 void modify_model_path_points(object *objp)
4210 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
4211 object *mobjp = &Objects[aip->path_objnum];
4212 ship_info *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4213 polymodel *pm = model_get(osip->modelnum);
4217 SDL_assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4219 pnp = &Path_points[aip->path_start];
4220 while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4223 path_num = pnp->path_num;
4224 SDL_assert((path_num >= 0) && (path_num < pm->n_paths));
4226 SDL_assert(pnp->path_index != -1); // If this is -1, that means we never found the model path points
4229 if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4233 copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4236 // Return an indication of the distance between two matrices.
4237 // This is the sum of the distances of their dot products from 1.0f.
4238 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4242 t = 1.0f - vm_vec_dot(&mat1->v.fvec, &mat2->v.fvec);
4243 t += 1.0f - vm_vec_dot(&mat1->v.uvec, &mat2->v.uvec);
4244 t += 1.0f - vm_vec_dot(&mat1->v.rvec, &mat2->v.rvec);
4250 // Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4251 // This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4252 // prevents this from happening too often.
4253 // force_recreate_flag TRUE means to recreate regardless of timestamp.
4254 // Returns TRUE if path recreated.
4255 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4259 SDL_assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4261 if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4262 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4263 force_recreate_flag = 1;
4265 // If no path, that means we don't need one.
4266 if (aip->path_start == -1)
4269 // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate. This is needed when ships
4270 // emerge from fighter bays. We don't need to recreate the path.. and in case the
4271 // parent ship dies, we still want to be able to continue on the path
4272 if ( aip->ai_flags & AIF_USE_STATIC_PATH )
4275 if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4278 path_objp = &Objects[aip->path_objnum];
4280 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4283 dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4284 dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4286 if (force_recreate_flag || (dist > 2.0f)) {
4287 aip->path_next_create_time = timestamp(1000); // Update again in as little as 1000 milliseconds, ie 1 second.
4288 aip->path_goal_obj_hash = hashval;
4289 modify_model_path_points(objp);
4291 aip->path_create_pos = path_objp->pos;
4292 aip->path_create_orient = path_objp->orient;
4302 // Set acceleration for ai_dock().
4303 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)
4305 float prev_dot_to_goal = aip->prev_dot_to_goal;
4307 aip->prev_dot_to_goal = dot;
4309 if (objp->phys_info.speed < 0.0f) {
4310 accelerate_ship(aip, 1.0f/32.0f);
4311 } else if ((prev_dot_to_goal-dot) > 0.01) {
4312 if (prev_dot_to_goal > dot + 0.05f) {
4313 accelerate_ship(aip, 0.0f);
4315 change_acceleration(aip, -1.0f); // -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4318 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4319 set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4320 //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4321 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4322 if (dist_to_goal > 200.0f)
4323 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4327 xdot = (dot_to_next + dot)/2.0f;
4331 // AL: if following a path not in dock mode, move full speed
4332 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4333 set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4335 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4336 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4337 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4339 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4346 xdot = max(dot_to_next, 0.1f);
4347 if ( aip->mode != AIM_DOCK ) {
4348 set_accel_for_target_speed(objp, sip->max_speed);
4351 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4352 speed = dist_to_goal/8.0f + 2.0f;
4353 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4354 speed = dist_to_goal/4.0f + 4.0f;
4356 speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4358 if (aip->mode == AIM_DOCK) {
4359 speed = speed * 2.0f + 1.0f;
4360 if (aip->goal_objnum != -1) {
4361 speed += Objects[aip->goal_objnum].phys_info.speed;
4365 set_accel_for_target_speed(objp, speed);
4371 // --------------------------------------------------------------------------
4372 // Follow a path associated with a large object, such as a capital ship.
4373 // The points defined on the path are in the object's reference frame.
4374 // The object of interest is goal_objnum.
4375 // The paths are defined in the model. The path of interest is wp_list.
4376 // The next goal point in the path is wp_index.
4377 // wp_flags contain special information specific to the path.
4379 // The path vertices are defined by model_path structs:
4380 // typedef struct model_path {
4381 // char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
4386 // The polymodel struct for the object contains the following:
4388 // model_path *paths;
4390 // Returns distance to goal point.
4394 int num_paths, num_points;
4395 float dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4396 ship *shipp = &Ships[Pl_objp->instance];
4397 ship_info *sip = &Ship_info[shipp->ship_info_index];
4400 float mag, prev_dot_to_goal;
4401 vector temp_vec, *slop_vec;
4404 vector *cvp, *nvp, next_vec, gcvp, gnvp; // current and next vertices in global coordinates.
4406 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4408 SDL_assert(aip->goal_objnum != -1);
4409 SDL_assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4411 gobjp = &Objects[aip->goal_objnum];
4412 gshipp = &Ships[gobjp->instance];
4414 pm = model_get( gshipp->modelnum );
4415 num_paths = pm->n_paths;
4416 SDL_assert(num_paths > 0);
4418 if (aip->path_start == -1) {
4420 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4421 SDL_assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4422 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4425 // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4427 maybe_recreate_path(Pl_objp, aip, 0);
4429 num_points = aip->path_length;
4431 // Set cvp and nvp as pointers to current and next vertices of interest on path.
4432 cvp = &Path_points[aip->path_cur].pos;
4433 if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4434 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4436 // If this is 0, then path length must be 1 which means we have no direction!
4437 SDL_assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4438 // Cleanup for above SDL_assert() which we hit too near release. -- MK, 5/24/98.
4439 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4440 if (aip->path_dir == 1)
4441 aip->path_cur = aip->path_start;
4443 aip->path_cur = aip->path_start + num_points - 1;
4447 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4448 vm_vec_normalize(&delvec);
4449 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4453 // Interrupt if can't get to current goal point. Debug only.
4454 /* if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4458 // See if can reach next point (as opposed to current point)
4459 // However, don't do this if docking and next point is last point.
4460 // That is, we don't want to pursue the last point under control of the
4461 // path code. In docking, this is a special hack.
4462 if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4463 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4464 if ( timestamp_elapsed(aip->path_next_check_time)) {
4465 aip->path_next_check_time = timestamp( 3000 );
4466 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4468 aip->path_cur += aip->path_dir;
4469 nvp = &Path_points[aip->path_cur].pos;
4470 //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4479 speed = Pl_objp->phys_info.speed;
4481 dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4482 dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4483 // Can't use fvec, need to use velocity vector because we aren't necessarily
4484 // moving in the direction we're facing.
4486 // if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4487 if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4489 vm_vec_zero(&nvel_vec);
4491 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4493 // If moving not-very-slowly and sliding, then try to slide at goal, rather than
4497 nvel_vec = Pl_objp->orient.v.fvec;
4498 else if (mag > 5.0f) {
4500 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4501 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4502 slop_vec = &temp_vec;
4503 vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4507 if (dist_to_goal > 0.1f)
4508 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4510 // Code to control speed is MUCH less forgiving in path following than in waypoint
4511 // following. Must be very close to path or might hit objects.
4512 prev_dot_to_goal = aip->prev_dot_to_goal;
4513 dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4514 dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4516 set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4517 aip->prev_dot_to_goal = dot;
4519 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4521 // If moving at a non-tiny velocity, detect attaining path point by its being close to
4522 // line between previous and current object location.
4523 if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4524 vector nearest_point;
4525 float r, min_dist_to_goal;
4527 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4529 // Set min_dist_to_goal = how close must be to waypoint to pick next one.
4530 // If docking and this is the second last waypoint, must be very close.
4531 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4532 min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4534 min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4536 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4537 (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius)))) {
4538 aip->path_cur += aip->path_dir;
4539 //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4540 if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4541 SDL_assert(aip->mode != AIM_DOCK); // If docking, should never get this far, getting to last point handled outside ai_path()
4542 aip->path_dir = -aip->path_dir;
4543 // aip->path_cur += aip->path_dir;
4548 return dist_to_goal;
4551 void update_min_max(float val, float *min, float *max)
4555 else if (val > *max)
4559 // Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4560 // Stuff ni min_vec and max_vec.
4561 // Return value: Number of enemy objects in bounding box.
4562 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4568 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4569 objp = &Objects[so->objnum];
4570 if (Ships[objp->instance].team & enemy_team_mask) {
4571 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER | SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_DRYDOCK | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER))
4572 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4574 *min_vec = objp->pos;
4575 *max_vec = objp->pos;
4578 update_min_max(objp->pos.xyz.x, &min_vec->xyz.x, &max_vec->xyz.x);
4579 update_min_max(objp->pos.xyz.y, &min_vec->xyz.y, &max_vec->xyz.y);
4580 update_min_max(objp->pos.xyz.z, &min_vec->xyz.z, &max_vec->xyz.z);
4590 // Pick a relatively safe spot for objp to fly to.
4592 // Finds a spot away from any enemy within a bounding box.
4593 // Doesn't verify that "safe spot" is not near some other enemy.
4594 void ai_safety_pick_spot(object *objp)
4597 int enemy_team_mask;
4598 vector min_vec, max_vec;
4599 vector vec_to_center, center;
4602 objnum = OBJ_INDEX(objp);
4604 enemy_team_mask = get_enemy_team_mask(objnum);
4606 if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4607 vm_vec_avg(¢er, &min_vec, &max_vec);
4608 vm_vec_normalized_dir(&vec_to_center, ¢er, &objp->pos);
4610 vm_vec_scale_add(&goal_pos, ¢er, &vec_to_center, 2000.0f);
4612 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.v.fvec, 100.0f);
4614 Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4617 // Fly to desired safe point.
4618 // Returns distance to that point.
4619 float ai_safety_goto_spot(object *objp)
4627 sip = &Ship_info[Ships[objp->instance].ship_info_index];
4629 aip = &Ai_info[Ships[objp->instance].ai_index];
4630 dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4631 dot = vm_vec_dot(&vec_to_goal, &objp->orient.v.fvec);
4633 dot_val = (1.1f + dot) / 2.0f;
4634 if (dist > 200.0f) {
4635 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4637 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4642 void ai_safety_circle_spot(object *objp)
4648 sip = &Ship_info[Ships[objp->instance].ship_info_index];
4650 goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4651 dot = turn_towards_tangent(objp, &goal_point, 250.0f); // Increased from 50 to 250 to make circling not look so wacky.
4653 set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4655 // float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4656 // nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f. Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z, dist));
4660 // --------------------------------------------------------------------------
4665 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4667 switch (aip->submode) {
4669 ai_safety_pick_spot(Pl_objp);
4670 aip->submode = AISS_2;
4671 aip->submode_start_time = Missiontime;
4673 case AISS_1a: // Pick a safe point because we just got whacked!
4677 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4678 aip->submode = AISS_3;
4679 aip->submode_start_time = Missiontime;
4683 ai_safety_circle_spot(Pl_objp);
4686 Int3(); // Illegal submode for ai_safety();
4691 // --------------------------------------------------------------------------
4692 // make Pl_objp fly waypoints.
4696 vector *wp_cur, *wp_next;
4697 float dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4698 ship *shipp = &Ships[Pl_objp->instance];
4699 ship_info *sip = &Ship_info[shipp->ship_info_index];
4704 float prev_dot_to_goal;
4708 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4710 wp_index = aip->wp_index;
4712 if (wp_index == -1) {
4713 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4714 wp_index = aip->wp_index;
4718 wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4720 SDL_assert(wpl->count); // What? Is this zero? Probably wp_index never got initialized!
4722 wp_cur = &wpl->waypoints[wp_index];
4723 wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4724 speed = Pl_objp->phys_info.speed;
4726 dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4727 dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4729 // Can't use fvec, need to use velocity vector because we aren't necessarily
4730 // moving in the direction we're facing.
4731 // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4732 // If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4733 // if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4734 if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4736 vm_vec_zero(&nvel_vec);
4738 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4741 // If moving not-very-slowly and sliding, then try to slide at goal, rather than
4745 nvel_vec = Pl_objp->orient.v.fvec;
4746 } else if (mag > 5.0f) {
4748 nv_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4749 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4750 slop_vec = &temp_vec;
4751 vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.v.fvec);
4755 // If a wing leader, take turns more slowly, based on size of wing.
4758 if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4759 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4760 scale = (int) ((scale+1)/2);
4765 if (dist_to_goal > 0.1f) {
4766 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4769 prev_dot_to_goal = aip->prev_dot_to_goal;
4770 dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4771 dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4772 aip->prev_dot_to_goal = dot;
4774 // If there is no next point on the path, don't care about dot to next.
4775 if (wp_index + 1 >= wpl->count) {
4779 // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4781 if (Pl_objp->phys_info.speed < 0.0f) {
4782 accelerate_ship(aip, 1.0f/32);
4783 } else if (prev_dot_to_goal > dot+0.01f) {
4784 // We are further from pointing at our goal this frame than last frame, so slow down.
4785 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4786 } else if (dist_to_goal < 100.0f) {
4787 float slew_dot = vm_vec_dot(&Pl_objp->orient.v.fvec, &nvel_vec);
4788 if (fl_abs(slew_dot) < 0.9f) {
4789 accelerate_ship(aip, 0.0f);
4790 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4791 accelerate_ship(aip, 0.0f);
4793 accelerate_ship(aip, 0.5f * dot * dot);
4797 if (dist_to_goal < 250.0f) {
4798 dot1 = dot*dot*dot; // Very important to be pointing towards goal when nearby. Note, cubing preserves sign.
4807 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4813 if (sip->flags & SIF_SMALL_SHIP) {
4814 set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4816 set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4820 // Make sure not travelling too fast for someone to keep up.
4821 float max_allowed_speed = 9999.9f;
4823 if (shipp->wingnum != -1) {
4824 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4827 // check if waypoint speed cap is set and adjust max speed
4828 if (aip->waypoint_speed_cap > 0) {
4829 max_allowed_speed = (float) aip->waypoint_speed_cap;
4832 if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4833 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4836 if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4837 vector nearest_point;
4840 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4842 if ( (vm_vec_dist_quick(&Pl_objp->pos, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius) + vm_vec_dist_quick(&Pl_objp->pos, &Pl_objp->last_pos))) ||
4843 (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius))))) {
4845 if (wp_index >= wpl->count) {
4846 if (aip->wp_flags & WPF_REPEAT) {
4851 // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4852 // we must be careful when dealing with wings. A ship in a wing might be completing
4853 // a waypoint for for the entire wing, or it might be completing a goal for itself. If
4854 // for itself and in a wing, treat the completion as we would a ship
4856 if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4859 // I don't think that you can fly waypoints as dynamic goals!!!
4860 // -- This is legal, just stupid. -- SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4862 // Clean up from above SDL_assert, just in case we ship without fixing it. (Encountered by JimB on 2/9/98)
4863 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4864 aip->mode = AIM_NONE;
4865 Int3(); // Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4868 type = aip->goals[aip->active_goal].type;
4869 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4876 // if the ship is not in a wing, remove the goal and continue on
4877 if ( treat_as_ship ) {
4878 ai_mission_goal_complete( aip ); // this call should reset the AI mode
4879 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4881 // this ship is in a wing. We must mark the goal as being completed for all ships
4882 // in the wing. We will also mark an entry in the log that the wing completed the goal
4883 // not the individual ship.
4884 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4885 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4887 //wp_index = wpl->count-1;
4891 aip->wp_index = wp_index;
4896 // Make Pl_objp avoid En_objp
4897 // Not like evading. This is for avoiding a collision!
4898 // Note, use sliding if available.
4901 // To avoid an object, turn towards right or left vector until facing away from object.
4902 // To choose right vs. left, pick one that is further from center of avoid object.
4903 // Keep turning away from until pointing away from ship.
4904 // Stay in avoid mode until at least 3 enemy ship radii away.
4907 // If inside sphere, zero speed and turn towards outside.
4908 // If outside sphere, inside 2x sphere, set speed percent of max to:
4909 // max(away_dot, (dist-rad)/rad)
4910 // where away_dot is dot(Pl_objp->v.fvec, vec_En_objp_to_Pl_objp)
4912 vector vec_to_enemy;
4915 ship *shipp = &Ships[Pl_objp->instance];
4916 ship_info *sip = &Ship_info[shipp->ship_info_index];
4917 ai_info *aip = &Ai_info[shipp->ai_index];
4918 vector player_pos, enemy_pos;
4920 // if we're avoiding a stealth ship, then we know where he is, update with no error
4921 if ( is_object_stealth_ship(En_objp) ) {
4922 update_ai_stealth_info_with_error(aip/*, 1*/);
4925 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4926 vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4928 dist = vm_vec_normalize(&vec_to_enemy);
4929 away_dot = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
4931 if ((sip->max_vel.xyz.x > 0.0f) || (sip->max_vel.xyz.y > 0.0f)) {
4932 if (vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_to_enemy) > 0.0f) {
4933 AI_ci.sideways = -1.0f;
4935 AI_ci.sideways = 1.0f;
4937 if (vm_vec_dot(&Pl_objp->orient.v.uvec, &vec_to_enemy) > 0.0f) {
4938 AI_ci.vertical = -1.0f;
4940 AI_ci.vertical = 1.0f;
4944 //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4945 // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4947 // If in front of enemy, turn away from it.
4948 // If behind enemy, try to get fully behind it.
4949 if (away_dot < 0.0f) {
4950 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4954 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f);
4955 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4959 float radsum = Pl_objp->radius + En_objp->radius;
4962 accelerate_ship(aip, max(away_dot, 0.2f));
4963 else if (dist < 2*radsum)
4964 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4966 accelerate_ship(aip, 1.0f);
4970 // Maybe it's time to resume the previous AI mode in aip->previous_mode.
4971 // Each type of previous_mode has its own criteria on when to resume.
4972 // Return true if previous mode was resumed.
4973 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4975 // Only (maybe) resume previous goal if current goal is dynamic.
4976 if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4979 if (aip->mode == AIM_EVADE_WEAPON) {
4980 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4981 SDL_assert(aip->previous_mode != AIM_EVADE_WEAPON);
4982 aip->mode = aip->previous_mode;
4983 aip->submode = aip->previous_submode;
4984 aip->submode_start_time = Missiontime;
4985 aip->active_goal = AI_GOAL_NONE;
4986 aip->mode_time = -1; // Means do forever.
4989 } else if ( aip->previous_mode == AIM_GUARD) {
4990 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4994 guard_objp = &Objects[aip->guard_objnum];
4995 dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4997 // If guarding ship is far away from guardee and enemy is far away from guardee,
4998 // then stop chasing and resume guarding.
4999 if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
5000 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
5001 if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
5002 SDL_assert(aip->previous_mode == AIM_GUARD);
5003 aip->mode = aip->previous_mode;
5004 aip->submode = AIS_GUARD_PATROL;
5005 aip->active_goal = AI_GOAL_NONE;
5017 // Call this function if you want something to happen on average every N quarters of a second.
5018 // The truth value returned by this function will be the same for any given quarter second interval.
5019 // The value "num" is only passed in to get asynchronous behavior for different objects.
5020 // modulus == 1 will always return true.
5021 // modulus == 2 will return true half the time.
5022 // modulus == 16 will return true for one quarter second interval every four seconds.
5023 int static_rand_timed(int num, int modulus)
5030 t = Missiontime >> 18; // Get time in quarters of a second
5033 return !(t % modulus);
5037 // Maybe fire afterburner based on AI class
5038 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
5040 if (aip->ai_class == 0)
5041 return 0; // Lowest level never aburners away
5043 // Maybe don't afterburner because of a potential collision with the player.
5044 // If not multiplayer, near player and player in front, probably don't afterburner.
5045 if (!(Game_mode & GM_MULTIPLAYER)) {
5046 if (Ships[objp->instance].team == Player_ship->team) {
5049 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
5050 if (dist < 150.0f) {
5054 vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
5055 dot = vm_vec_dot(&v2p, &objp->orient.v.fvec);
5058 if (dot * dist > 50.0f)
5065 if (aip->ai_class >= Num_ai_classes-2)
5066 return 1; // Highest two levels always aburner away.
5068 return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5073 // Maybe engage afterburner after being hit by an object.
5074 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5076 // Only do if facing a little away.
5077 if (en_objp != NULL) {
5080 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5081 if (vm_vec_dot(&v2e, &objp->orient.v.fvec) > -0.5f)
5085 if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5086 if (ai_maybe_fire_afterburner(objp, aip)) {
5087 afterburners_start(objp);
5088 aip->afterburner_stop_time = Missiontime + F1_0/2;
5093 // Return true if object *objp is an instructor.
5094 // Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5095 int is_instructor(object *objp)
5097 return !SDL_strncasecmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5100 // Evade the weapon aip->danger_weapon_objnum
5101 // If it's not valid, do a quick out.
5102 // Evade by accelerating hard.
5103 // If necessary, turn hard left or hard right.
5106 object *weapon_objp = NULL;
5107 object *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5108 vector weapon_pos, player_pos, goal_point;
5109 vector vec_from_enemy;
5110 float dot_from_enemy, dot_to_enemy;
5112 ship *shipp = &Ships[Pl_objp->instance];
5113 ai_info *aip = &Ai_info[shipp->ai_index];
5115 if (is_instructor(Pl_objp))
5118 // Make sure we're actually being attacked.
5119 // Favor locked objects.
5120 if (aip->nearest_locked_object != -1) {
5121 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5122 locked_weapon_objp = &Objects[aip->nearest_locked_object];
5125 if (aip->danger_weapon_objnum != -1) {
5126 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature) {
5127 unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5129 aip->danger_weapon_objnum = -1; // Signatures don't match, so no longer endangered.
5133 if (locked_weapon_objp != NULL) {
5134 if (unlocked_weapon_objp != NULL) {
5135 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))
5136 weapon_objp = locked_weapon_objp;
5138 weapon_objp = unlocked_weapon_objp;
5140 weapon_objp = locked_weapon_objp;
5141 } else if (unlocked_weapon_objp != NULL)
5142 weapon_objp = unlocked_weapon_objp;
5144 if (aip->mode == AIM_EVADE_WEAPON)
5145 maybe_resume_previous_mode(Pl_objp, aip);
5149 SDL_assert(weapon_objp != NULL);
5151 if (weapon_objp->type != OBJ_WEAPON) {
5152 if (aip->mode == AIM_EVADE_WEAPON)
5153 maybe_resume_previous_mode(Pl_objp, aip);
5157 weapon_pos = weapon_objp->pos;
5158 player_pos = Pl_objp->pos;
5160 // Make speed based on skill level, varying at highest skill level, which is harder to hit.
5161 accelerate_ship(aip, 1.0f);
5163 dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5165 dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_from_enemy);
5166 dot_from_enemy = vm_vec_dot(&weapon_objp->orient.v.fvec, &vec_from_enemy);
5167 //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5169 // If shot is incoming...
5170 if (dot_from_enemy < 0.3f) {
5171 if (weapon_objp == unlocked_weapon_objp)
5172 aip->danger_weapon_objnum = -1;
5174 } else if (dot_from_enemy > 0.7f) {
5175 if (dist < 200.0f) {
5176 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5177 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5178 //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5179 afterburners_start(Pl_objp);
5180 aip->afterburner_stop_time = Missiontime + F1_0/2;
5185 // If we're sort of pointing towards it...
5186 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5189 // Turn hard left or right, depending on which gets out of way quicker.
5190 rdot = vm_vec_dot(&Pl_objp->orient.v.rvec, &vec_from_enemy);
5192 if ((rdot < -0.5f) || (rdot > 0.5f))
5193 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, -200.0f);
5195 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 200.0f);
5197 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5203 // Use sliding and backwards moving to face enemy.
5204 // (Coded 2/20/98. Works fine, but it's hard to see how to integrate it into the AI system.
5205 // Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5206 // It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5207 // would be frustrating, I think.
5208 // This function is currently not called.)
5209 void slide_face_ship()
5213 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5215 // If can't slide, return.
5216 if ((sip->max_vel.xyz.x == 0.0f) && (sip->max_vel.xyz.y == 0.0f))
5220 float dot_from_enemy, dot_to_enemy;
5221 vector vec_from_enemy, vec_to_goal;
5226 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5228 dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5230 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5232 dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
5233 dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.v.fvec);
5235 if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > 0.0f)
5240 if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.uvec) > 0.0f)
5245 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.rvec, right * 200.0f);
5246 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.v.uvec, up * 200.0f);
5248 vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5250 if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.rvec) > 0.0f)
5251 AI_ci.sideways = 1.0f;
5253 AI_ci.sideways = -1.0f;
5255 if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.v.uvec) > 0.0f)
5256 AI_ci.vertical = 1.0f;
5258 AI_ci.vertical = -1.0f;
5260 if (dist < 200.0f) {
5261 if (dot_from_enemy < 0.7f)
5262 accelerate_ship(aip, -1.0f);
5264 accelerate_ship(aip, dot_from_enemy + 0.5f);
5266 if (dot_from_enemy < 0.7f) {
5267 accelerate_ship(aip, 0.2f);
5269 accelerate_ship(aip, 1.0f);
5274 // General code for handling one ship evading another.
5275 // Problem: This code is also used for avoiding an impending collision.
5276 // In such a case, it is not good to go to max speed, which is often good
5277 // for a certain kind of evasion.
5280 vector player_pos, enemy_pos, goal_point;
5281 vector vec_from_enemy;
5282 float dot_from_enemy;
5284 ship *shipp = &Ships[Pl_objp->instance];
5285 ship_info *sip = &Ship_info[shipp->ship_info_index];
5286 ai_info *aip = &Ai_info[shipp->ai_index];
5287 float bank_override = 0.0f;
5289 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5291 // Make speed based on skill level, varying at highest skill level, which is harder to hit.
5292 if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5296 rand_int = static_rand(Pl_objp-Objects);
5297 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5298 accelerate_ship(aip, accel_val);
5299 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5301 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5303 if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5304 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5305 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5306 afterburners_start(Pl_objp);
5307 aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5311 vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5313 dist = vm_vec_normalize(&vec_from_enemy);
5314 dot_from_enemy = vm_vec_dot(&En_objp->orient.v.fvec, &vec_from_enemy);
5316 if (dist > 250.0f) {
5318 // If far away from enemy, circle, going to nearer of point far off left or right wing
5319 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.v.rvec, 250.0f);
5320 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.v.rvec, -250.0f);
5321 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5325 } else if (dot_from_enemy < 0.1f) {
5326 // If already close to behind, goal is to get completely behind.
5327 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.fvec, -1000.0f);
5328 } else if (dot_from_enemy > 0.9f) {
5329 // If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5330 vector vec_to_enemy;
5333 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5335 vm_vec_normalize(&vec_to_enemy);
5336 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_enemy);
5337 if (dot_to_enemy > 0.75f) {
5338 // Used to go to En_objp's right vector, but due to banking while turning, that
5339 // caused flying in an odd spiral.
5340 vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.v.rvec, 1000.0f);
5342 bank_override = Pl_objp->phys_info.speed;
5344 bank_override = Pl_objp->phys_info.speed; // In enemy's sights, not pointing at him, twirl away.
5345 // nprintf(("Mike", " Do sumpin' else."));
5350 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5353 float psrandval; // some value close to zero to choose whether to turn right or left.
5355 psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); // Value between -8 and 7
5356 psrandval = psrandval/16.0f; // Value between -1/2 and 1/2 (approx)
5358 // If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5359 if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.rvec) > psrandval) {
5365 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, scale);
5367 temp = ((Missiontime >> 16) & 0x07);
5368 temp = ((temp * (temp+1)) % 16)/2 - 4;
5369 if ((psrandval == 0) && (temp == 0))
5372 scale = 200.0f * temp;
5374 vm_vec_scale_add2(&goal_point, &En_objp->orient.v.uvec, scale);
5376 // No evasion this frame, but continue with previous turn.
5377 // Reason: If you don't, you lose rotational momentum. Turning every other frame,
5378 // and not in between results in a very slow turn because of loss of momentum.
5379 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))
5380 goal_point = aip->prev_goal_point;
5382 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.v.rvec, 100.0f);
5386 // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z));
5387 turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5389 aip->prev_goal_point = goal_point;
5392 // --------------------------------------------------------------------------
5393 // Fly in a manner making it difficult for opponent to attack.
5400 // -------------------------------------------------------------------
5401 // Refine predicted enemy position because enemy will move while we move
5402 // towards predicted enemy position.
5403 // last_delta_vec is stuffed with size of polishing in last step. This small amount
5404 // can be used to perturb the predicted position to make firing not be exact.
5405 // This function will almost always undershoot actual position, assuming both ships
5406 // are moving at constant speed. But with even one polishing step, the error should
5407 // be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5408 void polish_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, float dist_to_enemy, vector *last_delta_vec, int num_polish_steps) // Not used:, float time_to_enemy)
5411 vector player_pos = pobjp->pos;
5412 vector enemy_pos = *predicted_enemy_pos;
5413 physics_info *en_physp = &eobjp->phys_info;
5414 float time_to_enemy;
5415 vector last_predicted_enemy_pos = *predicted_enemy_pos;
5417 vm_vec_zero(last_delta_vec);
5419 for (iteration=0; iteration < num_polish_steps; iteration++) {
5420 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5421 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5422 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5423 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5424 last_predicted_enemy_pos= *predicted_enemy_pos;
5430 Relevant variables are:
5431 best_dot_to_enemy best dot product to enemy in last BEST_DOT_TIME seconds
5432 best_dot_to_time time at which best dot occurred
5433 best_dot_from_enemy best dot product for enemy to player in last BEST_DOT_TIME seconds
5434 best_dot_from_time time at which best dot occurred
5435 submode_start_time time at which we entered the current submode
5436 previous_submode previous submode, get it?
5438 CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5444 float G_collision_time;
5445 vector G_predicted_pos, G_fire_pos;
5448 void show_firing_diag()
5455 if (G_collision_time == 0.0f)
5458 mprintf(("Fired from %5.1f, %5.1f %5.1f at time = %5.1f, predict collision in %5.2f seconds at %5.1f %5.1f %5.1f\n",
5459 Pl_objp->pos.xyz.x, Pl_objp->pos.xyz.y, Pl_objp->pos.xyz.z, (float) Missiontime/1000.0f, G_collision_time, G_predicted_pos.xyz.x, G_predicted_pos.xyz.y, G_predicted_pos.xyz.z));
5460 vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5461 dot = vm_vec_dot(&v2t, &Pl_objp->orient.v.fvec);
5462 mprintf(("Dot of v.fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5464 vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5465 vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.v.fvec, G_collision_time*300.0f);
5466 dist = vm_vec_dist(&pos1, &pos2);
5468 mprintf(("Enemy, laser pos, distance: [%5.1f %5.1f %5.1f] [%5.1f %5.1f %5.1f] %6.2f\n", pos1.xyz.x, pos1.xyz.y, pos1.xyz.z, pos2.xyz.x, pos2.xyz.y, pos2.xyz.z, dist));
5473 // flags & WIF_PUNCTURE
5474 // Then Select a Puncture weapon.
5476 // Select Any ol' weapon.
5477 // Returns primary_bank index.
5478 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5480 ship *shipp = &Ships[objp->instance];
5481 ship_weapon *swp = &shipp->weapons;
5484 //SDL_assert( other_objp != NULL );
5485 SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5487 sip = &Ship_info[shipp->ship_info_index];
5489 if (flags & WIF_PUNCTURE) {
5490 if (swp->current_primary_bank >= 0) {
5493 bank_index = swp->current_primary_bank;
5495 if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5496 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5497 return swp->current_primary_bank;
5500 for (int i=0; i<swp->num_primary_banks; i++) {
5501 int weapon_info_index;
5503 weapon_info_index = swp->primary_bank_weapons[i];
5505 if (weapon_info_index > -1){
5506 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5507 swp->current_primary_bank = i;
5508 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5514 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5515 if ( swp->current_primary_bank < 0 ) {
5516 if ( swp->num_primary_banks > 0 ) {
5517 swp->current_primary_bank = 0;
5521 } else { // Don't need to be using a puncture weapon.
5522 if (swp->current_primary_bank >= 0) {
5523 if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5524 return swp->current_primary_bank;
5527 for (int i=0; i<swp->num_primary_banks; i++) {
5528 if (swp->primary_bank_weapons[i] > -1) {
5529 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5530 swp->current_primary_bank = i;
5531 nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5536 // Wasn't able to find a non-puncture weapon. Stick with what we have.
5539 SDL_assert( swp->current_primary_bank != -1 ); // get Alan or Allender
5541 return swp->current_primary_bank;
5544 // --------------------------------------------------------------------------
5545 // Maybe link primary weapons.
5546 void set_primary_weapon_linkage(object *objp)
5551 shipp = &Ships[objp->instance];
5552 aip = &Ai_info[shipp->ai_index];
5554 shipp->flags &= ~SF_PRIMARY_LINKED;
5556 if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5557 if (shipp->flags & SF_PRIMARY_LINKED)
5558 nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5559 shipp->flags &= ~SF_PRIMARY_LINKED;
5560 return; // If low on slots, don't link.
5563 shipp->flags &= ~SF_PRIMARY_LINKED;
5565 // AL: ensure target is a ship!
5566 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5567 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5568 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5569 if ( aip->targeted_subsys == NULL ) {
5570 shipp->flags |= SF_PRIMARY_LINKED;
5571 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5577 // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5579 if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5580 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5582 swp = &shipp->weapons;
5583 // only continue if both primaries are puncture weapons
5584 if ( swp->num_primary_banks == 2 ) {
5585 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) )
5587 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) )
5593 // Don't want all ships always linking weapons at start, so asynchronize.
5594 if (Missiontime < i2f(30))
5596 else if (Missiontime < i2f(120)) {
5597 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5602 if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5603 shipp->flags |= SF_PRIMARY_LINKED;
5604 } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5605 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5606 shipp->flags |= SF_PRIMARY_LINKED;
5610 // --------------------------------------------------------------------------
5611 // Fire the current primary weapon.
5612 // *objp is the object to fire from.
5613 void ai_fire_primary_weapon(object *objp)
5615 ship *shipp = &Ships[objp->instance];
5616 ship_weapon *swp = &shipp->weapons;
5621 SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5622 sip = &Ship_info[shipp->ship_info_index];
5624 aip = &Ai_info[shipp->ai_index];
5626 // If low on slots, fire a little less often.
5627 if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5628 if (frand() > 0.5f) {
5629 nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5634 if (!Ai_firing_enabled){
5638 if (aip->target_objnum != -1){
5639 enemy_objp = &Objects[aip->target_objnum];
5644 if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5646 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5647 // if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5648 if ( aip->targeted_subsys != NULL ) {
5649 flags = WIF_PUNCTURE;
5651 ai_select_primary_weapon(objp, enemy_objp, flags);
5652 ship_primary_changed(shipp); // AL: maybe send multiplayer information when AI ship changes primaries
5653 aip->primary_select_timestamp = timestamp(5 * 1000); // Maybe change primary weapon five seconds from now.
5656 // If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5660 // if (!IS_VEC_NULL(&G_predicted_pos)) {
5661 if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5662 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5663 nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5665 vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5666 dot = vm_vec_dot(&v2t, &objp->orient.v.fvec);
5667 if (dot > .998629534f){ // if within 3.0 degrees of desired heading, bash
5668 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.v.uvec, NULL);
5673 // Make sure not firing at a protected ship unless firing at a live subsystem.
5674 // Note: This happens every time the ship tries to fire, perhaps every frame.
5675 // Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5676 // by multiple banks it can fire from.
5677 if (aip->target_objnum != -1) {
5678 object *tobjp = &Objects[aip->target_objnum];
5679 if (tobjp->flags & OF_PROTECTED) {
5680 if (aip->targeted_subsys != NULL) {
5683 type = aip->targeted_subsys->system_info->type;
5684 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5685 aip->target_objnum = -1;
5689 aip->target_objnum = -1;
5695 // If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5696 if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5697 // AL: 3-6-98: Check if current_primary_bank is valid
5698 if ((enemy_objp->hull_strength < 750.0f) &&
5699 ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5700 (swp->current_primary_bank >= 0) ) {
5701 if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5702 //nprintf(("AI", "Ship %s not firing at protected ship %s because not using disruptor.\n", Ships[objp->instance].ship_name, Ships[enemy_objp->instance].ship_name));
5703 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5709 num_attacking = num_enemies_attacking(enemy_objp-Objects);
5710 if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5711 if (frand() < 0.75f) {
5712 nprintf(("AI", "Ship %s not firing at protected ship %s because too many attacking.\n", Ships[objp->instance].ship_name, Ships[enemy_objp->instance].ship_name));
5713 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5721 set_primary_weapon_linkage(objp);
5723 // I think this will properly solve the problem
5724 // fire non-streaming weapons
5725 ship_fire_primary(objp, 0);
5727 // fire streaming weapons
5728 shipp->flags |= SF_TRIGGER_DOWN;
5729 ship_fire_primary(objp, 1);
5730 shipp->flags &= ~SF_TRIGGER_DOWN;
5733 // --------------------------------------------------------------------------
5734 // Return number of nearby enemy fighters.
5735 // threshold is the distance within which a ship is considered near.
5737 // input: enemy_team_mask => teams that are considered as an enemy
5738 // pos => world position to measure ship distances from
5739 // threshold => max distance from pos to be considered "near"
5741 // exit: number of ships within threshold units of pos
5742 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5748 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5750 ship_objp = &Objects[so->objnum];
5752 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5753 if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5754 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5763 // --------------------------------------------------------------------------
5764 // Select secondary weapon to fire.
5765 // Currently, 1/16/98:
5766 // If 0 secondary weapons available, return -1
5767 // If 1 available, use it.
5768 // If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5769 // priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT. If any weapon has any bit in priority1
5770 // set, that weapon will be selected. If not, apply to priority2. If neither, return -1, meaning no weapon selected.
5771 // Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5775 // Favor aspect seekers when attacking small ships faraway.
5776 // Favor rapid fire dumbfire when attacking a large ship.
5777 // Ignore heat seekers because we're not sure how they'll work.
5778 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5780 int num_weapon_types;
5781 int weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5786 initial_bank = swp->current_secondary_bank;
5788 // Ignore bombs unless one of the priorities asks for them to be selected.
5789 if (WIF_HUGE & (priority1 | priority2))
5792 ignore_mask = WIF_HUGE;
5794 if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5795 ignore_mask |= WIF_BOMBER_PLUS;
5798 for (i=0; i<MAX_WEAPON_TYPES; i++) {
5799 weapon_id_list[i] = -1;
5800 weapon_bank_list[i] = -1;
5804 // Stuff weapon_bank_list with bank index of available weapons.
5805 num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5807 int priority2_index = -1;
5809 for (i=0; i<num_weapon_types; i++) {
5812 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5813 if (!(wi_flags & ignore_mask)) { // Maybe bombs are illegal.
5814 if (wi_flags & priority1) {
5815 swp->current_secondary_bank = weapon_bank_list[i]; // Found first priority, return it.
5817 } else if (wi_flags & priority2)
5818 priority2_index = weapon_bank_list[i]; // Found second priority, but might still find first priority.
5822 // If didn't find anything above, then pick any secondary weapon.
5823 if (i == num_weapon_types) {
5824 swp->current_secondary_bank = priority2_index; // Assume we won't find anything.
5825 if (priority2_index == -1) {
5826 for (i=0; i<num_weapon_types; i++) {
5829 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5830 if (!(wi_flags & ignore_mask)) { // Maybe bombs are illegal.
5831 if (swp->secondary_bank_ammo[i] > 0) {
5832 swp->current_secondary_bank = i;
5840 // If switched banks, force reacquisition of aspect lock.
5841 if (swp->current_secondary_bank != initial_bank) {
5842 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5844 aip->aspect_locked_time = 0.0f;
5845 aip->current_target_is_locked = 0;
5849 ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5850 // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5853 // Return number of objects homing on object *target_objp
5854 int compute_num_homing_objects(object *target_objp)
5859 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5860 if (objp->type == OBJ_WEAPON) {
5861 if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5862 if (Weapons[objp->instance].homing_object == target_objp) {
5872 // Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5873 // If it's a shockwave weapon, tell your team about it!
5874 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5876 if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5878 int firing_ship_team;
5880 firing_ship_team = Ships[firing_objp->instance].team;
5882 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5883 object *A = &Objects[so->objnum];
5884 SDL_assert(A->type == OBJ_SHIP);
5886 if (Ships[A->instance].team == firing_ship_team) {
5887 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5888 // AL 1-5-98: only avoid shockwave if not docked or repairing
5889 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5890 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5897 // Return total payload of all incoming missiles.
5898 float compute_incoming_payload(object *target_objp)
5901 float payload = 0.0f;
5903 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5906 objp = &Objects[mo->objnum];
5907 SDL_assert(objp->type == OBJ_WEAPON);
5908 if (Weapons[objp->instance].homing_object == target_objp) {
5909 payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5916 // --------------------------------------------------------------------------
5917 // Return true if OK for *aip to fire its current weapon at its current target.
5918 // Only reason this function returns false is:
5919 // weapon is a homer
5920 // targeted at player
5921 // OR: player has too many homers targeted at him
5922 // Missiontime in that dead zone in which can't fire at this player
5923 // Note: If player is attacking a ship, that ship is allowed to fire at player. Otherwise, we get in a situation in which
5924 // player is attacking a large ship, but that large ship is not defending itself with missiles.
5925 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5928 object *tobjp = &Objects[target_objnum];
5930 if (target_objnum > -1) {
5931 // AL 3-4-98: Ensure objp target is a ship first
5932 if ( tobjp->type == OBJ_SHIP ) {
5934 // should not get this far. check if ship is protected from beam and weapon is type beam
5935 if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5939 if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5940 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5944 // If player, maybe fire based on Skill_level and number of incoming weapons.
5945 // If non-player, maybe fire based on payload of incoming weapons.
5946 if (wip->wi_flags & WIF_HOMING) {
5947 if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5948 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5949 // Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5950 // With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5951 // At Easy, 2/7...at Expert, 5/7
5952 int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5953 if (t > Game_skill_level) {
5954 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5958 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5960 if (wip->wi_flags & WIF_SWARM)
5961 swarmers = 2; // Note, always want to be able to fire swarmers if no currently incident homers.
5962 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5965 } else if (num_homers > 3) {
5966 float incoming_payload;
5968 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5970 if (incoming_payload > tobjp->hull_strength) {
5980 // --------------------------------------------------------------------------
5981 // Fire a secondary weapon.
5982 // Maybe choose to fire a different one.
5983 // priority1 and priority2 are optional parameters with defaults = -1
5984 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5993 if (!Ai_firing_enabled)
5997 SDL_assert( objp != NULL );
5998 SDL_assert(objp->type == OBJ_SHIP);
5999 shipp = &Ships[objp->instance];
6000 swp = &shipp->weapons;
6002 SDL_assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
6003 sip = &Ship_info[shipp->ship_info_index];
6005 // Select secondary weapon.
6006 current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
6008 //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
6009 if (current_bank == -1) {
6010 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6014 SDL_assert(current_bank < shipp->weapons.num_secondary_banks);
6016 weapon_info *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
6018 if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
6019 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
6020 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
6021 } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
6022 // This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
6023 // bombs, delivering them is probably more important than surviving.
6026 aip = &Ai_info[shipp->ai_index];
6028 // Note, maybe don't fire if firing at player and any homers yet fired.
6029 // Decreasing chance to fire the more homers are incoming on player.
6030 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
6031 if (ship_fire_secondary(objp)) {
6033 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6034 //nprintf(("AI", "Frane %i: Ship %s fired secondary %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[current_bank]].name));
6038 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
6045 // Return true if it looks like obj1, if continuing to move along current vector, will
6046 // collide with obj2.
6047 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
6049 if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
6050 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
6051 return objects_will_collide(obj1, obj2, duration, 2.0f);
6054 // CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
6060 // --------------------------------------------------------------------------
6061 // Return true if ship *objp firing a laser believes it will hit a teammate.
6062 int might_hit_teammate(object *firing_objp)
6068 team = Ships[firing_objp->instance].team;
6070 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6071 objp = &Objects[so->objnum];
6072 if (Ships[objp->instance].team == team) {
6076 vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6077 dist = vm_vec_mag_quick(&vec_to_objp);
6078 dot = vm_vec_dot(&firing_objp->orient.v.fvec, &vec_to_objp)/dist;
6079 if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6088 //int Team_not_fire_count=0, Team_hit_count = 0;
6090 void render_all_ship_bay_paths(object *objp)
6093 ship *sp = &Ships[objp->instance];
6097 pm = model_get(sp->modelnum);
6098 vector global_path_point;
6099 vertex v, prev_vertex;
6101 if ( pm->ship_bay == NULL )
6104 for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6105 mp = &pm->paths[pm->ship_bay->paths[i]];
6107 for ( j = 0; j < mp->nverts; j++ ) {
6108 vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6109 vm_vec_add2(&global_path_point, &objp->pos);
6110 g3_rotate_vertex(&v, &global_path_point);
6114 gr_set_color(0, color, 0);
6116 if ( j == mp->nverts-1 ) {
6117 gr_set_color(255, 0, 0);
6120 g3_draw_sphere( &v, 1.5f);
6123 g3_draw_line(&v, &prev_vertex);
6131 // debug function to show all path points associated with an object
6132 void render_all_subsys_paths(object *objp)
6135 ship *sp = &Ships[objp->instance];
6139 pm = model_get(sp->modelnum);
6140 vector global_path_point;
6141 vertex v, prev_vertex;
6143 if ( pm->ship_bay == NULL )
6146 for ( i = 0; i < pm->n_paths; i++ ) {
6148 for ( j = 0; j < mp->nverts; j++ ) {
6149 vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6150 vm_vec_add2(&global_path_point, &objp->pos);
6151 g3_rotate_vertex(&v, &global_path_point);
6155 gr_set_color(0, color, 0);
6157 if ( j == mp->nverts-1 ) {
6158 gr_set_color(255, 0, 0);
6161 g3_draw_sphere( &v, 1.5f);
6164 g3_draw_line(&v, &prev_vertex);
6171 void render_path_points(object *objp)
6173 ship *shipp = &Ships[objp->instance];
6174 ai_info *aip = &Ai_info[shipp->ai_index];
6178 render_all_subsys_paths(objp);
6179 render_all_ship_bay_paths(objp);
6181 if (aip->goal_objnum < 0)
6184 dobjp = &Objects[aip->goal_objnum];
6185 pm = model_get(Ships[dobjp->instance].modelnum);
6186 vector dock_point, global_dock_point;
6189 ship_model_start(&Objects[aip->goal_objnum]);
6191 dock_point = pm->docking_bays[0].pnt[0];
6192 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6193 g3_rotate_vertex(&v, &global_dock_point);
6194 gr_set_color(255, 255, 255);
6195 g3_draw_sphere( &v, 1.5f);
6198 if (aip->path_start != -1) {
6200 pnode *pp = &Path_points[aip->path_start];
6201 int num_points = aip->path_length;
6204 for (i=0; i<num_points; i++) {
6207 g3_rotate_vertex( &v0, &pp->pos );
6209 gr_set_color(0, 128, 96);
6211 g3_draw_line(&v0, &prev_vertex);
6213 if (pp-Path_points == aip->path_cur)
6214 gr_set_color(255,255,0);
6216 g3_draw_sphere( &v0, 4.5f);
6218 // Connect all the turrets that can fire upon this point to this point.
6219 /* if (0) { //pp->path_index != -1) {
6223 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6225 if (pmpv->nturrets) {
6226 for (int j = 0; j<pmpv->nturrets; j++) {
6231 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6233 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6235 g3_rotate_vertex(&v1, &turret_pos);
6236 gr_set_color(255, 255, 0);
6237 g3_draw_line(&v0, &v1);
6238 g3_draw_sphere( &v1, 1.5f);
6249 ship_model_stop(&Objects[aip->goal_objnum]);
6252 // Return the distance that the current AI weapon will travel
6253 float ai_get_weapon_dist(ship_weapon *swp)
6255 int bank_num, weapon_num;
6257 bank_num = swp->current_primary_bank;
6258 weapon_num = swp->primary_bank_weapons[bank_num];
6260 // If weapon_num is illegal, return a reasonable value. A valid weapon
6261 // will get selected when this ship tries to fire.
6262 if (weapon_num == -1) {
6267 return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6270 float ai_get_weapon_speed(ship_weapon *swp)
6272 int bank_num, weapon_num;
6274 bank_num = swp->current_primary_bank;
6278 weapon_num = swp->primary_bank_weapons[bank_num];
6280 if (weapon_num == -1) {
6285 return Weapon_info[weapon_num].max_speed;
6288 // Compute the predicted position of a ship to be fired upon from a turret.
6289 // This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6290 // Return value in *predicted_enemy_pos.
6291 // Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6292 // *pobjp object firing the weapon
6293 // *eobjp object being fired upon
6294 void set_predicted_enemy_pos_turret(vector *predicted_enemy_pos, vector *gun_pos, object *pobjp, vector *enemy_pos, vector *enemy_vel, float weapon_speed, float time_enemy_in_range)
6296 ship *shipp = &Ships[pobjp->instance];
6299 //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6301 if (weapon_speed < 1.0f)
6302 weapon_speed = 1.0f;
6306 // Make it take longer for enemies to get player's allies in range based on skill level.
6307 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6308 range_time += In_range_time[Game_skill_level];
6310 //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6312 if (time_enemy_in_range < range_time) {
6315 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6316 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6318 float collision_time, scale;
6320 ai_info *aip = &Ai_info[shipp->ai_index];
6322 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6324 if (collision_time == 0.0f){
6325 collision_time = 100.0f;
6328 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6329 if (time_enemy_in_range > 2*range_time){
6330 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6332 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6335 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6337 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6338 G_collision_time = collision_time;
6339 G_fire_pos = *gun_pos;
6342 G_predicted_pos = *predicted_enemy_pos;
6345 // Compute the predicted position of a ship to be fired upon.
6346 // This is based on current position of firing object, enemy object, relative position of gun on firing object,
6347 // weapon speed and skill level constraints.
6348 // Return value in *predicted_enemy_pos.
6349 // Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6350 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6352 float weapon_speed, range_time;
6353 ship *shipp = &Ships[pobjp->instance];
6355 weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6356 weapon_speed = max(weapon_speed, 1.0f); // set not less than 1
6360 // Make it take longer for enemies to get player's allies in range based on skill level.
6361 // but don't bias team v. team missions
6362 if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6363 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6364 range_time += In_range_time[Game_skill_level];
6367 //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6369 if (aip->time_enemy_in_range < range_time) {
6372 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6373 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6375 float collision_time;
6376 vector gun_pos, pnt;
6377 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6379 // Compute position of gun in absolute space and use that as fire position.
6380 if(po->gun_banks != NULL){
6381 pnt = po->gun_banks[0].pnt[0];
6383 pnt = Objects[shipp->objnum].pos;
6385 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6386 vm_vec_add2(&gun_pos, &pobjp->pos);
6388 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6390 if (collision_time == 0.0f) {
6391 collision_time = 100.0f;
6394 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6397 G_collision_time = collision_time;
6398 G_fire_pos = gun_pos;
6401 // Now add error terms (1) regular aim (2) EMP (3) stealth
6405 // regular skill level error in aim
6406 if (aip->time_enemy_in_range > 2*range_time) {
6407 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6409 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6412 // if this ship is under the effect of an EMP blast, throw his aim off a bit
6413 if (shipp->emp_intensity > 0.0f) {
6414 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6415 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6416 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6419 // if stealthy ship, throw his aim off, more when farther away and when dot is small
6420 if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6421 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6423 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6424 vm_vec_normalize_quick(&temp);
6425 float dot = vm_vec_dotprod(&temp, &pobjp->orient.v.fvec);
6426 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6428 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6431 // get a random vector that changes slowly over time (1x / sec)
6432 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6434 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6437 G_predicted_pos = *predicted_enemy_pos;
6440 // Handler of submode for Chase. Go into a continuous turn for awhile.
6447 SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6448 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6449 SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6450 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6452 // Make a continuous turn towards any combination of possibly negated
6453 // up and right vectors.
6454 tvec = Pl_objp->pos;
6456 if (aip->submode_parm0 & 0x01)
6457 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6458 if (aip->submode_parm0 & 0x02)
6459 vm_vec_sub2(&tvec, &Pl_objp->orient.v.rvec);
6460 if (aip->submode_parm0 & 0x04)
6461 vm_vec_add2(&tvec, &Pl_objp->orient.v.uvec);
6462 if (aip->submode_parm0 & 0x08)
6463 vm_vec_sub2(&tvec, &Pl_objp->orient.v.uvec);
6465 // Detect degenerate cases that cause tvec to be same as player pos.
6466 if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6467 aip->submode_parm0 &= 0x05;
6468 if (aip->submode_parm0 == 0)
6469 aip->submode_parm0 = 1;
6470 vm_vec_add2(&tvec, &Pl_objp->orient.v.rvec);
6473 ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6474 accelerate_ship(aip, 1.0f);
6477 // ATTACK submode handler for chase mode.
6478 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6481 float dot_to_enemy, dot_from_enemy;
6483 compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6485 // If we're trying to slow down to get behind, then point to turn towards is different.
6486 _pep = *predicted_enemy_pos;
6487 if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6488 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.v.fvec, 100.0f);
6490 ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6492 accelerate_ship(aip, 0.0f);
6495 // Return time until weapon_objp might hit ship_objp.
6496 // Assumes ship_objp is not moving.
6497 // Returns negative time if not going to hit.
6498 // This is a very approximate function, but is pretty fast.
6499 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6501 float to_dot, from_dot, dist;
6503 dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6505 // Note, this is bogus. It assumes only the weapon is moving.
6506 // Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6507 // (Ie, if object moving at right angle to weapon, just continue for now...)
6508 if (weapon_objp->phys_info.speed < 1.0f)
6510 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))
6511 return dist / weapon_objp->phys_info.speed;
6516 // Return time until danger weapon could hit this ai object.
6517 // Return negative time if not endangered.
6518 float ai_endangered_by_weapon(ai_info *aip)
6520 object *weapon_objp;
6522 if (aip->danger_weapon_objnum == -1) {
6526 weapon_objp = &Objects[aip->danger_weapon_objnum];
6528 if (weapon_objp->signature != aip->danger_weapon_signature) {
6529 aip->danger_weapon_objnum = -1;
6533 return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6536 // Return true if this ship is near full strength.
6537 int ai_near_full_strength(object *objp, ship_info *sip)
6539 return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6542 // Set acceleration while in attack mode.
6543 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6547 if (En_objp->phys_info.speed > 1.0f)
6548 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6552 // Sometimes, told to attack slowly. Allows to get in more hits.
6553 if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6554 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6555 if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6556 //nprintf(("AI", " slowly "));
6557 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6561 aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6564 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) {
6565 //nprintf(("AI", "1"));
6566 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6567 if (dist_to_enemy > 800.0f) {
6568 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6573 shipp = &Ships[Pl_objp->instance];
6574 sip = &Ship_info[shipp->ship_info_index];
6576 if (sip->afterburner_fuel_capacity > 0.0f) {
6577 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6578 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6579 afterburners_start(Pl_objp);
6580 aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6587 accelerate_ship(aip, 1.0f);
6588 } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6589 && (En_objp->phys_info.speed < 10.0f)
6590 && (dist_to_enemy > 25.0f)
6591 && (dot_to_enemy > 0.8f)
6592 && (dot_from_enemy < 0.8f)) {
6593 accelerate_ship(aip, 0.0f); // No one attacking us, so don't need to move.
6594 } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6595 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6596 } else if (Pl_objp->phys_info.speed < 15.0f) {
6597 accelerate_ship(aip, 1.0f);
6598 } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6599 if (dot_from_enemy > 0.75f)
6600 accelerate_ship(aip, 1.0f);
6602 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6604 change_acceleration(aip, 0.5f);
6608 // Pl_objp (aip) tries to get behind En_objp.
6609 // New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6610 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6614 vector vec_from_enemy;
6617 dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6619 vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.v.fvec, -100.0f); // Pick point 100 units behind.
6620 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6622 dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.v.fvec);
6625 accelerate_ship(aip, 1.0f);
6627 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6631 int avoid_player(object *objp, vector *goal_pos)
6633 maybe_avoid_player(Pl_objp, goal_pos);
6634 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6636 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6637 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6639 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6640 ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6641 accelerate_ship(aip, 0.5f);
6649 // Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6650 // If so, stuff *collision_point.
6651 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6655 mc.model_num = Ships[big_objp->instance].modelnum; // Fill in the model to check
6656 mc.orient = &big_objp->orient; // The object's orient
6657 mc.pos = &big_objp->pos; // The object's position
6658 mc.p0 = p0; // Point 1 of ray to check
6660 mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL; // flags
6664 // Only check the 2nd lowest hull object
6665 polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6666 mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6670 *collision_point = mc.hit_point_world;
6675 // Return true/false if *objp will collide with *big_objp
6676 // Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6677 // Global collision point stuffed in *collision_point
6678 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6683 radius = big_objp->radius + delta_time * objp->phys_info.speed;
6685 if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6689 if (goal_point == NULL) {
6690 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time); // Point 2 of ray to check
6692 end_pos = *goal_point;
6695 return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6698 // Return true if *objp is expected to collide with a large ship.
6699 // Stuff global collision point in *collision_point.
6700 // If *goal_point is not NULL, use that as the point towards which *objp will be flying. Don't use *objp velocity
6701 // *ignore_objp will typically be the target this ship is pursuing, either to attack or guard. We don't want to avoid it.
6702 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6706 int collision_obj_index = -1;
6707 float min_dist = 999999.9f;
6708 float collision_time = -1.0f;
6710 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6712 big_objp = &Objects[so->objnum];
6714 if (big_objp == ignore_objp)
6717 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6718 vector cur_collision_point;
6721 if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6723 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6725 if (cur_dist < min_dist) {
6726 min_dist = cur_dist;
6727 *collision_point = cur_collision_point;
6728 collision_time = time;
6729 collision_obj_index = OBJ_INDEX(big_objp);
6735 *distance = min_dist;
6736 return collision_obj_index;
6746 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6747 // Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6748 // Return result in *avoid_pos
6749 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6755 vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6756 vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6760 // Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6761 // First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6762 // means less of a turn.
6763 // Try going as far as 1.25f * radius.
6765 for (s=0.5f; s<1.3f; s += 0.25f) {
6767 for (i=0; i<4; i++) {
6768 vector p = big_objp->pos;
6769 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
6770 float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6775 vm_vec_scale_add2(&p, &mat1.v.uvec, ku);
6776 vm_vec_scale_add2(&p, &mat1.v.rvec, kr);
6778 goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6779 goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6780 if (!goals[i].collide)
6784 // If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6786 float min_dist = 9999999.9f;
6789 for (i=0; i<4; i++) {
6790 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6791 min_dist = goals[i].dist;
6796 SDL_assert(i != -1);
6798 *avoid_pos = goals[min_index].pos;
6804 // Drat. We tried and tried and could not find a point that did not cause a collision.
6805 // Get this dump pilot far away from the problem ship.
6807 vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6808 vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6812 // Return true if a large ship is being ignored.
6813 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6815 if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6817 vector collision_point;
6819 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6820 aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6821 mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6822 float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6823 aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f))); // Delay until check again is based on distance to avoid point.
6824 aip->avoid_ship_num = ship_num;
6826 aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6827 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6828 aip->avoid_ship_num = -1;
6829 aip->avoid_check_timestamp = timestamp(1500);
6833 if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6834 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6838 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6839 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6840 float dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
6841 float d2 = (1.0f + dot) * (1.0f + dot);
6842 accelerate_ship(aip, d2/4.0f);
6849 // Set desired right vector for ships flying towards another ship.
6850 // Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6851 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6855 vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6856 rvec->xyz.x = v2e.xyz.z;
6858 rvec->xyz.z = -v2e.xyz.x;
6859 if (vm_vec_mag_squared(rvec) < 0.001f)
6863 // Handler for stealth find submode of Chase.
6864 void ai_stealth_find()
6869 vector new_pos, vec_to_enemy;
6870 float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6872 SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6873 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6874 SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6875 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6877 // get time since last seen
6878 int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6880 // if delta_time is really big, i'm real confused, start sweep
6881 if (delta_time > 10000) {
6882 aip->submode_parm0 = SM_SF_BAIL;
6885 // guestimate new position
6886 vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6888 // if I think he's behind me, go to the goal point
6889 if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6890 new_pos = aip->goal_point;
6893 // check for collision with big ships
6894 if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6895 // reset ai submode to chase
6899 // if dist is near max and dot is close to 1, accel, afterburn
6900 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6901 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6902 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6904 // 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
6905 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) ) {
6907 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.v.fvec, -300.0f);
6908 aip->submode_parm0 = SM_SF_BEHIND;
6909 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6910 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6911 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.v.fvec);
6914 if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) { // 20 degree half angle
6916 accelerate_ship(aip, 1.0f);
6918 // engage afterburner
6919 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6920 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6921 afterburners_start(Pl_objp);
6922 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6926 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6930 // If enemy more than 500 meters away, all ships flying there will tend to match bank.
6931 // They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6932 // to interpolate a matrix rather than just a vector.
6933 if (dist_to_enemy > 500.0f) {
6935 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6936 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6938 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6941 dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec);
6943 attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6946 // -----------------------------------------------------------------------------
6947 // try to find stealth ship by sweeping an area
6948 void ai_stealth_sweep()
6953 SDL_assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6954 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6955 SDL_assert(Ships[Pl_objp->instance].ai_index >= 0);
6956 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6959 vector forward, right, up;
6962 // time since stealth last seen
6963 lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6965 // determine which pt to fly to in sweep by keeping track of parm0
6966 if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6968 // don't make goal pt more than 2k from current pos
6969 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6971 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6972 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6973 box_size = min(200.0f, box_size);
6974 box_size = max(500.0f, box_size);
6975 aip->stealth_sweep_box_size = box_size;
6977 aip->goal_point = goal_pt;
6978 aip->submode_parm0 = SM_SS_BOX0;
6981 // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6982 // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6983 // if stealth has no velocity make a velocity
6984 if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6985 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6988 // get "right" vector for box
6989 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6991 if ( vm_vec_mag_quick(&right) < 0.01 ) {
6992 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6995 vm_vec_normalize_quick(&right);
6997 // get forward for box
6998 vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
7001 vm_vec_crossprod(&up, &forward, &right);
7003 // lost far away ahead (do box)
7004 switch(aip->submode_parm0) {
7006 goal_pt = aip->goal_point;
7011 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7012 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7013 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7018 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7019 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7020 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7025 goal_pt = aip->goal_point;
7030 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
7031 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
7032 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7037 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
7038 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
7039 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
7044 goal_pt = aip->goal_point;
7052 // when close to goal_pt, update next goal pt
7053 float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
7054 if (dist_to_goal < 15) {
7055 aip->submode_parm0++;
7058 // check for collision with big ship
7059 if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
7060 // skip to the next pt on box
7061 aip->submode_parm0++;
7065 ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7068 if (dist_to_goal < 100) {
7070 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7071 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.v.fvec);
7074 accelerate_ship(aip, 0.8f*dot);
7077 // ATTACK submode handler for chase mode.
7078 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7081 float dot_to_enemy, dot_from_enemy; //, time_to_hit;
7082 float bank_override = 0.0f;
7084 if (avoid_player(Pl_objp, predicted_enemy_pos))
7087 compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7089 polymodel *po = model_get( sip->modelnum );
7096 start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7097 if (po->n_guns && start_bank != -1 ) {
7098 rel_pos = &po->gun_banks[start_bank].pnt[0];
7102 // If ship moving slowly relative to its size, then don't attack its center point.
7103 // How far from center we attack is based on speed, size and distance to enemy
7104 if (En_objp->radius > En_objp->phys_info.speed) {
7105 static_randvec(Pl_objp-Objects, &randvec);
7106 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7107 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius); // scale downward by 1/2 to 1/4
7108 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7110 new_pos = *predicted_enemy_pos;
7112 if (dist_to_enemy < 250.0f) {
7113 if (dot_from_enemy > 0.7f) {
7114 bank_override = Pl_objp->phys_info.speed;
7118 // If enemy more than 500 meters away, all ships flying there will tend to match bank.
7119 // They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7120 // to interpolate a matrix rather than just a vector.
7121 if (dist_to_enemy > 500.0f) {
7123 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7124 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7126 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7129 attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7132 // EVADE_SQUIGGLE submode handler for chase mode.
7133 // Changed by MK on 5/5/97.
7134 // Used to evade towards a point off the right or up vector.
7135 // Now, evade straight away to try to get far away.
7136 // The squiggling should protect against laser fire.
7137 void ai_chase_es(ai_info *aip, ship_info *sip)
7142 float bank_override = 0.0f;
7144 tvec = Pl_objp->pos;
7146 timeslice = (Missiontime >> 16) & 0x0f;
7147 scale = ((Missiontime >> 16) & 0x0f) << 14;
7149 if (timeslice & 0x01)
7150 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale ^ 0x10000));
7151 if (timeslice & 0x02)
7152 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.rvec, f2fl(scale));
7153 if (timeslice & 0x04)
7154 vm_vec_scale_add2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale ^ 0x10000));
7155 if (timeslice & 0x08)
7156 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.v.uvec, f2fl(scale));
7158 while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7159 tvec.xyz.x += frand();
7160 tvec.xyz.y += frand();
7163 bank_override = Pl_objp->phys_info.speed;
7165 ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7166 accelerate_ship(aip, 1.0f);
7169 // Trying to get away from opponent.
7170 void ai_chase_ga(ai_info *aip, ship_info *sip)
7172 // If not near end of this submode, evade squiggly. If near end, just fly straight for a bit
7174 float bank_override;
7175 vector vec_from_enemy;
7177 if (En_objp != NULL) {
7178 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7180 vec_from_enemy = Pl_objp->orient.v.fvec;
7182 static_randvec(Missiontime >> 15, &tvec);
7183 vm_vec_scale(&tvec, 100.0f);
7184 vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7185 vm_vec_add2(&tvec, &Pl_objp->pos);
7187 bank_override = Pl_objp->phys_info.speed;
7189 ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7191 accelerate_ship(aip, 2.0f);
7193 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7194 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7195 float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7196 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7197 afterburners_start(Pl_objp);
7198 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7200 afterburners_start(Pl_objp);
7201 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7207 // Make object *objp attack subsystem with ID = subnum.
7208 // Return true if found a subsystem to attack, else return false.
7209 // Note, can fail if subsystem exists, but has no hits.
7210 int ai_set_attack_subsystem(object *objp, int subnum)
7212 ship *shipp, *attacker_shipp;
7215 object *attacked_objp;
7217 SDL_assert(objp->type == OBJ_SHIP);
7218 SDL_assert(objp->instance >= 0);
7220 attacker_shipp = &Ships[objp->instance];
7221 SDL_assert(attacker_shipp->ai_index >= 0);
7223 aip = &Ai_info[attacker_shipp->ai_index];
7225 // MWA -- 2/27/98. Due to AL's changes, target_objnum is now not always valid (at least sometimes
7226 // in terms of goals). So, bail if we don't have a valid target.
7227 if ( aip->target_objnum == -1 )
7230 attacked_objp = &Objects[aip->target_objnum];
7231 shipp = &Ships[attacked_objp->instance]; // need to get our target's ship pointer!!!
7233 ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7237 set_targeted_subsys(aip, ssp, aip->target_objnum);
7239 if (aip->ignore_objnum == aip->target_objnum)
7240 aip->ignore_objnum = UNUSED_OBJNUM;
7242 // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7244 ai_set_goal_maybe_abort_dock(objp, aip);
7245 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7250 void ai_set_guard_vec(object *objp, object *guard_objp)
7255 aip = &Ai_info[Ships[objp->instance].ai_index];
7257 // Handle case of bogus call in which ship is told to guard self.
7258 SDL_assert(objp != guard_objp);
7259 if (objp == guard_objp) {
7260 vm_vec_rand_vec_quick(&aip->guard_vec);
7261 vm_vec_scale(&aip->guard_vec, 100.0f);
7265 // check if guard_objp is BIG
7266 radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7267 if (radius > 300.0f) {
7268 radius = guard_objp->radius * 1.25f;
7271 vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7273 if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7274 // Far away, don't just use vector to object, causes clustering of guard ships.
7277 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7278 vm_vec_rand_vec_quick(&rvec);
7279 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7280 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7283 vm_vec_normalize_quick(&aip->guard_vec);
7284 vm_vec_scale(&aip->guard_vec, radius);
7287 // Make object *objp guard object *other_objp.
7288 // To be called from the goals code.
7289 void ai_set_guard_wing(object *objp, int wingnum)
7293 int leader_objnum, leader_shipnum;
7295 SDL_assert(wingnum >= 0);
7297 SDL_assert(objp->type == OBJ_SHIP);
7298 SDL_assert(objp->instance >= 0);
7300 // shouldn't set the ai mode for the player
7301 if ( objp == Player_obj ) {
7305 shipp = &Ships[objp->instance];
7307 SDL_assert(shipp->ai_index >= 0);
7309 aip = &Ai_info[shipp->ai_index];
7310 force_avoid_player_check(objp, aip);
7312 ai_set_goal_maybe_abort_dock(objp, aip);
7313 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7315 // This function is called whenever a guarded ship is destroyed, so this code
7316 // prevents a ship from trying to guard a non-existent wing.
7317 if (Wings[wingnum].current_count < 1) {
7318 aip->guard_objnum = -1;
7319 aip->guard_wingnum = -1;
7320 aip->mode = AIM_NONE;
7322 leader_shipnum = Wings[wingnum].ship_index[0];
7323 leader_objnum = Ships[leader_shipnum].objnum;
7325 SDL_assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7326 //SDL_assert(leader_objnum != objp-Objects); // Don't allow ships to guard themselves.
7327 if (leader_objnum == OBJ_INDEX(objp)) {
7328 //Int3(); // Seems illegal, but let's clean up. Get MikeK.
7332 aip->guard_wingnum = wingnum;
7333 aip->guard_objnum = leader_objnum;
7334 aip->guard_signature = Objects[leader_objnum].signature;
7335 aip->mode = AIM_GUARD;
7336 aip->submode = AIS_GUARD_STATIC;
7338 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7342 // Make object *objp guard object *other_objp.
7343 // To be called from the goals code.
7344 void ai_set_evade_object(object *objp, object *other_objp)
7350 SDL_assert(objp->type == OBJ_SHIP);
7351 SDL_assert(objp->instance >= 0);
7353 shipp = &Ships[objp->instance];
7355 SDL_assert(shipp->ai_index >= 0);
7357 aip = &Ai_info[shipp->ai_index];
7359 other_objnum = OBJ_INDEX(other_objp);
7360 SDL_assert(other_objnum >= 0);
7362 SDL_assert(other_objnum != Ships[aip->shipnum].objnum); // make sure not targeting self
7363 aip->target_objnum = other_objnum;
7365 aip->mode = AIM_EVADE;
7368 // Make objp guard other_objp
7369 // If other_objp is a member of a wing, objp will guard that whole wing
7370 // UNLESS objp is also a member of the wing!
7371 void ai_set_guard_object(object *objp, object *other_objp)
7377 SDL_assert(objp->type == OBJ_SHIP);
7378 SDL_assert(objp->instance >= 0);
7379 SDL_assert(objp != other_objp);
7381 shipp = &Ships[objp->instance];
7383 SDL_assert(shipp->ai_index >= 0);
7385 aip = &Ai_info[shipp->ai_index];
7386 aip->avoid_check_timestamp = timestamp(1);
7388 // If ship to guard is in a wing, guard that whole wing.
7389 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7390 if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7391 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7394 other_objnum = other_objp-Objects;
7396 aip->guard_objnum = other_objnum;
7397 aip->guard_signature = other_objp->signature;
7398 aip->guard_wingnum = -1;
7400 aip->mode = AIM_GUARD;
7401 aip->submode = AIS_GUARD_STATIC;
7403 SDL_assert(other_objnum >= 0); // Hmm, bogus object and we need its position for guard_vec.
7405 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7406 ai_set_guard_vec(objp, &Objects[other_objnum]);
7408 ai_set_goal_maybe_abort_dock(objp, aip);
7409 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7413 // Update the aspect_locked_time field based on whether enemy is in view cone.
7414 // Also set/clear AIF_SEEK_LOCK.
7415 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7418 int num_weapon_types;
7419 int weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7424 shipp = &Ships[aip->shipnum];
7425 swp = &shipp->weapons;
7427 // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7428 if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7432 num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7434 wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7436 if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7437 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7438 aip->ai_flags |= AIF_SEEK_LOCK;
7440 aip->ai_flags &= ~AIF_SEEK_LOCK;
7442 // Update locking information for aspect seeking missiles.
7443 aip->current_target_is_locked = 0;
7444 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.v.fvec);
7446 float needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); // Replaced MIN_TRACKABLE_DOT with 0.9f
7447 if (dot_to_enemy > needed_dot) {
7448 aip->aspect_locked_time += flFrametime;
7449 // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7450 if (aip->aspect_locked_time >= wip->min_lock_time) {
7451 aip->aspect_locked_time = wip->min_lock_time;
7452 aip->current_target_is_locked = 1;
7455 aip->aspect_locked_time -= flFrametime*2;
7456 // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7457 if (aip->aspect_locked_time < 0.0f)
7458 aip->aspect_locked_time = 0.0f;
7460 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7463 aip->current_target_is_locked = 0;
7464 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7465 aip->ai_flags &= ~AIF_SEEK_LOCK;
7470 // We're in chase mode and we've recently collided with our target.
7471 // Fly away from it!
7472 void ai_chase_fly_away(object *objp, ai_info *aip)
7476 if (aip->ai_flags & AIF_TARGET_COLLISION) {
7477 aip->ai_flags &= ~AIF_TARGET_COLLISION; // Don't process this hit again next frame.
7478 aip->submode = SM_FLY_AWAY; // Focus on avoiding target
7479 aip->submode_start_time = Missiontime;
7482 if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7486 if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7487 aip->last_attack_time = Missiontime;
7488 aip->submode = SM_ATTACK;
7489 aip->submode_start_time = Missiontime;
7494 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7496 dot = vm_vec_dot(&objp->orient.v.fvec, &v2e);
7498 accelerate_ship(aip, 1.0f);
7500 accelerate_ship(aip, 1.0f - dot);
7501 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7505 // Return bank index of favored secondary weapon.
7506 // Return -1 if nothing favored.
7507 // "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7508 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7510 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7513 for (i=0; i<swp->num_secondary_banks; i++) {
7514 if (swp->secondary_bank_capacity[i] > 0) {
7515 if (swp->secondary_bank_ammo[i] > 0) {
7516 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7526 // Choose which secondary weapon to fire.
7527 // Note, this is not like ai_select_secondary_weapon(). "choose" means make a choice.
7528 // "select" means execute an order. Get it?
7529 // This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7530 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7532 float subsystem_strength = 0.0f;
7533 int is_big_ship, priority1, priority2;
7537 if ( en_objp->type == OBJ_SHIP ) {
7538 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7543 swp = &Ships[objp->instance].weapons;
7545 // AL 3-5-98: do a quick out if the ship has no secondaries
7546 if ( swp->num_secondary_banks <= 0 ) {
7547 swp->current_secondary_bank = -1;
7551 int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7553 if (preferred_secondary != -1) {
7554 if (swp->current_secondary_bank != preferred_secondary) {
7555 aip->current_target_is_locked = 0;
7556 aip->aspect_locked_time = 0.0f;
7557 swp->current_secondary_bank = preferred_secondary;
7559 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7560 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7562 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7563 if (aip->targeted_subsys) {
7564 subsystem_strength = aip->targeted_subsys->current_hits;
7568 is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7574 priority1 = WIF_HUGE;
7575 priority2 = WIF_HOMING;
7576 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7577 priority1 = WIF_BOMBER_PLUS;
7578 priority2 = WIF_HOMING;
7579 } else if (subsystem_strength > 100.0f) {
7580 priority1 = WIF_PUNCTURE;
7581 priority2 = WIF_HOMING;
7583 priority1 = WIF_HOMING;
7587 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7590 // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7593 // Return time, in seconds, at which this ship can next fire its current secondary weapon.
7594 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7596 float t = swip->fire_wait; // Base delay for this weapon.
7597 if (shipp->team == Player_ship->team) {
7598 // On player's team, _lower_ skill level = faster firing
7599 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7600 } else { // Not on player's team, higher skill level = faster firing
7601 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7604 t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7605 t *= frand_range(0.8f, 1.2f);
7607 // For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7610 t = t * 2.0f + 2.0f;
7616 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7620 // head straight toward him and maybe circle later
7621 vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7623 // get distance to goal
7624 dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7627 if (dist_to_goal > 400.0f) {
7630 *accel = dist_to_goal/400.0f;
7634 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7636 get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7641 // get the current and desired horizontal separations between target
7642 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7644 float temp, r_target, r_attacker, h_attacker, h_target;
7646 vector vec_to_target;
7649 // get parameters of ships (as cylinders - radius and height)
7650 // get radius of attacker (for rotations about forward)
7651 pm = model_get(Ships[attack_objp->instance].modelnum);
7652 temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7653 r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7654 r_attacker = max(temp, r_attacker);
7655 h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7657 // get radius of target (for rotations about forward)
7658 pm = model_get(Ships[attack_objp->instance].modelnum);
7659 temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7660 r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7661 r_target = max(temp, r_target);
7662 h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7664 // find separation between cylinders [if parallel]
7665 vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7667 // find the distance between centers along forward direction of ships
7668 perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7670 // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7671 vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.v.fvec, -perp_dist);
7672 *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7674 // choose "optimal" separation of 1000 + r_target + r_attacker
7675 *desired_separation = 1000 + r_target + r_attacker;
7678 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7681 float temp, r_target, r_attacker, h_attacker, h_target;
7682 float separation, optimal_separation;
7683 vector horz_vec_to_target;
7686 // get parameters of ships (as cylinders - radius and height)
7687 // get radius of attacker (for rotations about forward)
7688 pm = model_get(Ships[attack_objp->instance].modelnum);
7689 temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7690 r_attacker = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7691 r_attacker = max(temp, r_attacker);
7692 h_attacker = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7694 // get radius of target (for rotations about forward)
7695 pm = model_get(Ships[attack_objp->instance].modelnum);
7696 temp = max(pm->maxs.xyz.x, pm->maxs.xyz.y);
7697 r_target = max(-pm->mins.xyz.x, -pm->mins.xyz.y);
7698 r_target = max(temp, r_target);
7699 h_target = max(-pm->mins.xyz.z, pm->maxs.xyz.z);
7701 // are we opposing (only when other ship is not moving)
7702 opposing = ( vm_vec_dotprod(&attack_objp->orient.v.fvec, &target_objp->orient.v.fvec) < 0 );
7704 ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7706 // choose dist (2000) so that we don't bash
7712 // set the goal pos as dist forward from target along target forward
7713 vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.v.fvec, dist);
7714 // then add horizontal separation
7715 vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7717 // find the distance between centers along forward direction of ships
7718 vector vec_to_target;
7719 vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7720 float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.v.fvec);
7722 float match_accel = target_objp->phys_info.vel.xyz.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.xyz.z;
7723 float length_scale = attack_objp->radius;
7725 // if we're heading toward enemy ship, we want to keep going if we're ahead
7727 perp_dist = -perp_dist;
7730 if (perp_dist > 0) {
7731 // falling behind, so speed up
7732 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7734 // up in front, so slow down
7735 *accel = match_accel - match_accel / length_scale * -perp_dist;
7736 *accel = max(0.0f, *accel);
7742 // Return *goal_pos for one cruiser to attack another (big ship).
7743 // Choose point fairly nearby that is not occupied by another cruiser.
7744 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7748 aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7751 switch (aip->submode) {
7752 case SM_BIG_APPROACH:
7753 // do approach stuff;
7754 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7759 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7762 case SM_BIG_PARALLEL:
7763 // do parallel stuff
7764 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7769 int maybe_hack_cruiser_chase_abort()
7771 ship *shipp = &Ships[Pl_objp->instance];
7772 ship *eshipp = &Ships[En_objp->instance];
7773 ai_info *aip = &Ai_info[shipp->ai_index];
7775 // mission sm3-08, sathanos chasing collosus
7776 if ( SDL_strcasecmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7777 if (( SDL_strcasecmp(eshipp->ship_name, "colossus") == 0 ) || ( SDL_strcasecmp(shipp->ship_name, "colossus") == 0 )) {
7778 // Changed so all big ships attacking the Colossus will not do the chase code.
7779 // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7780 //if ( SDL_strcasecmp(shipp->ship_name, "Sathanas") == 0 ) {
7781 // do cool hack stuff here
7782 ai_clear_ship_goals( aip );
7783 aip->mode = AIM_NONE;
7792 // Make a big ship pursue another big ship.
7793 // (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7794 void ai_cruiser_chase()
7796 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7797 ship *shipp = &Ships[Pl_objp->instance];
7798 ai_info *aip = &Ai_info[shipp->ai_index];
7800 if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7801 Int3(); // Hmm, not a very big ship, how did we get in this function?
7802 aip->mode = AIM_NONE;
7806 if (En_objp->type != OBJ_SHIP) {
7811 if (En_objp->instance < 0) {
7819 eshipp = &Ships[En_objp->instance];
7820 esip = &Ship_info[eshipp->ship_info_index];
7822 if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7823 // Int3(); // Hmm, we're big and we're pursuing something other than a big ship?
7824 aip->mode = AIM_NONE;
7829 float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7831 // kamikaze - ram and explode
7832 if (aip->ai_flags & AIF_KAMIKAZE) {
7833 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7834 accelerate_ship(aip, 1.0f);
7837 // really track down and chase
7839 // check valid submode
7840 SDL_assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7842 // just entering, approach enemy ship
7843 if (aip->submode == SM_ATTACK) {
7844 aip->submode = SM_BIG_APPROACH;
7849 vector *rvecp = NULL;
7851 switch (aip->submode) {
7852 case SM_BIG_APPROACH:
7853 // do approach stuff;
7854 ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7860 ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7864 case SM_BIG_PARALLEL:
7865 // do parallel stuff
7866 ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7872 // now move as desired
7873 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7874 accelerate_ship(aip, accel);
7877 // maybe switch to new mode
7878 vector vec_to_enemy;
7879 float dist_to_enemy;
7880 int moving = (En_objp->phys_info.vel.xyz.z > 0.5f);
7881 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7882 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7884 switch (aip->submode) {
7885 case SM_BIG_APPROACH:
7886 if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7889 // if within 90 degrees of en forward, go into parallel, otherwise circle
7890 if ( vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0 ) {
7891 aip->submode = SM_BIG_PARALLEL;
7896 if ( !maybe_hack_cruiser_chase_abort() ) {
7897 aip->submode = SM_BIG_CIRCLE;
7906 float desired_sep, cur_sep;
7907 // we're behind the enemy ship
7908 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7909 // and we're turning toward the enemy
7910 if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7912 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7913 // and the separation is > 0.9 desired
7914 if (cur_sep > 0.9 * desired_sep) {
7915 aip->submode = SM_BIG_PARALLEL;
7922 float desired_sep, cur_sep;
7923 // we're behind the enemy ship
7924 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.v.fvec) > 0) {
7925 // and we're turning toward the enemy
7926 if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) > 0) {
7928 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7929 //and the separation is [0.9 to 1.1] desired
7930 if ( (cur_sep > 0.9f * desired_sep) ) {
7931 aip->submode = SM_BIG_PARALLEL;
7937 // and we're turning toward the enemy
7938 if (vm_vec_dotprod(&En_objp->orient.v.fvec, &Pl_objp->orient.v.fvec) < 0) {
7940 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7941 //and the separation is [0.9 to 1.1] desired
7942 if ( (cur_sep > 0.9f * desired_sep) ) {
7943 aip->submode = SM_BIG_PARALLEL;
7950 case SM_BIG_PARALLEL:
7952 if ( vm_vec_dotprod(&Pl_objp->orient.v.fvec, &En_objp->orient.v.fvec) < 0 ) {
7953 // and the other ship is moving
7955 // and we no longer overlap
7956 if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7957 aip->submode = SM_BIG_APPROACH;
7966 // --------------------------------------------------------------------------
7967 // Make object Pl_objp chase object En_objp
7970 float dist_to_enemy, time_to_enemy;
7971 float dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7972 vector player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7973 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7974 ship *shipp = &Ships[Pl_objp->instance];
7975 ship_weapon *swp = &shipp->weapons;
7976 ai_info *aip = &Ai_info[shipp->ai_index];
7977 int enemy_sip_flags;
7979 if (aip->mode != AIM_CHASE) {
7983 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7988 if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7989 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7990 aip->mode = AIM_NONE;
7994 //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7996 if ( En_objp->type == OBJ_SHIP ) {
7997 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7999 enemy_sip_flags = 0;
8002 if ( enemy_sip_flags > 0 ) {
8003 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
8009 // If collided with target_objnum last frame, avoid that ship.
8010 // This should prevent the embarrassing behavior of ships getting stuck on each other
8011 // as if they were magnetically attracted. -- MK, 11/13/97.
8012 if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
8013 ai_chase_fly_away(Pl_objp, aip);
8017 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
8018 dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
8019 time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
8020 vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
8022 vm_vec_normalize(&real_vec_to_enemy);
8024 real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.v.fvec);
8026 int is_stealthy_ship = 0;
8027 if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
8028 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
8029 is_stealthy_ship = 1;
8033 // Can only acquire lock on a target that isn't hidden from sensors
8034 if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
8035 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
8037 aip->current_target_is_locked = 0;
8038 aip->ai_flags &= ~AIF_SEEK_LOCK;
8041 // If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
8042 // If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
8043 if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
8044 predicted_enemy_pos = enemy_pos;
8046 // Set predicted_enemy_pos.
8047 // See if attacking a subsystem.
8048 if (aip->targeted_subsys != NULL) {
8049 SDL_assert(En_objp->type == OBJ_SHIP);
8050 ship_info *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
8051 if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
8054 if (aip->targeted_subsys != NULL) {
8055 get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
8056 predicted_enemy_pos = enemy_pos;
8057 predicted_vec_to_enemy = real_vec_to_enemy;
8059 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8060 set_target_objnum(aip, -1);
8062 // nprintf(("AI", "Attacking subsystem: rval = %i, pos = %7.3f %7.3f %7.3f\n", rval, predicted_enemy_pos.xyz.x, predicted_enemy_pos.xyz.y, predicted_enemy_pos.xyz.z));
8065 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8066 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", predicted_enemy_pos.xyz.x, predicted_enemy_pos.xyz.y, predicted_enemy_pos.xyz.z));
8069 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8073 vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8075 vm_vec_normalize(&predicted_vec_to_enemy);
8077 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.v.fvec, &predicted_vec_to_enemy);
8078 dot_from_enemy= - vm_vec_dot(&En_objp->orient.v.fvec, &real_vec_to_enemy);
8081 // Set turn and acceleration based on submode.
8083 switch (aip->submode) {
8084 case SM_CONTINUOUS_TURN:
8088 case SM_STEALTH_FIND:
8092 case SM_STEALTH_SWEEP:
8097 case SM_SUPER_ATTACK:
8098 case SM_ATTACK_FOREVER:
8099 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8100 if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8104 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8107 case SM_EVADE_SQUIGGLE:
8108 ai_chase_es(aip, sip);
8111 case SM_EVADE_BRAKE:
8112 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8124 get_behind_ship(aip, sip, dist_to_enemy);
8127 case SM_GET_AWAY: // Used to get away from opponent to prevent endless circling.
8128 ai_chase_ga(aip, sip);
8131 case SM_EVADE_WEAPON:
8137 aip->last_attack_time = Missiontime;
8138 aip->submode = SM_ATTACK;
8139 aip->submode_start_time = Missiontime;
8143 // Maybe choose a new submode.
8145 if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8146 // If a very long time since attacked, attack no matter what!
8147 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8148 if (Missiontime - aip->last_attack_time > i2f(6)) {
8149 aip->submode = SM_SUPER_ATTACK;
8150 aip->submode_start_time = Missiontime;
8151 aip->last_attack_time = Missiontime;
8155 // If a collision is expected, pull out!
8156 // If enemy is pointing away and moving a bit, don't worry about collision detection.
8157 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8158 if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8159 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8160 accelerate_ship(aip, -1.0f);
8162 aip->submode = SM_AVOID;
8163 aip->submode_start_time = Missiontime;
8169 switch (aip->submode) {
8170 case SM_CONTINUOUS_TURN:
8171 if (Missiontime - aip->submode_start_time > i2f(3)) {
8172 aip->last_attack_time = Missiontime;
8173 aip->submode = SM_ATTACK;
8174 aip->submode_start_time = Missiontime;
8179 // if taraget is stealth and stealth not visible, then enter stealth find mode
8180 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8181 aip->submode = SM_STEALTH_FIND;
8182 aip->submode_start_time = Missiontime;
8183 aip->submode_parm0 = SM_SF_AHEAD;
8184 } else if (ai_near_full_strength(Pl_objp, sip) && (Missiontime - aip->last_hit_target_time > i2f(3)) && (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.5f)) {
8185 aip->submode = SM_SUPER_ATTACK;
8186 aip->submode_start_time = Missiontime;
8187 aip->last_attack_time = Missiontime;
8188 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8189 (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8190 (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8191 aip->submode = SM_GET_AWAY;
8192 aip->submode_start_time = Missiontime;
8193 aip->last_hit_target_time = Missiontime;
8194 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8195 && (dot_to_enemy < dot_from_enemy)
8196 && (En_objp->phys_info.speed > 15.0f)
8197 && (dist_to_enemy < 200.0f)
8198 && (dist_to_enemy > 50.0f)
8199 && (dot_to_enemy < 0.1f)
8200 && (Missiontime - aip->submode_start_time > i2f(2))) {
8201 aip->submode = SM_EVADE_BRAKE;
8202 aip->submode_start_time = Missiontime;
8203 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8204 aip->submode = SM_GET_BEHIND;
8205 aip->submode_start_time = Missiontime;
8206 } 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)) {
8207 if ((Missiontime - aip->last_hit_target_time > i2f(5)) && (frand() < (float) (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS))) { aip->submode = SM_GET_AWAY;
8208 aip->submode_start_time = Missiontime;
8209 aip->last_hit_target_time = Missiontime;
8211 aip->submode = SM_EVADE_SQUIGGLE;
8212 aip->submode_start_time = Missiontime;
8214 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8215 if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8216 if (frand() > 0.5f) {
8217 aip->submode = SM_CONTINUOUS_TURN;
8218 aip->submode_parm0 = myrand() & 0x0f;
8219 aip->submode_start_time = Missiontime;
8221 aip->submode = SM_EVADE;
8222 aip->submode_start_time = Missiontime;
8225 aip->submode_start_time = Missiontime;
8229 aip->last_attack_time = Missiontime;
8233 case SM_EVADE_SQUIGGLE:
8234 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8235 if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8236 aip->submode = SM_EVADE_BRAKE;
8237 aip->submode_start_time = Missiontime;
8239 aip->last_attack_time = Missiontime;
8240 aip->submode = SM_ATTACK;
8241 aip->submode_start_time = Missiontime;
8246 case SM_EVADE_BRAKE:
8247 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8248 aip->submode = SM_AVOID;
8249 aip->submode_start_time = Missiontime;
8250 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8251 aip->last_attack_time = Missiontime;
8252 aip->submode = SM_ATTACK;
8253 aip->submode_start_time = Missiontime;
8254 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8255 aip->last_attack_time = Missiontime;
8256 aip->submode = SM_ATTACK;
8257 aip->submode_start_time = Missiontime;
8262 // Modified by MK on 5/5/97 to keep trying to regain attack mode. It's what a human would do.
8263 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8264 aip->last_attack_time = Missiontime;
8265 aip->submode = SM_EVADE_BRAKE;
8266 aip->submode_start_time = Missiontime;
8267 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8268 && (Missiontime > aip->submode_start_time + i2f(1)))
8269 || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8270 aip->last_attack_time = Missiontime;
8271 aip->submode = SM_ATTACK;
8272 aip->submode_start_time = Missiontime;
8273 } else if (Missiontime - aip->submode_start_time > i2f(2))
8274 if (dot_from_enemy > 0.8f) {
8275 aip->submode = SM_EVADE_SQUIGGLE;
8276 aip->submode_start_time = Missiontime;
8281 case SM_SUPER_ATTACK:
8282 // if stealth and invisible, enter stealth find mode
8283 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8284 aip->submode = SM_STEALTH_FIND;
8285 aip->submode_start_time = Missiontime;
8286 aip->submode_parm0 = SM_SF_AHEAD;
8287 } 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) )) {
8288 aip->ai_flags &= ~AIF_ATTACK_SLOWLY; // Just in case, clear here.
8290 switch (myrand() % 5) {
8292 aip->submode = SM_CONTINUOUS_TURN;
8293 aip->submode_start_time = Missiontime;
8296 aip->submode_start_time = Missiontime; // Stay in super attack mode
8300 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8301 aip->submode = SM_GET_AWAY;
8302 aip->submode_start_time = Missiontime;
8304 aip->submode = SM_EVADE;
8305 aip->submode_start_time = Missiontime;
8309 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) { // Less likely to GET_AWAY at lower skill levels.
8310 aip->submode = SM_EVADE;
8311 aip->submode_start_time = Missiontime;
8313 aip->submode = SM_GET_AWAY;
8314 aip->submode_start_time = Missiontime;
8318 Int3(); // Impossible!
8322 aip->last_attack_time = Missiontime;
8327 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8328 aip->submode_start_time = Missiontime;
8329 } else if (Missiontime - aip->submode_start_time > i2f(1)/2) {
8330 if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8331 aip->submode_start_time = Missiontime;
8333 aip->submode = SM_GET_BEHIND;
8334 aip->submode_start_time = Missiontime;
8341 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8342 aip->submode = SM_ATTACK;
8343 aip->submode_start_time = Missiontime;
8344 aip->last_attack_time = Missiontime;
8349 if (Missiontime - aip->submode_start_time > i2f(2)) {
8352 rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f; // Some value in 200..500
8353 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8354 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8355 aip->submode = SM_ATTACK;
8356 aip->time_enemy_in_range = 2.0f; // Cheat. Presumably if they were running away from you, they were monitoring you!
8357 aip->submode_start_time = Missiontime;
8358 aip->last_attack_time = Missiontime;
8363 case SM_EVADE_WEAPON:
8364 if (aip->danger_weapon_objnum == -1) {
8365 aip->submode = SM_ATTACK;
8366 aip->submode_start_time = Missiontime;
8367 aip->last_attack_time = Missiontime;
8371 // Either change to SM_ATTACK or AIM_FIND_STEALTH
8372 case SM_STEALTH_FIND:
8373 // if time > 5 sec change mode to sweep
8374 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8375 aip->submode = SM_ATTACK;
8376 aip->submode_start_time = Missiontime;
8377 aip->last_attack_time = Missiontime;
8378 // sweep if I can't find in 5 sec or bail from find
8379 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8381 aip->submode = SM_STEALTH_SWEEP;
8382 aip->submode_start_time = Missiontime;
8383 aip->last_attack_time = Missiontime;
8384 aip->submode_parm0 = SM_SS_SET_GOAL;
8388 case SM_STEALTH_SWEEP:
8389 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8390 aip->submode = SM_ATTACK;
8391 aip->submode_start_time = Missiontime;
8392 aip->last_attack_time = Missiontime;
8393 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8394 // go back to find mode
8395 aip->submode = SM_STEALTH_FIND;
8396 aip->submode_start_time = Missiontime;
8397 aip->submode_parm0 = SM_SF_AHEAD;
8398 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8399 // set target objnum = -1
8400 set_target_objnum(aip, -1);
8402 // set submode to attack
8403 aip->submode = SM_ATTACK;
8404 aip->submode_start_time = Missiontime;
8405 aip->last_attack_time = Missiontime;
8409 case SM_ATTACK_FOREVER: // Engines blown, just attack.
8414 aip->submode = SM_ATTACK;
8415 aip->last_attack_time = Missiontime;
8417 aip->submode_start_time = Missiontime;
8421 // Maybe fire primary weapon and update time_enemy_in_range
8423 //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8425 if (aip->mode != AIM_EVADE) {
8426 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8427 aip->time_enemy_in_range += flFrametime;
8429 // Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8430 // and also the size of the target relative to distance to target.
8431 if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8436 temp_shipp = &Ships[Pl_objp->instance];
8437 tswp = &temp_shipp->weapons;
8438 if ( tswp->num_primary_banks > 0 ) {
8440 SDL_assert(tswp->current_primary_bank < tswp->num_primary_banks);
8441 weapon_info *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8443 // Less likely to fire if far away and moving.
8444 scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8446 scale = (scale - 0.6f) * 1.5f;
8449 if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8450 ai_fire_primary_weapon(Pl_objp);
8453 // Don't fire secondaries at a protected ship.
8454 if (!(En_objp->flags & OF_PROTECTED)) {
8455 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8456 int current_bank = tswp->current_secondary_bank;
8457 weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8459 if (current_bank > -1) {
8460 if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8461 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8462 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8466 if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8467 if (tswp->current_secondary_bank >= 0) {
8468 weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8471 if (swip->wi_flags & WIF_BOMB)
8472 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8474 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8476 // reduce firing range in nebula
8477 extern int Nebula_sec_range;
8478 if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8479 firing_range *= 0.8f;
8482 // If firing a spawn weapon, distance doesn't matter.
8485 if (swip->wi_flags & WIF_SPAWN) {
8488 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8492 else if (count >= 1) {
8493 float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8495 if (hull_percent < 0.01f)
8496 hull_percent = 0.01f;
8498 if (frand() < 0.25f/(30.0f*hull_percent) * count) // With timestamp below, this means could fire in 30 seconds if one enemy.
8503 if (spawn_fire || (dist_to_enemy < firing_range)) {
8504 if (ai_fire_secondary_weapon(Pl_objp)) {
8505 // Only if weapon was fired do we specify time until next fire. If not fired, done in ai_fire_secondary...
8508 if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8509 t = swip->fire_wait;
8511 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8513 //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8514 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8517 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8526 aip->time_enemy_in_range *= (1.0f - flFrametime);
8529 aip->time_enemy_in_range *= (1.0f - flFrametime);
8533 // Make the object *objp move so that the point *dp on the object moves towards the point *vp
8535 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8537 physics_info *pi = &objp->phys_info;
8538 float dist; // dist to goal
8539 vector v2g; // vector to goal
8540 vector abs_pnt; // location of dock point, ie objp->pos + db
8543 abs_pnt = objp->pos;
8545 vm_vec_add(&abs_pnt, &objp->pos, dp);
8547 dist = vm_vec_dist_quick(vp, &abs_pnt);
8551 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8552 speed = fl_sqrt(dist) * speed_scale;
8553 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8554 speed += other_obj_speed;
8556 speed += MAX_REPAIR_SPEED*0.75f;
8558 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8560 vm_vec_zero(&pi->desired_vel);
8563 // Set the orientation in the global reference frame for an object to attain
8564 // to dock with another object.
8565 // *dom resultant global matrix
8566 // *db_dest pointer to destination docking bay information
8567 // *db_src pointer to source docking bay information
8568 // *dorient pointer to global orientation of docking bay (ie, the dockee object's orient)
8569 // *sorient pointer to global orientation of docker
8570 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8575 // Compute the global orientation of the docker's (dest) docking bay.
8576 fvec = db_dest->norm[0];
8577 vm_vec_negate(&fvec);
8579 vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8580 vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8582 vm_matrix_x_matrix(&m3, dorient, &m1);
8584 // Compute the matrix given by the source docking bay.
8585 // Pre-multiply the orientation of the source object (sorient) by the transpose
8586 // of the docking bay's orientation, ie unrotate the source object's matrix.
8587 fvec = db_src->norm[0];
8588 vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8589 vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8592 vm_matrix_x_matrix(dom, &m3, &m2);
8595 #define DOCK_BACKUP_RETURN_VAL 99999.9f
8597 // Make objp dock with dobjp
8598 // Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8599 // DOA_APPROACH means approach point aip->path_cur
8600 // DOA_DOCK means dock
8601 // DOA_UNDOCK_1 means undock, moving to point nearest dock bay
8602 // DOA_UNDOCK_2 means undock, moving to point nearest dock bay and facing away from ship
8603 // DOA_DOCK_STAY means rigidly maintain position in dock bay.
8604 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8606 ship_info *sip0, *sip1;
8607 polymodel *pm0, *pm1;
8610 vector goal_point, docker_point;
8611 float fdist = UNINITIALIZED_VALUE;
8612 int docker_index, dockee_index; // index into docking_bays[] array for objects docking
8613 // docker is Pl_objp -- dockee is dobjp
8614 aip = &Ai_info[Ships[objp->instance].ai_index];
8616 // If dockee has moved much, then path will be recreated.
8617 // Might need to change state if moved too far.
8618 if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8619 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8620 /* if (dock_mode == DOA_APPROACH) {
8621 return DOCK_BACKUP_RETURN_VAL;
8622 } else if (dock_mode == DOA_DOCK) {
8623 return DOCK_BACKUP_RETURN_VAL;
8628 objp->phys_info.forward_thrust = 0.0f; // Kill thrust so we don't have a sputtering thruster.
8630 sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8631 sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8632 pm0 = model_get( sip0->modelnum );
8633 pm1 = model_get( sip1->modelnum );
8635 docker_index = aip->dock_index;
8636 dockee_index = aip->dockee_index;
8638 SDL_assert( docker_index >= 0 );
8639 SDL_assert( dockee_index >= 0 );
8641 SDL_assert(pm0->docking_bays[docker_index].num_slots == 2);
8642 SDL_assert(pm1->docking_bays[dockee_index].num_slots == 2);
8644 float speed_scale = 1.0f;
8645 if (sip0->flags & SIF_SUPPORT) {
8649 switch (dock_mode) {
8652 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8656 // Compute the desired global orientation matrix for the docker's station.
8657 // That is, the normal vector of the docking station must be the same as the
8658 // forward vector and the vector between its two points must be the uvec.
8659 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8661 // Compute new orientation matrix and update rotational velocity.
8662 vector w_in, w_out, vel_limit, acc_limit;
8663 float tdist, mdist, ss1;
8665 w_in = objp->phys_info.rotvel;
8666 vel_limit = objp->phys_info.max_rotvel;
8667 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8669 if (sip0->flags & SIF_SUPPORT)
8670 vm_vec_scale(&acc_limit, 2.0f);
8672 // 1 at end of line prevent overshoot
8673 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8674 objp->phys_info.rotvel = w_out;
8677 // Translate towards goal and note distance to goal.
8678 goal_point = Path_points[aip->path_cur].pos;
8679 mdist = ai_matrix_dist(&objp->orient, &dom);
8680 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8682 // If translation is badly lagging rotation, speed up translation.
8684 ss1 = tdist/(10.0f * mdist);
8690 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8691 speed_scale *= 1.0f + ss1;
8693 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8695 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8697 // Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8698 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8699 fdist += 2.0f * mdist;
8704 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8708 // Compute the desired global orientation matrix for the docker's station.
8709 // That is, the normal vector of the docking station must be the same as the
8710 // forward vector and the vector between its two points must be the uvec.
8711 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8713 // Compute distance between dock bay points.
8714 vector db0, db1, db2, db3;
8716 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8717 vm_vec_add2(&db0, &objp->pos);
8719 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8720 vm_vec_add2(&db1, &objp->pos);
8722 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8723 vm_vec_add2(&db2, &dobjp->pos);
8725 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8726 vm_vec_add2(&db3, &dobjp->pos);
8728 vm_vec_avg(&goal_point, &db2, &db3);
8730 vm_vec_avg(&docker_point, &db0, &db1);
8731 vm_vec_sub2(&docker_point, &objp->pos);
8733 if (dock_mode == DOA_DOCK) {
8735 vector w_in, w_out, vel_limit, acc_limit;
8737 fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8739 // Compute new orientation matrix and update rotational velocity.
8740 w_in = objp->phys_info.rotvel;
8741 vel_limit = objp->phys_info.max_rotvel;
8742 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8744 if (sip0->flags & SIF_SUPPORT)
8745 vm_vec_scale(&acc_limit, 2.0f);
8747 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8748 objp->phys_info.rotvel = w_out;
8751 // Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8752 fdist += 10.0f * vm_vec_mag_quick(&w_out);
8754 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8756 SDL_assert(dock_mode == DOA_DOCK_STAY);
8759 vm_vec_sub(&temp, &goal_point, &docker_point);
8760 vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8764 case DOA_UNDOCK_1: {
8765 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8770 // Move to point on dock path nearest to dock station.
8771 SDL_assert(aip->path_length >= 2);
8772 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8774 vm_vec_zero(&docker_point);
8775 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8777 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8782 case DOA_UNDOCK_2: {
8784 // Move to point on dock path nearest to dock station and orient away from big ship.
8787 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8791 SDL_assert(aip->path_length >= 2);
8792 // if (aip->path_length >= 3)
8793 // desired_index = aip->path_length-3;
8795 desired_index = aip->path_length-2;
8797 goal_point = Path_points[aip->path_start + desired_index].pos;
8799 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8801 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8804 case DOA_UNDOCK_3: {
8805 float dist, goal_dist;
8808 goal_dist = objp->radius + dobjp->radius + 25.0f;
8810 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8811 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8812 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8816 float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8817 ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8819 dot = vm_vec_dot(&objp->orient.v.fvec, &away_vec);
8823 if (dist > goal_dist/2)
8824 accel *= 1.2f - 0.5f*goal_dist/dist;
8826 accelerate_ship(aip, accel);
8827 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8835 // For debug purposes, compute global orientation of both dock vectors and show
8836 // how close they are.
8839 vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8840 vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8842 //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n",
8843 // vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8844 // vm_vec_dot(&objp->orient.v.fvec, &dom.v.fvec),
8845 // vm_vec_dot(&d0, &d1)));
8848 // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: SDL_assert(fdist != UNINITIALIZED_VALUE);
8853 void debug_find_guard_object()
8855 ship *shipp = &Ships[Pl_objp->instance];
8858 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8859 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8860 if (objp->instance != -1) {
8861 if (Ships[objp->instance].team == shipp->team) {
8862 // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8863 ai_set_guard_object(Pl_objp, objp);
8871 // Given an object number, return the number of ships attacking it.
8872 int num_ships_attacking(int objnum)
8878 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8879 objp = &Objects[so->objnum];
8880 if (objp->instance != -1) {
8882 aip = &Ai_info[Ships[objp->instance].ai_index];
8884 if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8885 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8893 // For all objects attacking object #objnum, remove the one that is farthest away.
8894 // Do this by resuming previous behavior, if any. If not, set target_objnum to -1.
8895 void remove_farthest_attacker(int objnum)
8897 object *objp, *objp2, *farthest_objp;
8899 float farthest_dist;
8901 objp2 = &Objects[objnum];
8903 farthest_dist = 9999999.9f;
8904 farthest_objp = NULL;
8906 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8907 objp = &Objects[so->objnum];
8908 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8909 if (objp->instance != -1) {
8912 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8914 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8915 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8918 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8919 if (dist < farthest_dist) {
8920 farthest_dist = dist;
8921 farthest_objp = objp;
8929 if (farthest_objp != NULL) {
8931 SDL_assert(farthest_objp->type == OBJ_SHIP);
8932 SDL_assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8933 SDL_assert(Ships[farthest_objp->instance].ai_index > -1);
8935 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8937 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8938 // If already ignoring something under player's orders, don't ignore current target.
8939 if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8940 aip->ignore_objnum = aip->target_objnum;
8941 aip->ignore_signature = Objects[aip->target_objnum].signature;
8942 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8943 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000); // OK to attack again in 20 to 24 seconds.
8945 aip->target_objnum = -1;
8946 ai_do_default_behavior(farthest_objp);
8951 // Maybe limit the number of attackers on attack_objnum. For now, only limit attackers
8952 // in attacked_objnum is the player
8953 // input: attacked_objnum => object index for ship we want to limit attacks on
8955 // exit: 1 => num attackers exceeds maximum, abort
8956 // 0 => removed the farthest attacker
8957 // -1 => nothing was done
8958 int ai_maybe_limit_attackers(int attacked_objnum)
8962 // limit the number of ships attacking the _player_ only
8963 // if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8964 if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8966 num_attacking = num_ships_attacking(attacked_objnum);
8968 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8969 remove_farthest_attacker(attacked_objnum);
8971 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8974 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8980 // Object being guarded by object *guard_objp was hit by object *hitter_objp
8981 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8986 aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8988 if (guard_objp == hitter_objp) {
8989 // Int3(); // Bogus! Who tried to get me to attack myself! Trace out and fix!
8993 if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8996 if (aip->ai_flags & AIF_NO_DYNAMIC) // Not allowed to pursue dynamic goals. So, why are we guarding?
8999 SDL_assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
9001 hitter_objnum = OBJ_INDEX(hitter_objp);
9003 if ( hitter_objp->type == OBJ_SHIP ) {
9004 // If the hitter object is the ignore object, don't attack it.
9005 if (is_ignore_object(aip, hitter_objp-Objects))
9008 // If hitter is on same team as me, don't attack him.
9009 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
9012 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9013 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9017 // dont attack if you can't see him
9018 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
9019 // if he's a stealth and visible, but not targetable, ok to attack.
9020 if ( is_object_stealth_ship(hitter_objp) ) {
9021 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
9028 if (aip->target_objnum == -1) {
9029 aip->ok_to_target_timestamp = timestamp(0);
9032 if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
9034 if ( hitter_objp->type == OBJ_SHIP ) {
9035 if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
9039 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9040 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9045 if (aip->target_objnum != hitter_objnum) {
9046 aip->aspect_locked_time = 0.0f;
9049 aip->ok_to_target_timestamp = timestamp(0);
9051 set_target_objnum(aip, hitter_objnum);
9052 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9053 aip->previous_mode = AIM_GUARD;
9054 aip->previous_submode = aip->submode;
9055 aip->mode = AIM_CHASE;
9056 aip->submode = SM_ATTACK;
9057 aip->submode_start_time = Missiontime;
9058 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9059 } else if (aip->previous_mode == AIM_GUARD) {
9060 if (aip->target_objnum == -1) {
9062 if ( hitter_objp->type == OBJ_SHIP ) {
9063 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9064 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9069 set_target_objnum(aip, hitter_objnum);
9070 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9071 aip->mode = AIM_CHASE;
9072 aip->submode = SM_ATTACK;
9073 aip->submode_start_time = Missiontime;
9074 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9076 int num_attacking_cur, num_attacking_new;
9078 num_attacking_cur = num_ships_attacking(aip->target_objnum);
9079 if (num_attacking_cur > 1) {
9080 num_attacking_new = num_ships_attacking(hitter_objnum);
9082 if (num_attacking_new < num_attacking_cur) {
9084 if ( hitter_objp->type == OBJ_SHIP ) {
9085 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9086 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9090 set_target_objnum(aip, hitter_objp-Objects);
9091 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9092 aip->mode = AIM_CHASE;
9093 aip->submode = SM_ATTACK;
9094 aip->submode_start_time = Missiontime;
9095 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9102 // Ship object *hit_objp was hit by ship object *hitter_objp.
9103 // See if anyone is guarding hit_objp and, if so, do something useful.
9104 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9109 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9110 objp = &Objects[so->objnum];
9111 if (objp->instance != -1) {
9113 aip = &Ai_info[Ships[objp->instance].ai_index];
9115 if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9116 if (aip->guard_objnum == hit_objp-Objects) {
9117 guard_object_was_hit(objp, hitter_objp);
9118 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9119 guard_object_was_hit(objp, hitter_objp);
9126 // Scan missile list looking for bombs homing on guarded_objp
9127 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9128 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9131 object *bomb_objp, *closest_bomb_objp=NULL;
9132 float dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9136 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9137 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9138 bomb_objp = &Objects[mo->objnum];
9140 wp = &Weapons[bomb_objp->instance];
9141 wip = &Weapon_info[wp->weapon_info_index];
9143 if ( !(wip->wi_flags & WIF_BOMB) ) {
9147 if ( wp->homing_object != guarded_objp ) {
9151 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9153 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9154 dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9155 if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9156 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9157 closest_bomb_objp = bomb_objp;
9162 if ( closest_bomb_objp ) {
9163 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9170 // Scan enemy ships and see if one is near enough to guard object to be pursued.
9171 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9173 ship *guarding_shipp = &Ships[guarding_objp->instance];
9174 ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9179 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9180 enemy_objp = &Objects[so->objnum];
9182 if (enemy_objp->instance < 0) {
9186 ship *eshipp = &Ships[enemy_objp->instance];
9188 // Don't attack a cargo container or other harmless ships
9189 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9190 if (guarding_shipp->team != eshipp->team) {
9191 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9192 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9193 guard_object_was_hit(guarding_objp, enemy_objp);
9194 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9195 //nprintf(("AI", "%i: Enemy %s targeting guard object (%s), %s will attack!!\n", Framecount, eshipp->ship_name, Ships[Objects[guarding_aip->guard_objnum].instance].ship_name, guarding_shipp->ship_name));
9196 guard_object_was_hit(guarding_objp, enemy_objp);
9203 // Scan for nearby asteroids. Favor asteroids which have their collide_objnum set to that of the
9204 // guarded ship. Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9205 // when a ship blows up an asteroid then goes after the pieces that break off.
9206 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9210 object *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9211 float dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9213 for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9214 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9215 // Attack asteroid if near guarded ship
9216 dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9217 if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9218 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9219 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9220 if( dist_to_self < closest_danger_asteroid_dist ) {
9221 danger_asteroid_objp=asteroid_objp;
9222 closest_danger_asteroid_dist=dist_to_self;
9225 if ( dist_to_self < closest_asteroid_dist ) {
9226 // only attack if moving slower than own max speed
9227 if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.xyz.z ) {
9228 closest_asteroid_dist = dist_to_self;
9229 closest_asteroid_objp = asteroid_objp;
9236 if ( danger_asteroid_objp ) {
9237 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9238 } else if ( closest_asteroid_objp ) {
9239 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9243 // Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9244 void ai_guard_find_nearby_object()
9246 ship *shipp = &Ships[Pl_objp->instance];
9247 ai_info *aip = &Ai_info[shipp->ai_index];
9251 guardobjp = &Objects[aip->guard_objnum];
9253 // highest priority is a bomb fired on guarded ship
9254 bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9256 if ( !bomb_found ) {
9257 // check for ships if there are no bombs fired at guarded ship
9258 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9260 // if not attacking anything, go for asteroid close to guarded ship
9261 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9262 ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9267 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9268 // returns z of axis_point in cyl_objp reference frame
9269 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9271 SDL_assert(other_objp->type == OBJ_SHIP);
9272 SDL_assert(cyl_objp->type == OBJ_SHIP);
9274 // get radius of cylinder
9275 polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9277 tempx = max(-pm->mins.xyz.x, pm->maxs.xyz.x);
9278 tempy = max(-pm->mins.xyz.y, pm->maxs.xyz.y);
9279 *radius = max(tempx, tempy);
9281 // get vec from cylinder to other_obj
9283 vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9285 // get point on axis and on cylinder
9286 // extended_cylinder_z is along extended cylinder
9287 // cylinder_z is capped within cylinder
9288 float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.v.fvec);
9290 // get pt on axis of extended cylinder
9291 vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.v.fvec, extended_cylinder_z);
9293 // get r_vec (pos - axis_pt) normalized
9294 vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9296 return extended_cylinder_z;
9299 // handler for guard behavior when guarding BIG ships
9300 // When someone has attacked guarded ship, then attack that ship.
9301 // To attack another ship, switch out of guard mode into chase mode.
9305 ship *shipp = &Ships[Pl_objp->instance];
9306 ai_info *aip = &Ai_info[shipp->ai_index];
9309 // sanity checks already done in ai_guard()
9310 guard_objp = &Objects[aip->guard_objnum];
9312 switch (aip->submode) {
9313 case AIS_GUARD_STATIC:
9314 case AIS_GUARD_PATROL:
9316 vector axis_pt, r_vec, theta_vec;
9317 float radius, extended_z;
9319 // get random [0 to 1] based on OBJNUM
9320 float objval = static_randf(Pl_objp-Objects);
9322 // get position relative to cylinder of guard_objp
9323 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9324 vm_vec_crossprod(&theta_vec, &guard_objp->orient.v.fvec, &r_vec);
9326 // half ships circle each way
9327 if (objval > 0.5f) {
9328 vm_vec_negate(&theta_vec);
9331 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9332 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9333 float max_guard_dist = min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9336 float min_z, max_z, length;
9337 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9338 min_z = pm->mins.xyz.z;
9339 max_z = pm->maxs.xyz.z;
9340 length = max_z - min_z;
9343 // how often to choose new desired_z
9344 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9345 int time_choose = int(floor(log(length * 0.001) / log(2.0)));
9346 float desired_z = min_z + length * static_randf( (Pl_objp-Objects) ^ (Missiontime >> (22 + time_choose)) );
9348 // get r from guard_ship
9349 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9351 // is ship within extents of cylinder of ship it is guarding
9352 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9355 // maybe go into orbit mode
9356 if (cur_guard_rad < max_guard_dist) {
9357 if ( cur_guard_rad > min_guard_dist ) {
9360 vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9361 vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9363 // move to where I can orbit
9364 if (extended_z < min_z) {
9365 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9367 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9369 vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9370 vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9373 // too close for orbit mode
9375 // inside (fly straight out and return circle)
9376 vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9378 // outside (fly to edge and circle)
9379 if (extended_z < min_z) {
9380 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, min_z);
9382 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, max_z);
9384 vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9385 vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9389 if (Pl_objp->phys_info.fspeed > 0) {
9390 // modify goal_pt to take account moving guard objp
9391 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9392 float time = dist / Pl_objp->phys_info.fspeed;
9393 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9395 // now modify to move to desired z (at a max of 20 m/s)
9396 float delta_z = desired_z - extended_z;
9397 float v_z = delta_z * 0.2f;
9400 } else if (v_z > 20) {
9404 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.v.fvec, v_z*time);
9408 // cast vector to center of guard_ship adjusted by desired_z
9409 float delta_z = desired_z - extended_z;
9410 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.v.fvec, delta_z);
9413 // try not to bump into things along the way
9414 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9415 if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9419 if (avoid_player(Pl_objp, &goal_pt)) {
9423 if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9428 // got the point, now let's go there
9429 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);
9430 // aip->goal_point = goal_pt;
9431 accelerate_ship(aip, 1.0f);
9433 // Periodically, scan for a nearby ship to attack.
9434 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9435 ai_guard_find_nearby_object();
9440 case AIS_GUARD_ATTACK:
9441 // The guarded ship has been attacked. Do something useful!
9446 //Int3(); // Illegal submode for Guard mode.
9447 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9448 aip->submode = AIS_GUARD_PATROL;
9453 // Main handler for guard behavior.
9454 // When someone has attacked guarded ship, then attack that ship.
9455 // To attack another ship, switch out of guard mode into chase mode.
9458 ship *shipp = &Ships[Pl_objp->instance];
9459 ai_info *aip = &Ai_info[shipp->ai_index];
9462 float dist_to_guardobj, dot_to_guardobj;
9463 vector vec_to_guardobj;
9465 /* // Debug code, find an object to guard.
9466 int finding_guard_objnum = 0; // Debug code, to see if body of "if" below gets executed.
9467 if (aip->guard_objnum == -1) {
9468 finding_guard_objnum = 1;
9469 debug_find_guard_object();
9470 if (aip->guard_objnum == -1)
9474 if (aip->guard_objnum == -1) {
9475 aip->mode = AIM_NONE;
9479 SDL_assert(aip->guard_objnum != -1);
9481 guard_objp = &Objects[aip->guard_objnum];
9483 if (guard_objp == Pl_objp) {
9484 Int3(); // This seems illegal. Why is a ship guarding itself?
9485 aip->guard_objnum = -1;
9489 // check that I have someone to guard
9490 if (guard_objp->instance == -1) {
9494 // Not sure whether this should be impossible, or a reasonable cleanup condition.
9495 // For now (3/31/97), it's getting trapped by an SDL_assert, so clean it up.
9496 if (guard_objp->type != OBJ_SHIP) {
9497 aip->guard_objnum = -1;
9501 // handler for gurad object with BIG radius
9502 if (guard_objp->radius > BIG_GUARD_RADIUS) {
9507 gshipp = &Ships[guard_objp->instance];
9512 float dist_to_goal_point, dot_to_goal_point, accel_scale;
9515 // get random [0 to 1] based on OBJNUM
9516 objval = static_randf(Pl_objp-Objects);
9518 switch (aip->submode) {
9519 case AIS_GUARD_STATIC:
9520 case AIS_GUARD_PATROL:
9522 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9523 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.v.fvec, &vec_to_guardobj);
9525 rel_vec = aip->guard_vec;
9526 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9528 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9529 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9530 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.v.fvec);
9531 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9533 // If far away, get closer
9534 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9535 if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9539 if (avoid_player(Pl_objp, &goal_point)) {
9543 // quite far away, so try to go straight to
9544 compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9545 ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9547 accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9549 if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9553 // get max of guard_objp (1) normal speed (2) dock speed
9554 float speed = guard_objp->phys_info.speed;
9556 if (guard_objp->type == OBJ_SHIP) {
9557 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9559 if (guard_aip->dock_objnum != -1) {
9560 speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9564 // Deal with guarding a small object.
9565 // If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9566 if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9567 if (dist_to_guardobj < dist_to_goal_point) {
9568 ai_set_guard_vec(Pl_objp, guard_objp); // OK to return here.
9573 if (speed > 10.0f) {
9574 // If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9575 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9576 if (vm_vec_dot(&Pl_objp->orient.v.fvec, &v2g) < 0.0f) {
9577 // Just slow down, don't turn.
9578 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9580 // Goal point is in front.
9582 // If close to goal point, don't change direction, just change speed.
9583 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9584 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9587 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9590 if (dot_to_goal_point > 0.8f) {
9591 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9592 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9594 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9597 // consider guard object STILL
9598 } else if (guard_objp->radius < 50.0f) {
9599 if (dist_to_goal_point > 15.0f) {
9600 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9601 set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9602 } else if (Pl_objp->phys_info.speed < 1.0f) {
9603 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9606 } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9607 // Orbiting ship, too far away
9608 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9609 accelerate_ship(aip, (1.0f + dot)/2.0f);
9610 } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9611 // Orbiting ship, got too close
9612 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9613 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9614 change_acceleration(aip, 0.25f);
9616 accelerate_ship(aip, 0.5f + objval/4.0f);
9618 // Orbiting ship, about the right distance away.
9619 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9620 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9621 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));
9623 accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9627 // Periodically, scan for a nearby ship to attack.
9628 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9629 ai_guard_find_nearby_object();
9633 case AIS_GUARD_ATTACK:
9634 // The guarded ship has been attacked. Do something useful!
9639 //Int3(); // Illegal submode for Guard mode.
9640 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9641 aip->submode = AIS_GUARD_PATROL;
9647 // Return the object of the ship that the given object is docked
9648 // with. Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9649 // and his submode is AIS_DOCK_3. I suppose that this is likely to change though.
9650 // Also, the objnum that was is passed in may not be the object that actually
9651 // performed the docking maneuver. This code will account for that case.
9652 object *ai_find_docked_object( object *docker )
9656 // we are trying to find the dockee of docker. (Note that that these terms
9657 // are totally relative to what is passed in as a parameter.)
9659 // first thing to attempt is to check and see if this object is docked with something.
9660 SDL_assert( docker->type == OBJ_SHIP ); // this had probably better be a ship!!!
9661 aip = &Ai_info[Ships[docker->instance].ai_index];
9662 if ( !(aip->ai_flags & AIF_DOCKED) ) // flag not set if not docked with anything
9665 if ( aip->dock_objnum == -1 ) {
9666 Int3(); // mwa says this is wrong wrong wrong
9667 ai_do_objects_undocked_stuff( docker, NULL );
9671 return &Objects[aip->dock_objnum];
9676 // define for the points subtracted from score for a rearm started on a player.
9677 #define REPAIR_PENALTY 50
9680 // function to clean up ai flags, variables, and other interesting information
9681 // for a ship that was getting repaired. The how parameter is useful for multiplayer
9682 // only in that it tells us why the repaired ship is being cleaned up.
9683 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9685 ai_info *aip, *repair_aip;
9688 SDL_assert( repaired_objp->type == OBJ_SHIP);
9689 aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9694 if(Game_mode & GM_MULTIPLAYER){
9695 p_index = multi_find_player_by_object(repaired_objp);
9698 if(repaired_objp == Player_obj){
9699 p_index = Player_num;
9704 case REPAIR_INFO_BEGIN:
9705 aip->ai_flags |= AIF_BEING_REPAIRED;
9706 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9707 stamp = timestamp(-1);
9709 // if this is a player ship, then subtract the repair penalty from this player's score
9710 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9711 if ( !(Game_mode & GM_MULTIPLAYER) ) {
9712 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor()); // subtract the penalty
9717 // multiplayer game -- find the player, then subtract the score
9718 pnum = multi_find_player_by_object( repaired_objp );
9720 Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9723 multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9725 nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9732 case REPAIR_INFO_BROKEN:
9733 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9734 aip->ai_flags |= AIF_AWAITING_REPAIR;
9735 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9738 case REPAIR_INFO_END:
9739 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9740 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9741 aip->dock_objnum = -1;
9743 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9744 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9747 case REPAIR_INFO_QUEUE:
9748 aip->ai_flags |= AIF_AWAITING_REPAIR;
9749 if ( aip == Player_ai ){
9750 hud_support_view_start();
9752 stamp = timestamp(-1);
9755 case REPAIR_INFO_ABORT:
9756 case REPAIR_INFO_KILLED:
9757 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9758 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9759 aip->dock_objnum = -1;
9760 aip->ai_flags &= ~AIF_DOCKED;
9761 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9762 if (repair_objp != NULL) {
9763 repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9764 repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9767 if ( p_index >= 0 ) {
9768 hud_support_view_abort();
9770 // send appropriate message to player here
9771 if ( how == REPAIR_INFO_KILLED ){
9772 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9775 message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9780 // add log entry if this is a player
9781 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9782 mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9785 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9788 case REPAIR_INFO_COMPLETE:
9789 // clear the being repaired flag -- and
9790 if ( p_index >= 0 ) {
9791 SDL_assert( repair_objp );
9793 hud_support_view_stop();
9795 message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9797 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9800 case REPAIR_INFO_ONWAY:
9801 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9802 SDL_assert( repair_objp );
9803 aip->dock_signature = repair_objp->signature;
9804 aip->dock_objnum = OBJ_INDEX(repair_objp);
9805 stamp = timestamp(-1);
9809 Int3(); // bogus type of repair info
9813 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9816 // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9818 if ( repair_objp ) {
9819 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9821 case REPAIR_INFO_ONWAY:
9822 SDL_assert( repaired_objp != NULL );
9823 aip->goal_objnum = OBJ_INDEX(repaired_objp);
9824 aip->ai_flags |= AIF_REPAIRING;
9827 case REPAIR_INFO_BROKEN:
9830 case REPAIR_INFO_END:
9831 case REPAIR_INFO_ABORT:
9832 case REPAIR_INFO_KILLED:
9833 if ( how == REPAIR_INFO_ABORT )
9834 aip->goal_objnum = -1;
9836 aip->ai_flags &= ~AIF_REPAIRING;
9839 case REPAIR_INFO_QUEUE:
9840 ai_add_rearm_goal( repaired_objp, repair_objp );
9843 case REPAIR_INFO_BEGIN:
9844 case REPAIR_INFO_COMPLETE:
9848 Int3(); // bogus type of repair info
9852 multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9855 // Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9856 // it was supposed to dock with is no longer valid.
9857 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9861 objp = &Objects[shipp->objnum];
9862 aip->mode = AIM_NONE;
9864 if (aip->ai_flags & AIF_REPAIRING) {
9865 SDL_assert( aip->goal_objnum != -1 );
9866 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9867 } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9868 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9869 SDL_assert( aip->dock_objnum != -1 );
9870 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9871 } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9872 // need to find the support ship that has me as a goal_objnum
9873 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9874 // MWA -- 3/38/98 Check to see if this guy is queued for a support ship, or there is already
9875 // one in the mission
9876 if ( mission_is_repair_scheduled(objp) ) {
9877 mission_remove_scheduled_repair( objp ); // this function will notify multiplayer clients.
9879 if ( aip->dock_objnum != -1 )
9880 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9882 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9886 if ( aip->ai_flags & AIF_DOCKED ) {
9889 SDL_assert( aip->dock_objnum != -1 );
9891 // if docked, and the dock_objnum is not undocking, force them to near last stage
9892 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9893 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9894 other_aip->submode = AIS_UNDOCK_3;
9895 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9900 // Make dockee_objp shake a bit due to docking.
9901 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9908 scale = 0.25f; // Compute this based on mass and speed at time of docking.
9910 vm_vec_rand_vec_quick(&tangles);
9911 vm_vec_scale(&tangles, scale);
9913 ap = (angles *) &tangles;
9915 vm_angles_2_matrix(&rotmat, ap);
9916 vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9917 dockee_objp->orient = tmp;
9919 vm_orthogonalize_matrix(&dockee_objp->orient);
9921 dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9926 // Make Pl_objp point at aip->goal_point.
9932 SDL_assert(Pl_objp->type == OBJ_SHIP);
9933 SDL_assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9935 shipp = &Ships[Pl_objp->instance];
9936 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9938 aip = &Ai_info[shipp->ai_index];
9940 turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9943 // Make *Pl_objp stay near another ship.
9949 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9951 goal_objnum = aip->goal_objnum;
9953 if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9954 aip->mode = AIM_NONE;
9956 float dist, max_dist, scale;
9957 vector rand_vec, goal_pos, vec_to_goal;
9960 goal_objp = &Objects[goal_objnum];
9962 // Make not all ships pursue same point.
9963 static_randvec(Pl_objp-Objects, &rand_vec);
9965 // Make sure point is in front hemisphere (relative to Pl_objp's position.
9966 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9967 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9968 vm_vec_negate(&rand_vec);
9971 // Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9972 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9973 max_dist = aip->stay_near_distance;
9974 scale = dist - max_dist/2;
9978 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9980 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9981 max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9983 if (dist > max_dist) {
9984 turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9985 accelerate_ship(aip, dist / max_dist - 0.8f);
9992 // Warn player if dock path is obstructed.
9993 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9995 vector *goalpos, *curpos;
10000 aip = &Ai_info[Ships[cur_objp->instance].ai_index];
10002 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
10004 if (goal_objp != Player_obj)
10007 curpos = &cur_objp->pos;
10008 radius = cur_objp->radius;
10009 goalpos = &Path_points[aip->path_cur].pos;
10010 collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
10012 if (collide_objnum != -1)
10013 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
10015 return collide_objnum;
10019 int Dock_path_warning_given = 0;
10021 // Docking behavior.
10022 // Approach a ship, follow path to docking platform, approach platform, after awhile,
10026 ship *shipp = &Ships[Pl_objp->instance];
10027 ai_info *aip = &Ai_info[shipp->ai_index];
10029 ship_info *sip = &Ship_info[shipp->ship_info_index];
10031 // Make sure object we're supposed to dock with still exists.
10032 if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
10033 ai_cleanup_dock_mode(aip, shipp);
10037 goal_objp = &Objects[aip->goal_objnum];
10039 // For docking submodes (ie, not undocking), follow path. Once at second last
10040 // point on path (point just before point on dock platform), orient into position.
10041 // For undocking, first mode pushes docked ship straight back from docking point
10042 // second mode turns ship and moves to point on docking radius
10043 switch (aip->submode) {
10045 // This mode means to find the path to the docking point.
10047 //aip->path_start = -1;
10048 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10050 if (!Dock_path_warning_given && (aip->path_length < 4)) {
10051 Warning( LOCATION, "Ship '%s' has only %i points on dock path. Docking will look strange. Contact Adam.", shipp->ship_name, aip->path_length );
10052 Dock_path_warning_given = 1; // This is on a mission-wide basis, but it's just a hack for now...
10055 aip->submode = AIS_DOCK_1;
10056 aip->path_start = -1;
10057 aip->submode_start_time = Missiontime;
10060 // This mode means to follow the path until just before the end.
10065 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10067 if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10068 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10071 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10072 accelerate_ship(aip, 0.0f);
10073 aip->submode = AIS_DOCK_0;
10078 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10079 //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10081 if (aip->path_cur-aip->path_start >= aip->path_length-1) { // If got this far, advance no matter what.
10082 aip->submode = AIS_DOCK_2;
10083 aip->submode_start_time = Missiontime;
10085 SDL_assert(aip->path_cur-aip->path_start >= 0);
10086 } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10087 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10088 set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10090 aip->submode = AIS_DOCK_2;
10091 aip->submode_start_time = Missiontime;
10097 // This mode means to drag oneself right to the second last point on the path.
10098 // Path code allows it to overshoot.
10103 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10104 nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10105 accelerate_ship(aip, 0.0f);
10106 aip->submode = AIS_DOCK_1;
10108 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10109 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10110 SDL_assert(dist != UNINITIALIZED_VALUE);
10112 if (dist == DOCK_BACKUP_RETURN_VAL) {
10114 aip->submode = AIS_DOCK_1;
10115 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10116 SDL_assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10117 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10121 //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10123 if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10124 tolerance = 6*flFrametime + 1.0f;
10126 tolerance = 4*flFrametime + 0.5f;
10128 if ( dist < tolerance) {
10129 aip->submode = AIS_DOCK_3;
10130 aip->submode_start_time = Missiontime;
10140 SDL_assert(aip->goal_objnum != -1);
10143 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10144 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10145 accelerate_ship(aip, 0.0f);
10146 aip->submode = AIS_DOCK_2;
10149 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10150 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10151 SDL_assert(dist != UNINITIALIZED_VALUE);
10153 if (dist == DOCK_BACKUP_RETURN_VAL) {
10154 aip->submode = AIS_DOCK_2;
10158 //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10160 if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10161 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10162 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10163 SDL_assert(dist != UNINITIALIZED_VALUE);
10165 physics_ship_init(Pl_objp);
10167 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10169 if (aip->submode == AIS_DOCK_3) {
10170 snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10171 hud_maybe_flash_docking_text(Pl_objp);
10172 // ai_dock_shake(Pl_objp, goal_objp);
10174 if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10175 joy_ff_docked(); // shake player's joystick a little
10178 // If this ship is repairing another ship...
10179 if (aip->ai_flags & AIF_REPAIRING) {
10180 aip->submode = AIS_DOCK_4; // Special rearming only dock mode.
10181 aip->submode_start_time = Missiontime;
10183 aip->submode = AIS_DOCK_4A;
10184 aip->submode_start_time = Missiontime;
10191 // Yes, we just sit here. We wait for further orders. No, it's not a bug.
10193 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10194 //nprintf(("AI", "."));
10195 if (aip->active_goal >= 0) {
10196 mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10198 if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10199 ai_mission_goal_complete( aip ); // Note, this calls ai_set_default_behavior().
10201 } else { // Can happen for initially docked ships.
10202 ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] ); // do the default behavior
10208 // This mode is only for rearming/repairing.
10209 // The ship that is performing the rearm enters this mode after it docks.
10210 SDL_assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10212 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10213 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10214 SDL_assert(dist != UNINITIALIZED_VALUE);
10216 object *goal_objp = &Objects[aip->goal_objnum];
10217 SDL_assert(goal_objp->type == OBJ_SHIP);
10218 ship *goal_shipp = &Ships[goal_objp->instance];
10219 ai_info *goal_aip = &Ai_info[goal_shipp->ai_index];
10221 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10223 // Make sure repair has not broken off.
10224 if (dist > 5.0f) { // Oops, too far away!
10225 if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10226 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10228 if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10229 // Got real far away from goal, so move back a couple modes and try again.
10230 aip->submode = AIS_DOCK_2;
10231 aip->submode_start_time = Missiontime;
10234 if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10235 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10241 case AIS_UNDOCK_0: {
10243 // First stage of undocking.
10245 //nprintf(("AI", "Undock 0:\n"));
10247 aip->submode = AIS_UNDOCK_1;
10248 aip->submode_start_time = Missiontime;
10249 if (aip->dock_objnum == -1) {
10250 aip->submode = AIS_UNDOCK_3;
10253 // set up the path points for the undocking procedure. dock_path_index member should
10254 // have gotten set in the docking code.
10255 SDL_assert( aip->dock_path_index != -1 );
10256 path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10257 ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10259 // Play a ship docking detach sound
10260 snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10264 case AIS_UNDOCK_1: {
10265 // Using thrusters, exit from dock station to nearest next dock path point.
10268 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10270 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10271 break; // Waiting for one second to elapse to let detach sound effect play out.
10273 else { // AL - added 05/16/97. Hack to play depart sound. Will probably take out.
10274 // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10275 if ( aip->submode_start_time != 0 )
10276 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10277 aip->submode_start_time = 0;
10280 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10281 SDL_assert(dist != UNINITIALIZED_VALUE);
10283 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10285 // Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10286 // This allows undock to complete if first ship flies away.
10287 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10288 aip->submode = AIS_UNDOCK_2;
10289 aip->submode_start_time = Missiontime;
10293 case AIS_UNDOCK_2: {
10295 ai_info *other_aip;
10297 // get pointer to docked object's aip to reset flags, etc
10298 SDL_assert( aip->dock_objnum != -1 );
10299 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10301 // Second stage of undocking.
10302 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10303 SDL_assert(dist != UNINITIALIZED_VALUE);
10306 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10308 // If at goal point, or quite far away from dock object
10309 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 > MAX_UNDOCK_ABORT_SPEED) ) {
10310 // reset the dock flags. If rearm/repair, reset rearm repair flags for those ships as well.
10311 if ( sip->flags & SIF_SUPPORT ) {
10312 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10315 // clear out flags for AIF_DOCKED for both objects.
10316 ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10317 physics_ship_init(Pl_objp);
10318 aip->submode = AIS_UNDOCK_3; // The do-nothing mode, until another order is issued
10320 //aip->ai_flags &= ~AIF_DOCKED; // @MK, 9/18/97
10321 //other_aip->ai_flags &= ~AIF_DOCKED;
10322 //aip->dock_objnum = -1; // invalidate who obj is docked with
10323 //other_aip->dock_objnum = -1; // MWA 10/07/97 invalide docked objects dock_objnum value as well
10325 // don't add undock log entries for support ships.
10326 if ( !(sip->flags & SIF_SUPPORT) )
10327 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10332 case AIS_UNDOCK_3: {
10333 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10334 SDL_assert(dist != UNINITIALIZED_VALUE);
10336 if (dist < Pl_objp->radius/2 + 5.0f) {
10337 aip->submode = AIS_UNDOCK_4;
10340 // possible that this flag hasn't been cleared yet. When aborting a rearm, this submode might
10341 // be entered directly.
10342 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10343 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10348 case AIS_UNDOCK_4: {
10349 ai_info *other_aip;
10351 // MWA 10/07/97 I'm slightly confused by the dual use of goal_objnum and dock_objnum. Seems to me
10352 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10353 // I could be wrong. dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10354 // get other ships ai_info pointer
10355 SDL_assert( aip->goal_objnum != -1 );
10356 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10358 aip->mode = AIM_NONE;
10359 aip->dock_path_index = -1; // invalidate the docking path index
10361 // these flags should have been cleared long ago!
10362 // Get Allender if you hit one of these!!!!!
10363 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10364 // goal_objnum of this ship ending it's undocking mode.
10365 //SDL_assert( !(aip->ai_flags & AIF_DOCKED) );
10366 //SDL_assert( !(other_aip->ai_flags & AIF_DOCKED) );
10367 //SDL_assert( !(aip->ai_flags & AIF_REPAIRING) );
10368 //SDL_assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10369 //SDL_assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10371 // only call mission goal complete if this was indeed an undock goal
10372 if ( aip->active_goal > -1 ) {
10373 if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10374 ai_mission_goal_complete( aip ); // this call should reset the AI mode
10376 // aip->active_goal = -1; // this ensures that this ship might get new goal
10382 Int3(); // Error, bogus submode
10389 // Given an object and a turret on that object, return the global position and forward vector
10390 // of the turret. The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10391 // the actual gun normal given using the current turret heading. But it _is_ rotated into the model's orientation
10392 // in global space.
10393 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10396 vm_copy_transpose_matrix(&m, &objp->orient);
10397 // vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10398 vm_vec_rotate(gpos, &tp->pnt, &m);
10399 vm_vec_add2(gpos, &objp->pos);
10400 vm_vec_rotate(gvec, &tp->turret_norm, &m);
10403 // Given an object and a turret on that object, return the actual firing point of the gun
10404 // and its normal. This uses the current turret angles. We are keeping track of which
10405 // gun to fire next in the ship specific info for this turret subobject. Use this info
10406 // to determine which position to fire from next.
10408 // *gpos: absolute position of gun firing point
10409 // *gvec: vector fro *gpos to *targetp
10410 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10413 model_subsystem *tp = ssp->system_info;
10415 ship_model_start(objp);
10417 gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10419 model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10422 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10425 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10426 vm_vec_normalized_dir(gvec, targetp, gpos);
10429 ship_model_stop(objp);
10432 // Rotate a turret towards an enemy.
10433 // Return TRUE if caller should use angles in subsequent rotations.
10434 // Some obscure model thing only John Slagel knows about.
10435 // Sets predicted enemy position.
10436 // If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10437 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10439 if (ss->turret_enemy_objnum != -1) {
10440 model_subsystem *tp = ss->system_info;
10441 vector gun_pos, gun_vec;
10442 float weapon_speed;
10443 float weapon_system_strength;
10445 // weapon_system_strength scales time enemy in range in 0..1. So, the lower this is, the worse the aiming will be.
10446 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10448 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10450 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10451 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10453 vector enemy_point;
10454 if (ss->targeted_subsys != NULL) {
10455 if (ss->turret_enemy_objnum != -1) {
10456 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10457 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10460 if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10461 ai_big_pick_attack_point_turret(lep, ss, &gun_pos, &gun_vec, &enemy_point, tp->turret_fov, min(weapon_travel_dist, Weapon_info[tp->turret_weapon_type].weapon_range));
10463 enemy_point = lep->pos;
10467 set_predicted_enemy_pos_turret(predicted_enemy_pos, &gun_pos, objp, &enemy_point, &lep->phys_info.vel, weapon_speed, ss->turret_time_enemy_in_range * (weapon_system_strength + 1.0f)/2.0f);
10469 if (weapon_system_strength < 0.7f) {
10472 static_randvec(Missiontime >> 18, &rand_vec); // Return same random number for two seconds.
10473 // Add to predicted_enemy_pos value in .45 to 1.5x radius of enemy ship, so will often miss, but not by a huge amount.
10474 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10478 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10479 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10482 rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient,
10483 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10484 &Objects[parent_objnum].pos, predicted_enemy_pos);
10491 // Determine if subsystem *enemy_subsysp is hittable from objp.
10492 // If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10493 float aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10496 vector subobj_pos, vector_out;
10498 vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10499 vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10501 if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10502 vector turret_norm;
10504 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10505 return vm_vec_dot(&turret_norm, &vector_out);
10511 #define MAX_AIFFT_TURRETS 60
10512 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10513 float aifft_rank[MAX_AIFFT_TURRETS];
10514 int aifft_list_size = 0;
10515 int aifft_max_checks = 5;
10518 dc_get_arg(ARG_INT);
10519 aifft_max_checks = Dc_arg_int;
10523 // Pick a subsystem to attack on enemy_objp.
10524 // Only pick one if enemy_objp is a big ship or a capital ship.
10525 // Returns dot product from turret to subsystem in *dot_out
10526 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10528 ship *eshipp, *shipp;
10530 ship_subsys *best_subsysp = NULL;
10533 SDL_assert(enemy_objp->type == OBJ_SHIP);
10535 eshipp = &Ships[enemy_objp->instance];
10536 esip = &Ship_info[eshipp->ship_info_index];
10538 shipp = &Ships[objp->instance];
10540 float best_dot = 0.0f;
10541 *dot_out = best_dot;
10543 // Compute absolute gun position.
10544 vector abs_gun_pos;
10545 vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10546 vm_vec_add2(&abs_gun_pos, &objp->pos);
10548 // Only pick a turret to attack on large ships.
10549 if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10550 return best_subsysp;
10552 // Make sure big or huge ship *actually* has subsystems (ie, knossos)
10553 if (esip->n_subsystems == 0) {
10554 return best_subsysp;
10557 // first build up a list subsystems to traverse
10559 aifft_list_size = 0;
10560 for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10561 model_subsystem *psub = pss->system_info;
10563 // if we've reached max turrets bail
10564 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10568 // Don't process destroyed objects
10569 if ( pss->current_hits <= 0.0f ){
10573 switch (psub->type) {
10574 case SUBSYSTEM_WEAPONS:
10575 aifft_list[aifft_list_size] = pss;
10576 aifft_rank[aifft_list_size++] = 1.4f;
10579 case SUBSYSTEM_TURRET:
10580 aifft_list[aifft_list_size] = pss;
10581 aifft_rank[aifft_list_size++] = 1.2f;
10584 case SUBSYSTEM_SENSORS:
10585 case SUBSYSTEM_ENGINE:
10586 aifft_list[aifft_list_size] = pss;
10587 aifft_rank[aifft_list_size++] = 1.0f;
10592 // DKA: 6/28/99 all subsystems can be destroyed.
10593 //SDL_assert(aifft_list_size > 0);
10594 if (aifft_list_size == 0) {
10595 return best_subsysp;
10598 // determine a stride value so we're not checking too many turrets
10599 int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10603 int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10605 for(idx=offset; idx<aifft_list_size; idx+=stride){
10606 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);
10608 if (dot* aifft_rank[idx] > best_dot) {
10609 best_dot = dot*aifft_rank[idx];
10610 best_subsysp = aifft_list[idx];
10614 SDL_assert(best_subsysp != &eshipp->subsys_list);
10616 *dot_out = best_dot;
10617 return best_subsysp;
10620 // Set active weapon for turret
10621 void ai_turret_select_default_weapon(ship_subsys *turret)
10625 twp = &turret->weapons;
10627 // If a primary weapon is available, select it
10628 if ( twp->num_primary_banks > 0 ) {
10629 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10630 } else if ( twp->num_secondary_banks > 0 ) {
10631 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10635 // return !0 if the specified target should scan for a new target, otherwise return 0
10636 int turret_should_pick_new_target(ship_subsys *turret)
10638 // int target_type;
10640 if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10647 if ( turret->turret_enemy_objnum == -1 ) {
10651 target_type = Objects[turret->turret_enemy_objnum].type;
10652 if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10660 // Set the next fire timestamp for a turret, based on weapon type and ai class
10661 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10666 weapon_id = turret->system_info->turret_weapon_type;
10668 wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10670 // make side even for team vs. team
10671 if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10672 // flak guns need to fire more rapidly
10673 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10674 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10675 wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10677 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10678 wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10681 // flak guns need to fire more rapidly
10682 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10683 if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10684 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10686 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10688 wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10690 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10691 // make huge weapons fire independently of team
10692 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10693 wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10695 // give team friendly an advantage
10696 if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10697 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10699 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10701 wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10705 // vary wait time +/- 10%
10706 wait *= frand_range(0.9f, 1.1f);
10707 turret->turret_next_fire_stamp = timestamp((int) wait);
10710 // Decide if a turret should launch an aspect seeking missile
10711 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10715 wip = &Weapon_info[weapon_class];
10717 if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10724 // Update how long current target has been in this turrets range
10725 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10727 turret->turret_time_enemy_in_range += seconds;
10729 if ( turret->turret_time_enemy_in_range < 0.0f ) {
10730 turret->turret_time_enemy_in_range = 0.0f;
10733 if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10734 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10740 // Fire a weapon from a turret
10741 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10743 matrix turret_orient;
10744 int turret_weapon_class, weapon_objnum;
10745 ai_info *parent_aip;
10747 beam_fire_info fire_info;
10748 float flak_range = 0.0f;
10750 parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10751 parent_ship = &Ships[Objects[parent_objnum].instance];
10752 turret_weapon_class = turret->system_info->turret_weapon_type;
10754 if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10755 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10756 turret->turret_last_fire_direction = *turret_fvec;
10758 // set next fire timestamp for the turret
10759 turret_set_next_fire_timestamp(turret, parent_aip);
10761 // if this weapon is a beam weapon, handle it specially
10762 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10763 // if this beam isn't free to fire
10764 if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10765 Int3(); // should never get this far
10769 // stuff beam firing info
10770 memset(&fire_info, 0, sizeof(beam_fire_info));
10771 fire_info.accuracy = 1.0f;
10772 fire_info.beam_info_index = turret_weapon_class;
10773 fire_info.beam_info_override = NULL;
10774 fire_info.shooter = &Objects[parent_objnum];
10775 fire_info.target = &Objects[turret->turret_enemy_objnum];
10776 fire_info.target_subsys = NULL;
10777 fire_info.turret = turret;
10779 // fire a beam weapon
10780 beam_fire(&fire_info);
10783 // don't fire swarm, but set up swarm info
10784 if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10785 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10788 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10789 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);
10792 //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));
10793 if (weapon_objnum != -1) {
10794 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10795 // AL 1-6-97: Store pointer to turret subsystem
10796 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10798 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10799 // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10800 if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10801 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );
10805 // if the gun is a flak gun
10806 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
10807 // show a muzzle flash
10808 flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10810 // pick a firing range so that it detonates properly
10811 flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10813 // determine what that range was
10814 flak_range = flak_get_range(&Objects[weapon_objnum]);
10817 // in multiplayer (and the master), then send a turret fired packet.
10818 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10821 subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10822 SDL_assert( subsys_index != -1 );
10823 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
10824 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10826 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10832 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10833 turret->turret_next_fire_stamp = timestamp((int) wait);
10837 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10839 int turret_weapon_class, weapon_objnum;
10840 matrix turret_orient;
10841 vector turret_pos, turret_fvec;
10843 // parent not alive, quick out.
10844 if (Objects[parent_objnum].type != OBJ_SHIP) {
10848 // change firing point
10849 ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10850 turret->turret_next_fire_pos++;
10852 // get class [index into Weapon_info array
10853 turret_weapon_class = turret->system_info->turret_weapon_type;
10854 SDL_assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10856 // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10857 vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10859 // create weapon and homing info
10860 weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10861 weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10863 // do other cool stuff if weapon is created.
10864 if (weapon_objnum > -1) {
10865 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10866 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10869 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10870 // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10871 if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10872 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10876 // in multiplayer (and the master), then send a turret fired packet.
10877 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10880 subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10881 SDL_assert( subsys_index != -1 );
10882 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10887 int Num_ai_firing = 0;
10888 int Num_find_turret_enemy = 0;
10889 int Num_turrets_fired = 0;
10890 // Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10891 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10893 float weapon_firing_range;
10895 object *lep; // Last enemy pointer
10896 model_subsystem *tp = ss->system_info;
10897 int use_angles, turret_weapon_class;
10898 vector predicted_enemy_pos;
10902 if (!Ai_firing_enabled) {
10906 if (ss->current_hits < 0.0f) {
10910 if ( ship_subsys_disrupted(ss) ){ // AL 1/19/98: Make sure turret isn't suffering disruption effects
10914 // Check turret free
10915 if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10919 // If beam weapon, check beam free
10920 if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10924 SDL_assert( shipp->objnum == parent_objnum );
10926 if ( tp->turret_weapon_type < 0 ){
10930 // Monitor number of calls to ai_fire_from_turret
10933 turret_weapon_class = tp->turret_weapon_type;
10935 // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10936 if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10937 lep = &Objects[ss->turret_enemy_objnum];
10939 // MK -- here is where turret is targeting a bomb. I simply return for now. We should force
10940 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10943 // we only care about targets which are ships.
10944 //if ( lep->type != OBJ_SHIP )
10947 // If targeted a small ship and have a huge weapon, don't fire. But this shouldn't happen, as a small ship should not get selected.
10948 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10949 if ( lep->type != OBJ_SHIP ) {
10952 if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10957 // If targeting protected or beam protected ship, don't fire. Reset enemy objnum
10958 if (lep->type == OBJ_SHIP) {
10959 // Check if we're targeting a protected ship
10960 if (lep->flags & OF_PROTECTED) {
10961 ss->turret_enemy_objnum = -1;
10962 ss->turret_time_enemy_in_range = 0.0f;
10966 // Check if we're targeting a beam protected ship with a beam weapon
10967 if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10968 ss->turret_enemy_objnum = -1;
10969 ss->turret_time_enemy_in_range = 0.0f;
10974 ss->turret_enemy_objnum = -1;
10978 SDL_assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10979 objp = &Objects[parent_objnum];
10980 SDL_assert(objp->type == OBJ_SHIP);
10981 aip = &Ai_info[Ships[objp->instance].ai_index];
10983 // Use the turret info for all guns, not one gun in particular.
10985 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10987 // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10988 use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10990 if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10994 // Don't try to fire beyond weapon_limit_range
10995 weapon_firing_range = min(Weapon_info[tp->turret_weapon_type].lifetime * Weapon_info[tp->turret_weapon_type].max_speed, Weapon_info[tp->turret_weapon_type].weapon_range);
10997 // if beam weapon in nebula and target not tagged, decrase firing range
10998 extern int Nebula_sec_range;
10999 if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
11000 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
11001 if (Nebula_sec_range) {
11002 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
11007 if (ss->turret_enemy_objnum != -1) {
11008 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
11009 if (dist_to_enemy > weapon_firing_range) {
11010 ss->turret_enemy_objnum = -1; // Force picking of new enemy.
11014 // Turret spawn weapons are a special case. They fire if there are enough enemies in the
11015 // immediate area (not necessarily in the turret fov).
11016 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
11017 int num_ships_nearby;
11018 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
11019 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
11020 turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
11022 ss->turret_next_fire_stamp = timestamp(1000); // Regardless of firing rate, don't check whether should fire for awhile.
11027 // Maybe pick a new enemy.
11028 if ( turret_should_pick_new_target(ss) ) {
11029 Num_find_turret_enemy++;
11030 int objnum = find_turret_enemy(ss, parent_objnum, &gpos, &gvec, ss->turret_enemy_objnum, tp->turret_fov, Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE);
11031 SDL_assert(objnum < 0 || is_target_beam_valid(ss, objnum));
11033 if (objnum != -1) {
11034 if (ss->turret_enemy_objnum == -1) {
11035 ss->turret_enemy_objnum = objnum;
11036 ss->turret_enemy_sig = Objects[objnum].signature;
11040 ss->turret_enemy_objnum = objnum;
11041 ss->turret_enemy_sig = Objects[objnum].signature;
11044 ss->turret_enemy_objnum = -1;
11047 if (ss->turret_enemy_objnum != -1) {
11049 lep = &Objects[ss->turret_enemy_objnum];
11050 if ( lep->type == OBJ_SHIP ) {
11051 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);
11053 ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
11055 ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f))); // Check every two seconds
11059 // If still don't have an enemy, return. Or, if enemy is protected, return.
11060 if (ss->turret_enemy_objnum != -1) {
11061 // Don't shoot at ship we're going to dock with.
11062 if (ss->turret_enemy_objnum == aip->dock_objnum) {
11063 ss->turret_enemy_objnum = -1;
11067 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11068 // This can happen if the enemy was selected before it became protected.
11069 ss->turret_enemy_objnum = -1;
11072 lep = &Objects[ss->turret_enemy_objnum];
11074 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11075 ss->turret_next_fire_stamp = timestamp(500);
11080 if ( lep == NULL ){
11084 SDL_assert(ss->turret_enemy_objnum != -1);
11086 float dot = vm_vec_dot(&v2e, &gvec);
11088 if (dot > tp->turret_fov ) {
11089 // Ok, the turret is lined up... now line up a particular gun.
11090 int ok_to_fire = 0;
11091 float dist_to_enemy;
11093 // We're ready to fire... now get down to specifics, like where is the
11094 // actual gun point and normal, not just the one for whole turret.
11095 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11096 ss->turret_next_fire_pos++;
11098 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11099 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11100 dist_to_enemy = vm_vec_normalize(&v2e);
11101 dot = vm_vec_dot(&v2e, &gvec);
11103 // if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone" to make a cool visual effect
11104 // and make them less lethal
11105 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11106 flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11110 // dumbfire and nearly pointing at target.
11111 // heat seeking and target in a fairly wide cone.
11112 // aspect seeking and target is locked.
11113 turret_weapon_class = tp->turret_weapon_type;
11115 // if dumbfire (lasers and non-homing missiles)
11116 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11117 if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11118 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11121 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) { // if heat seekers
11122 if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11123 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11126 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) { // if aspect seeker
11127 if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11128 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11130 if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11135 if ( ok_to_fire ) {
11136 Num_turrets_fired++;
11138 turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);
11140 turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11141 ss->turret_next_fire_stamp = timestamp(500);
11145 ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
11146 ss->turret_time_enemy_in_range = 0.0f;
11153 #define MAX_AI_DEBUG_RENDER_STUFF 100
11154 typedef struct ai_render_stuff {
11159 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11161 int Num_AI_debug_render_stuff = 0;
11163 void ai_debug_render_stuff()
11165 vertex vert1, vert2;
11169 for (i=0; i<Num_AI_debug_render_stuff; i++) {
11173 model_subsystem *tp;
11175 ss = AI_debug_render_stuff[i].ss;
11176 tp = ss->system_info;
11178 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11180 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11181 g3_rotate_vertex(&vert1, &gpos);
11182 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11183 g3_rotate_vertex(&vert2, &gpos2);
11184 gr_set_color(0, 0, 255);
11185 g3_draw_sphere(&vert1, 2.0f);
11186 gr_set_color(255, 0, 255);
11187 g3_draw_sphere(&vert2, 2.0f);
11188 g3_draw_line(&vert1, &vert2);
11191 // draw from beta to its goal point
11192 /* for (i=0; i<6; i++) {
11193 ai_info *aip = &Ai_info[i];
11194 gr_set_color(0, 0, 255);
11195 g3_rotate_vertex(&vert1, &Objects[i].pos);
11196 g3_rotate_vertex(&vert2, &aip->goal_point);
11197 g3_draw_line(&vert1, &vert2);
11201 Num_AI_debug_render_stuff = 0;
11207 int Msg_count_4996 = 0;
11210 // --------------------------------------------------------------------------
11211 // Process subobjects of object objnum.
11212 // Deal with engines disabled.
11213 void process_subobjects(int objnum)
11215 model_subsystem *psub;
11217 object *objp = &Objects[objnum];
11218 ship *shipp = &Ships[objp->instance];
11219 ai_info *aip = &Ai_info[shipp->ai_index];
11220 ship_info *sip = &Ship_info[shipp->ship_info_index];
11222 for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11223 psub = pss->system_info;
11225 // Don't process destroyed objects
11226 if ( pss->current_hits <= 0.0f )
11229 switch (psub->type) {
11230 case SUBSYSTEM_TURRET:
11231 if ( psub->turret_num_firing_points > 0 ) {
11232 ai_fire_from_turret(shipp, pss, objnum);
11235 if (!Msg_count_4996) {
11236 Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11243 case SUBSYSTEM_ENGINE:
11244 case SUBSYSTEM_NAVIGATION:
11245 case SUBSYSTEM_COMMUNICATION:
11246 case SUBSYSTEM_WEAPONS:
11247 case SUBSYSTEM_SENSORS:
11248 case SUBSYSTEM_UNKNOWN:
11251 // next set of subsystems may rotation
11252 case SUBSYSTEM_RADAR:
11253 case SUBSYSTEM_SOLAR:
11254 case SUBSYSTEM_GAS_COLLECT:
11255 case SUBSYSTEM_ACTIVATION:
11258 Error(LOCATION, "Illegal subsystem type.\n");
11261 // do solar/radar/gas/activator rotation here
11262 if ( psub->flags & MSS_FLAG_ROTATES ) {
11263 if (psub->flags & MSS_FLAG_STEPPED_ROTATE ) {
11264 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11266 submodel_rotate(psub, &pss->submodel_info_1 );
11272 // Deal with a ship with blown out engines.
11273 if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11274 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11275 // AL: Only attack forever if not trying to depart to a docking bay. Need to have this in, since
11276 // a ship may get repaired... and it should still try to depart. Since docking bay departures
11277 // are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11278 if ( aip->mode != AIM_BAY_DEPART ) {
11279 ai_attack_object(objp, NULL, 99, NULL); // Regardless of current mode, enter attack mode.
11280 aip->submode = SM_ATTACK_FOREVER; // Never leave attack submode, don't avoid, evade, etc.
11288 // Given an object and the wing it's in, return its index in the wing list.
11289 // This defines its location in the wing formation.
11290 // If the object can't be found in the wing, return -1.
11291 // *objp object of interest
11292 // wingnum the wing *objp is in
11293 int get_wing_index(object *objp, int wingnum)
11298 SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11300 wingp = &Wings[wingnum];
11302 for (i=wingp->current_count-1; i>=0; i--)
11303 if ( objp->instance == wingp->ship_index[i] )
11306 return i; // Note, returns -1 if string not found.
11309 // Given a wing, return a pointer to the object of its leader.
11310 // Asserts if object not found.
11311 // Currently, the wing leader is defined as the first object in the wing.
11312 // wingnum Wing number in Wings array.
11313 // If wing leader is disabled, swap it with another ship.
11314 object * get_wing_leader(int wingnum)
11319 SDL_assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11321 wingp = &Wings[wingnum];
11323 SDL_assert(wingp->current_count != 0); // Make sure there is a leader
11325 ship_num = wingp->ship_index[0];
11327 // If this ship is disabled, try another ship in the wing.
11329 while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11331 if (n >= wingp->current_count)
11333 ship_num = wingp->ship_index[n];
11336 if (( n != 0) && (n != wingp->current_count)) {
11337 int t = wingp->ship_index[0];
11338 wingp->ship_index[0] = wingp->ship_index[n];
11339 wingp->ship_index[n] = t;
11342 return &Objects[Ships[ship_num].objnum];
11345 #define DEFAULT_WING_X_DELTA 1.0f
11346 #define DEFAULT_WING_Y_DELTA 0.25f
11347 #define DEFAULT_WING_Z_DELTA 0.75f
11348 #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))
11349 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11350 #define MAX_FORMATION_ROWS 4
11352 // Given a position in a wing, return the desired location of the ship relative to the leader
11353 // *_delta_vec OUTPUT. delta vector based on wing_index
11354 // wing_index position in wing.
11355 void get_wing_delta(vector *_delta_vec, int wing_index)
11359 SDL_assert(wing_index >= 0);
11361 int k, row, column;
11363 int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11364 wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11367 for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11374 column = wi0 - k + row + 1;
11376 _delta_vec->xyz.x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11377 _delta_vec->xyz.y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11378 _delta_vec->xyz.z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11381 // Compute the largest radius of a ship in a *objp's wing.
11382 float gwlr_1(object *objp, ai_info *aip)
11384 int wingnum = aip->wing;
11389 SDL_assert(wingnum >= 0);
11391 max_radius = objp->radius;
11393 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11394 o = &Objects[so->objnum];
11395 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11396 if (o->radius > max_radius)
11397 max_radius = o->radius;
11403 // Compute the largest radius of a ship forming on *objp's wing.
11404 float gwlr_object_1(object *objp, ai_info *aip)
11410 max_radius = objp->radius;
11412 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11413 o = &Objects[so->objnum];
11414 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11415 if (o->radius > max_radius)
11416 max_radius = o->radius;
11422 // For the wing that *objp is part of, return the largest ship radius in that wing.
11423 float get_wing_largest_radius(object *objp, int formation_object_flag)
11428 SDL_assert(objp->type == OBJ_SHIP);
11429 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11430 shipp = &Ships[objp->instance];
11431 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11432 aip = &Ai_info[shipp->ai_index];
11434 if (formation_object_flag) {
11435 return gwlr_object_1(objp, aip);
11437 return gwlr_1(objp, aip);
11442 float Wing_y_scale = 2.0f;
11443 float Wing_scale = 1.0f;
11444 DCF(wing_y_scale, "")
11446 dc_get_arg(ARG_FLOAT);
11447 Wing_y_scale = Dc_arg_float;
11450 DCF(wing_scale, "")
11452 dc_get_arg(ARG_FLOAT);
11453 Wing_scale = Dc_arg_float;
11456 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11457 // Returns result in *result_pos.
11458 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11460 vector wing_delta, rotated_wing_delta;
11461 float wing_spread_size;
11463 get_wing_delta(&wing_delta, wing_index); // Desired location in leader's reference frame
11465 wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11467 // for player obj (1) move ships up 20% (2) scale formation up 20%
11468 if (leader_objp->flags & OF_PLAYER_SHIP) {
11469 wing_delta.xyz.y *= Wing_y_scale;
11470 wing_spread_size *= Wing_scale;
11473 vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11475 vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient); // Rotate into leader's reference.
11477 vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); // goal_point is absolute 3-space point.
11481 int Debug_render_wing_phantoms;
11483 void render_wing_phantoms(object *objp)
11489 int wing_index; // Index in wing struct, defines 3-space location in wing.
11492 SDL_assert(objp->type == OBJ_SHIP);
11493 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11495 shipp = &Ships[objp->instance];
11496 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11498 aip = &Ai_info[shipp->ai_index];
11500 wingnum = aip->wing;
11505 wing_index = get_wing_index(objp, wingnum);
11507 // If this ship is NOT the leader, abort.
11508 if (wing_index != 0)
11511 for (i=0; i<32; i++)
11512 if (Debug_render_wing_phantoms & (1 << i)) {
11513 get_absolute_wing_pos(&goal_point, objp, i, 0);
11516 gr_set_color(255, 0, 128);
11517 g3_rotate_vertex(&vert, &goal_point);
11518 g3_draw_sphere(&vert, 2.0f);
11521 Debug_render_wing_phantoms = 0;
11525 void render_wing_phantoms_all()
11530 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11534 int wing_index; // Index in wing struct, defines 3-space location in wing.
11536 objp = &Objects[so->objnum];
11538 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11539 shipp = &Ships[objp->instance];
11540 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11542 aip = &Ai_info[shipp->ai_index];
11544 wingnum = aip->wing;
11549 wing_index = get_wing_index(objp, wingnum);
11551 // If this ship is NOT the leader, abort.
11552 if (wing_index != 0)
11555 render_wing_phantoms(objp);
11563 // Hook from goals code to AI.
11564 // Force a wing to fly in formation.
11565 // Sets AIF_FORMATION bit in ai_flags.
11566 // wingnum Wing to force to fly in formation
11567 void ai_fly_in_formation(int wingnum)
11573 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11574 objp = &Objects[so->objnum];
11575 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11577 shipp = &Ships[objp->instance];
11578 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11580 if (Ai_info[shipp->ai_index].wing == wingnum) {
11581 Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11582 Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11587 // Hook from goals code to AI.
11588 // Force a wing to abandon formation flying.
11589 // Clears AIF_FORMATION bit in ai_flags.
11590 // wingnum Wing to force to fly in formation
11591 void ai_disband_formation(int wingnum)
11597 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11598 objp = &Objects[so->objnum];
11599 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11601 shipp = &Ships[objp->instance];
11602 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11604 if (Ai_info[shipp->ai_index].wing == wingnum) {
11605 Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11610 float Leader_chaos = 0.0f;
11611 int Chaos_frame = -1;
11613 // Return true if objp is flying in an erratic manner
11614 // Only true if objp is a player
11615 int formation_is_leader_chaotic(object *objp)
11617 if (Game_mode & GM_MULTIPLAYER)
11620 if (objp != Player_obj)
11623 if (Framecount != Chaos_frame) {
11627 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11629 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.v.fvec, &objp->last_orient.v.fvec)) * flFrametime;
11630 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.v.uvec, &objp->last_orient.v.uvec)) * flFrametime;
11632 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11634 Leader_chaos *= (1.0f - flFrametime*0.2f);
11636 if (Leader_chaos < 0.0f)
11637 Leader_chaos = 0.0f;
11638 else if (Leader_chaos > 1.7f)
11639 Leader_chaos = 1.7f;
11641 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11643 Chaos_frame = Framecount;
11646 return (Leader_chaos > 1.0f);
11649 // Fly in formation.
11650 // Make Pl_objp assume its proper place in formation.
11651 // If the leader of the wing is doing something stupid, like fighting a battle,
11652 // then the poor sap wingmates will be in for a "world of hurt"
11653 // Return TRUE if we need to process this object's normal mode
11656 object *leader_objp;
11658 ai_info *aip, *laip;
11660 int wing_index; // Index in wing struct, defines 3-space location in wing.
11661 int player_wing; // index of the players wingnum
11662 vector goal_point, future_goal_point_5, future_goal_point_2, future_goal_point_x, future_goal_point_1000x, vec_to_goal, dir_to_goal;
11663 float dot_to_goal, dist_to_goal, leader_speed;
11665 SDL_assert(Pl_objp->type == OBJ_SHIP);
11666 SDL_assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11668 shipp = &Ships[Pl_objp->instance];
11670 SDL_assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11672 aip = &Ai_info[shipp->ai_index];
11674 SDL_assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION); // Make sure not both types of formation flying in effect.
11676 // Determine which kind of formation flying.
11677 // If tracking an object, not in waypoint mode:
11678 if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11679 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11680 aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11684 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11685 leader_objp = &Objects[aip->goal_objnum];
11686 } else { // Formation flying in waypoint mode.
11687 SDL_assert(aip->ai_flags & AIF_FORMATION_WING);
11688 if (aip->mode != AIM_WAYPOINTS) {
11689 aip->ai_flags &= ~AIF_FORMATION_WING;
11693 wingnum = aip->wing;
11698 // disable formation flying for any ship in the players wing
11699 player_wing = Ships[Player_obj->instance].wingnum;
11700 if ( (player_wing != -1) && (wingnum == player_wing) )
11703 wing_index = get_wing_index(Pl_objp, wingnum);
11705 leader_objp = get_wing_leader(wingnum);
11709 // If docked with a ship in this wing, only the more massive one actually flies in formation.
11710 if (aip->dock_objnum != -1) {
11711 object *other_objp = &Objects[aip->dock_objnum];
11712 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11714 if (aip->wing == other_aip->wing) {
11715 if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11717 else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11718 if (Pl_objp->signature < other_objp->signature)
11724 SDL_assert(leader_objp != NULL);
11725 laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11727 // Make sure we're really in this wing.
11728 if (wing_index == -1)
11731 // If this ship is the leader, abort, as he doesn't have to follow anyone.
11732 if (wing_index == 0) {
11733 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11737 if (aip->mode == AIM_WAYPOINTS) {
11738 aip->wp_list = laip->wp_list;
11739 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11740 aip->wp_index = laip->wp_index;
11742 aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11743 aip->wp_flags = laip->wp_flags;
11744 aip->wp_dir = laip->wp_dir;
11748 Debug_render_wing_phantoms |= (1 << wing_index);
11751 leader_speed = leader_objp->phys_info.speed;
11752 vector leader_vec = leader_objp->phys_info.vel;
11754 get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11755 vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11756 vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11757 vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.v.fvec, 10.0f); // used when very close to destination
11758 vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.v.fvec, 1000.0f); // used when very close to destination
11760 // Now, get information telling this object how to turn and accelerate to get to its
11761 // desired location.
11762 vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11763 if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11764 vec_to_goal.xyz.x += 0.1f;
11766 vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11767 //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.v.fvec);
11768 dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.v.fvec);
11769 dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11770 float dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11772 // nprintf(("AI", "dot = %7.3f, dist = %8.3f, speed = %7.3f, leader speed = %7.3f\n", dot_to_goal, dist_to_goal, Pl_objp->phys_info.speed, leader_objp->phys_info.speed));
11774 int chaotic_leader = 0;
11776 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.
11778 if (dist_to_goal > 500.0f) {
11779 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11780 accelerate_ship(aip, 1.0f);
11781 } else if (dist_to_goal > 200.0f) {
11782 if (dot_to_goal > -0.5f) {
11783 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11784 float range_speed = shipp->current_max_speed - leader_speed;
11785 if (range_speed > 0.0f)
11786 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11788 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11790 turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11791 if (leader_speed > 10.0f)
11792 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11794 set_accel_for_target_speed(Pl_objp, 10.0f);
11801 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11802 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.v.fvec);
11804 // Leader flying like a maniac. Don't try hard to form on wing.
11805 if (chaotic_leader) {
11806 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11807 set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11808 } else if (dist_to_goal > 75.0f) {
11809 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11811 float range_speed = shipp->current_max_speed - leader_speed;
11812 if (range_speed > 0.0f)
11813 delta_speed = dist_to_goal_2/500.0f * range_speed;
11815 delta_speed = shipp->current_max_speed - leader_speed;
11816 if (dot_to_goal < 0.0f) {
11817 delta_speed = -delta_speed;
11818 if (-delta_speed > leader_speed/2)
11819 delta_speed = -leader_speed/2;
11822 if (leader_speed < 5.0f)
11823 if (delta_speed < 5.0f)
11824 delta_speed = 5.0f;
11826 float scale = dot_to_f2;
11832 set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11834 //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11836 if (leader_speed < 5.0f) {
11837 // Leader very slow. If not close to goal point, get very close. Note, keep trying to get close unless
11838 // moving very slowly, else momentum can carry far away from goal.
11840 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11841 //nprintf(("MK", "(1) "));
11842 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11843 set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11845 if (Pl_objp->phys_info.speed < 0.5f) {
11846 //nprintf(("MK", "(2) "));
11847 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11849 //nprintf(("MK", "(3) "));
11851 set_accel_for_target_speed(Pl_objp, leader_speed);
11853 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11854 } else if (dist_to_goal > 10.0f) {
11857 //future_goal_point_2;
11859 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11861 if (dist_to_goal > 25.0f) {
11862 if (dot_to_goal < 0.3f)
11865 dv = dot_to_goal - 0.2f;
11867 set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11869 set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11872 if (Pl_objp->phys_info.speed < 0.1f)
11873 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11875 turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11876 set_accel_for_target_speed(Pl_objp, 0.0f);
11882 // See how different this ship's bank is relative to wing leader
11883 float up_dot = vm_vec_dot(&leader_objp->orient.v.uvec, &Pl_objp->orient.v.uvec);
11884 if (up_dot < 0.996f) {
11887 vector angular_accel;
11889 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11890 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);
11892 // nprintf(("AI", "Frame %d Bashing formation orient. Dot was %6.3f, becomes %6.3f\n", Framecount, up_dot, vm_vec_dot(&leader_objp->orient.v.uvec, &new_orient.v.uvec)));
11893 Pl_objp->orient = new_orient;
11894 Pl_objp->phys_info.rotvel = w_out;
11895 // Pl_objp->phys_info.desired_rotvel = w_out;
11897 Pl_objp->phys_info.rotvel.xyz.z = 0.0f;
11903 // Return index of object repairing object objnum.
11904 int find_repairing_objnum(int objnum)
11911 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11912 objp = &Objects[so->objnum];
11914 SDL_assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11916 shipp = &Ships[objp->instance];
11917 sip = &Ship_info[shipp->ship_info_index];
11919 if (sip->flags & SIF_SUPPORT) {
11922 aip = &Ai_info[shipp->ai_index];
11924 if (aip->goal_objnum == objnum) {
11925 return objp-Objects;
11933 // If object *objp is being repaired, deal with it!
11934 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11936 if (Ships[objp->instance].team == TEAM_TRAITOR) {
11937 ai_abort_rearm_request(objp);
11941 if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11943 ai_info *repair_aip;
11945 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11946 //SDL_assert(dock_objnum != -1);
11947 if (dock_objnum == -1)
11949 if (Objects[dock_objnum].signature != aip->dock_signature) {
11950 Int3(); // Curious -- object numbers match, but signatures do not.
11951 // Must mean original repair ship died and was replaced by current ship.
11955 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11956 //SDL_assert(repair_aip->mode == AIM_DOCK);
11958 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11959 // SDL_assert(repair_aip->submode == AIS_DOCK_4);
11961 // Wait awhile into the mode to synchronize with sound effect.
11962 if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11965 repaired = ship_do_rearm_frame( objp, frametime ); // hook to do missile rearming
11967 // See if fully repaired. If so, cause process to stop.
11968 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11970 repair_aip->submode = AIS_UNDOCK_0;
11971 repair_aip->submode_start_time = Missiontime;
11973 // if repairing player object -- tell him done with repair
11974 if ( !MULTIPLAYER_CLIENT ){
11975 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11979 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11980 // If this ship has been awaiting repair for 90+ seconds, abort.
11981 if ( !MULTIPLAYER_CLIENT ) {
11982 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11983 if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11984 ai_abort_rearm_request(objp);
11985 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11991 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing. We need to
11992 // do this check, since this is a looping sound, and may continue on if rearm/repair
11993 // finishes abnormally once sound begins looping.
11994 if ( objp == Player_obj ) {
11995 player_stop_repair_sound();
12000 // Shell around dock_orient_and_approach to detect whether dock process should be aborted.
12001 // obj1 is the ship performing the repair.
12002 // obj2 is the ship being repaired.
12003 void call_doa(object *obj1, object *obj2, ship_info *sip1)
12005 if (sip1->flags & SIF_SUPPORT) {
12006 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
12008 // call the ai_abort rearm request code
12009 ai_abort_rearm_request( obj2 );
12011 dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12013 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
12014 dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12015 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
12016 dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
12018 //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
12019 // Ships[obj1->instance].ship_name, Ship_info[Ships[obj1->instance].ship_info_index].name, Ships[obj2->instance].ship_name, Ship_info[Ships[obj2->instance].ship_info_index].name));
12020 dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
12027 // Maybe launch a countermeasure.
12028 // Also, detect a supposed homing missile that no longer exists.
12029 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
12035 shipp = &Ships[objp->instance];
12036 sip = &Ship_info[shipp->ship_info_index];
12038 if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
12041 if (!shipp->cmeasure_count)
12044 if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
12047 // If not on player's team and Skill_level + ai_class is low, never fire a countermeasure. The ship is too dumb.
12048 if (shipp->team != Player_ship->team) {
12049 if (Game_skill_level + aip->ai_class < 4){
12054 if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
12055 object *weapon_objp;
12059 weapon_objp = &Objects[aip->nearest_locked_object];
12060 weaponp = &Weapons[weapon_objp->instance];
12061 wip = &Weapon_info[weaponp->weapon_info_index];
12063 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
12065 aip->nearest_locked_distance = dist;
12066 // Verify that this object is really homing on us.
12067 object *weapon_objp;
12069 weapon_objp = &Objects[aip->nearest_locked_object];
12073 // For ships on player's team, have constant, average chance to fire.
12074 // For enemies, increasing chance with higher skill level.
12075 if (shipp->team == Player_ship->team)
12076 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12078 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12080 // Decrease chance to fire at lower ai class.
12081 fire_chance *= (float) aip->ai_class/Num_ai_classes;
12084 if (fire_chance < r) {
12085 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12086 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.
12090 if (weapon_objp->type == OBJ_WEAPON) {
12091 if (weapon_objp->instance >= 0) {
12092 //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12093 ship_launch_countermeasure(objp);
12094 shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12105 // --------------------------------------------------------------------------
12106 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12108 // if (aip->ignore_objnum == UNUSED_OBJNUM)
12111 if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12112 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12113 aip->ignore_objnum = UNUSED_OBJNUM;
12117 if (is_ignore_object(aip, aip->goal_objnum)) {
12118 aip->goal_objnum = -1;
12119 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12121 if ( aip->mode == AIM_STRAFE ) {
12122 aip->target_objnum = -1;
12126 if (is_ignore_object(aip, aip->target_objnum))
12127 aip->target_objnum = -1;
12131 void ai_safety_circle_spot()
12136 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12138 goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12139 turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12141 set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12143 // float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12144 // nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f. Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.xyz.x, goal_point.xyz.y, goal_point.xyz.z, dist));
12149 #define CHASE_CIRCLE_DIST 100.0f
12151 void ai_chase_circle(object *objp)
12153 float dist_to_goal;
12154 float target_speed;
12159 sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12161 target_speed = sip->max_speed/4.0f;
12162 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12164 SDL_assert(vm_vec_mag(&aip->goal_point) >= 0.0f); // Supposedly detects bogus vector
12166 goal_point = aip->goal_point;
12168 if (aip->ignore_objnum == UNUSED_OBJNUM) {
12169 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12171 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12172 vector vec_to_goal;
12173 // Too far from circle goal, create a new goal point.
12174 vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12175 vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12178 goal_point = aip->goal_point;
12179 } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12180 object *ignore_objp = &Objects[aip->ignore_objnum];
12185 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12187 if (dist < ignore_objp->radius*2 + 1500.0f) {
12188 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12189 if (dist < ignore_objp->radius*2 + 1300.0f)
12190 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12194 SDL_assert(vm_vec_mag(&aip->goal_point) >= 0.0f); // Supposedly detects bogus vector
12196 turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12198 set_accel_for_target_speed(Pl_objp, target_speed);
12202 #define SHIELD_BALANCE_RATE 0.2f // 0.1f -> takes 10 seconds to equalize shield.
12204 // Transfer shield energy to most recently hit section from others.
12205 void ai_transfer_shield(object *objp, int quadrant_num)
12208 float transfer_amount;
12209 float transfer_delta;
12211 float max_quadrant_strength;
12213 sip = &Ship_info[Ships[objp->instance].ship_info_index];
12214 max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12216 transfer_amount = 0.0f;
12217 transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12219 if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12220 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12222 for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12223 if (i != quadrant_num) {
12224 if (objp->shields[i] >= transfer_delta) {
12225 objp->shields[i] -= transfer_delta;
12226 transfer_amount += transfer_delta;
12228 transfer_amount += objp->shields[i];
12229 objp->shields[i] = 0.0f;
12233 objp->shields[quadrant_num] += transfer_amount;
12236 void ai_balance_shield(object *objp)
12239 float shield_strength_avg;
12243 shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12245 delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12247 for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12248 if (objp->shields[i] < shield_strength_avg) {
12249 add_shield_strength(objp, delta);
12250 if (objp->shields[i] > shield_strength_avg)
12251 objp->shields[i] = shield_strength_avg;
12253 add_shield_strength(objp, -delta);
12254 if (objp->shields[i] < shield_strength_avg)
12255 objp->shields[i] = shield_strength_avg;
12259 // Manage the shield for this ship.
12260 // Try to max out the side that was most recently hit.
12261 void ai_manage_shield(object *objp, ai_info *aip)
12265 sip = &Ship_info[Ships[objp->instance].ship_info_index];
12267 if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12270 // Scale time until next manage shield based on Skill_level.
12271 // Ships on player's team are treated as if Skill_level is average.
12272 if (Ships[objp->instance].team != Player_ship->team){
12273 delay = Shield_manage_delays[Game_skill_level];
12275 delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12278 // Scale between 1x and 3x based on ai_class
12279 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12280 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12282 if (sip->flags & SIF_SMALL_SHIP) {
12283 if (Missiontime - aip->last_hit_time < F1_0*10)
12284 ai_transfer_shield(objp, aip->last_hit_quadrant);
12286 ai_balance_shield(objp);
12289 // nprintf(("AI", "Time: %7.3f Next: %7.3f, Shields: %7.3f %7.3f %7.3f %7.3f\n", f2fl(Missiontime), f2fl(Missiontime) + delay, objp->shields[0], objp->shields[1], objp->shields[2], objp->shields[3]));
12293 // See if object *objp should evade an incoming missile.
12294 // *aip is the ai_info pointer within *objp.
12295 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12300 shipp = &Ships[objp->instance];
12301 sip = &Ship_info[shipp->ship_info_index];
12303 // Only small ships evade an incoming missile. Why would a capital ship try to swerve?
12304 if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12308 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
12312 if (aip->nearest_locked_object != -1) {
12313 object *missile_objp;
12315 missile_objp = &Objects[aip->nearest_locked_object];
12317 if (Weapons[missile_objp->instance].homing_object != objp) {
12318 //nprintf(("AI", "\nMissile lost home!"));
12319 aip->nearest_locked_object = -1;
12323 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12324 float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12325 float dist2 = 4.0f * vm_vec_mag_quick(&missile_objp->phys_info.vel);
12326 if (dist < dist2) {
12327 switch (aip->mode) {
12328 // If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12330 if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12333 ; // Alan -- If you want to handle incoming weapons from someone other than the ship
12334 // the strafing ship is attacking, do it here.
12338 // Don't always go into evade weapon mode. Usually, a countermeasure gets launched.
12339 // If low on countermeasures, more likely to try to evade. If 8+, never evade due to low cmeasures.
12340 if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) ||
12341 (objp->phys_info.speed < 40.0f) ||
12342 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12343 if (aip->submode != SM_ATTACK_FOREVER) { // SM_ATTACK_FOREVER means engines blown.
12344 aip->submode = SM_EVADE_WEAPON;
12345 aip->submode_start_time = Missiontime;
12349 case AIM_DOCK: // Ships in dock mode can evade iif they are not currently repairing or docked.
12350 if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12353 // If in guard mode and far away from guard object, don't pursue guy that hit me.
12354 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12355 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12360 case AIM_GET_BEHIND:
12361 case AIM_STAY_NEAR:
12364 case AIM_WAYPOINTS:
12368 case AIM_BE_REARMED:
12370 case AIM_BAY_EMERGE:
12371 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12372 aip->previous_mode = aip->mode;
12373 aip->previous_submode = aip->submode;
12374 aip->mode = AIM_EVADE_WEAPON;
12376 aip->submode_start_time = Missiontime;
12377 aip->mode_time = timestamp(MAX_EVADE_TIME); // Max time to evade.
12378 //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12380 case AIM_EVADE_WEAPON: // Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12381 case AIM_PLAY_DEAD:
12382 case AIM_BAY_DEPART:
12383 case AIM_SENTRYGUN:
12388 Int3(); // Hey, what mode is it?
12393 aip->nearest_locked_object = -1;
12398 // Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12399 // Have an 80% chance of evading in a second
12400 void maybe_evade_dumbfire_weapon(ai_info *aip)
12402 // Only small ships evade an incoming missile. Why would a capital ship try to swerve?
12403 if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12407 // Make sure in a mode in which we evade dumbfire weapons.
12408 switch (aip->mode) {
12410 if (aip->submode == SM_ATTACK_FOREVER) {
12414 // If in guard mode and far away from guard object, don't pursue guy that hit me.
12415 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12416 if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12421 case AIM_STAY_NEAR:
12423 case AIM_GET_BEHIND:
12427 case AIM_WAYPOINTS:
12433 case AIM_PLAY_DEAD:
12434 case AIM_EVADE_WEAPON:
12435 case AIM_BAY_EMERGE:
12436 case AIM_BAY_DEPART:
12437 case AIM_SENTRYGUN:
12441 Int3(); // Bogus mode!
12445 if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12446 return; // Instructor doesn't evade.
12448 float t = ai_endangered_by_weapon(aip);
12449 if ((t > 0.0f) && (t < 1.0f)) {
12450 // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12451 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12455 switch (aip->mode) {
12457 switch (aip->submode) {
12459 case SM_ATTACK_FOREVER:
12462 case SM_EVADE_WEAPON:
12465 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12466 //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12467 aip->submode = SM_SUPER_ATTACK;
12468 aip->submode_start_time = Missiontime;
12469 aip->last_attack_time = Missiontime;
12471 //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12472 aip->submode = SM_EVADE_WEAPON;
12473 aip->submode_start_time = Missiontime;
12480 case AIM_STAY_NEAR:
12482 case AIM_GET_BEHIND:
12486 case AIM_WAYPOINTS:
12488 if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12489 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12490 aip->previous_mode = aip->mode;
12491 aip->previous_submode = aip->submode;
12492 aip->mode = AIM_EVADE_WEAPON;
12494 aip->submode_start_time = Missiontime;
12495 aip->mode_time = timestamp(MAX_EVADE_TIME); // Evade for up to five seconds.
12501 case AIM_PLAY_DEAD:
12502 case AIM_EVADE_WEAPON:
12503 case AIM_BAY_EMERGE:
12504 case AIM_BAY_DEPART:
12505 case AIM_SENTRYGUN:
12508 Int3(); // Bogus mode!
12513 // determine what path to use when emerging from a fighter bay
12514 // input: pl_objp => pointer to object for ship that is arriving
12515 // pos => output parameter, it is the starting world pos for path choosen
12516 // v.fvec => output parameter, this is the forward vector that ship has when arriving
12518 // exit: -1 => path could not be located
12520 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12522 int path_index, sb_path_index;
12523 ship *parent_sp = NULL;
12528 vector *next_point;
12530 aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12532 if ( parent_objnum == -1 ) {
12537 parent_sp = &Ships[Objects[parent_objnum].instance];
12539 SDL_assert(parent_sp != NULL);
12540 pm = model_get( parent_sp->modelnum );
12546 if ( sb->num_paths <= 0 )
12549 // try to find a bay path that is not taken
12551 sb_path_index = Ai_last_arrive_path++;
12553 if ( sb_path_index >= sb->num_paths ) {
12555 Ai_last_arrive_path=0;
12558 path_index = sb->paths[sb_path_index];
12559 if ( path_index == -1 )
12562 // create the path for pl_objp to follow
12563 create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12565 // Set this flag, so we don't bother recreating the path... we won't need to update the path
12566 // that has just been created.
12567 // aip->ai_flags |= AIF_USE_STATIC_PATH;
12569 // now return to the caller what the starting world pos and starting fvec for the ship will be
12570 SDL_assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12571 pnp = &Path_points[aip->path_start];
12574 // calc the forward vector using the starting two points of the path
12575 pnp = &Path_points[aip->path_start+1];
12576 next_point = &pnp->pos;
12577 vm_vec_normalized_dir(fvec, next_point, pos);
12579 // record the parent objnum, since we'll need it once we're done with following the path
12580 aip->goal_objnum = parent_objnum;
12581 aip->goal_signature = Objects[parent_objnum].signature;
12582 aip->mode = AIM_BAY_EMERGE;
12583 aip->submode_start_time = Missiontime;
12585 // set up starting vel
12588 speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12590 vm_vec_scale( &vel, speed );
12591 pl_objp->phys_info.vel = vel;
12592 pl_objp->phys_info.desired_vel = vel;
12593 pl_objp->phys_info.prev_ramp_vel.xyz.x = 0.0f;
12594 pl_objp->phys_info.prev_ramp_vel.xyz.y = 0.0f;
12595 pl_objp->phys_info.prev_ramp_vel.xyz.z = speed;
12596 pl_objp->phys_info.forward_thrust = 0.0f; // How much the forward thruster is applied. 0-1.
12601 // clean up path data used for emerging from a fighter bay
12602 void ai_emerge_bay_path_cleanup(ai_info *aip)
12604 aip->path_start = -1;
12605 aip->path_cur = -1;
12606 aip->path_length = 0;
12607 aip->mode = AIM_NONE;
12610 // handler for AIM_BAY_EMERGE
12611 void ai_bay_emerge()
12616 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12618 // if no path to follow, leave this mode
12619 if ( aip->path_start < 0 ) {
12620 aip->mode = AIM_NONE;
12624 // ensure parent ship is still alive
12625 if ( aip->goal_objnum < 0 ) {
12628 if ( !parent_died ) {
12629 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12634 if ( !parent_died ) {
12635 SDL_assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12636 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12641 if ( parent_died ) {
12642 ai_emerge_bay_path_cleanup(aip);
12646 // follow the path to the final point
12649 // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12650 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)) {
12652 ai_emerge_bay_path_cleanup(aip);
12655 // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps
12656 if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12657 ai_emerge_bay_path_cleanup(aip);
12661 // Select the closest depart path
12663 // input: aip => ai info pointer to ship seeking to depart
12664 // pm => pointer to polymodel for the ship contining the ship bay/depart paths
12666 // exit: >=0 => ship bay path index for depart path (ie index into sb->paths[])
12667 // -1 => no path could be found
12669 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12670 // AI_BAY_DEPART. It tries to find the closest path that isn't already in use
12671 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12673 int i, j, best_path, best_free_path;
12674 float dist, min_dist, min_free_dist;
12681 best_free_path = best_path = -1;
12682 min_free_dist = min_dist = 1e20f;
12683 SDL_assert(aip->shipnum >= 0);
12684 source = &Objects[Ships[aip->shipnum].objnum].pos;
12686 for ( i = 0; i < sb->num_paths; i++ ) {
12689 mp = &pm->paths[sb->paths[i]];
12690 for ( j = 0; j < mp->nverts; j++ ) {
12691 dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12693 if ( dist < min_dist ) {
12698 // If this is a free path
12699 if ( !(sb->depart_flags & (1<<i)) ) {
12700 if ( dist < min_free_dist ) {
12701 min_free_dist = dist;
12702 best_free_path = i;
12708 if ( best_free_path >= 0 ) {
12709 return best_free_path;
12715 // determine what path to use when trying to depart to a fighter bay
12716 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12718 // input: pl_objp => pointer to object for ship that is departing
12720 // exit: -1 => could not find depart path
12721 // 0 => found depart path
12722 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12724 int objnum, path_index;
12730 aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12732 if ( parent_objnum == -1 ) {
12735 // for now just locate a captial ship on the same team:
12736 so = GET_FIRST(&Ship_obj_list);
12738 while(so != END_OF_LIST(&Ship_obj_list)){
12739 sp = &Ships[Objects[so->objnum].instance];
12740 if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12741 objnum = so->objnum;
12747 objnum = parent_objnum;
12750 aip->path_start = -1;
12752 if ( objnum == -1 )
12755 pm = model_get( Ships[Objects[objnum].instance].modelnum );
12760 if ( sb->num_paths <= 0 )
12766 for ( i = 0; i < sb->num_paths; i++ ) {
12767 if ( !(sb->depart_flags & (1<<i)) ) {
12768 sb->depart_flags |= (1<<i);
12769 path_index = sb->paths[i];
12770 aip->submode_parm0 = i; // use mode-specific parameter to record ship bay path index
12776 // take the closest path we can find
12778 ship_bay_path = ai_find_closest_depart_path(aip, pm);
12780 if (ship_bay_path == -1) {
12784 path_index = sb->paths[ship_bay_path];
12785 aip->submode_parm0 = ship_bay_path;
12786 sb->depart_flags |= (1<<ship_bay_path);
12788 if ( path_index == -1 ) {
12792 SDL_assert(pm->n_paths > path_index);
12793 ai_find_path(pl_objp, objnum, path_index, 0);
12795 // Set this flag, so we don't bother recreating the path... we won't need to update the path
12796 // that has just been created.
12797 aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12799 aip->goal_objnum = objnum;
12800 aip->goal_signature = Objects[objnum].signature;
12801 aip->mode = AIM_BAY_DEPART;
12803 Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12807 // handler for AIM_BAY_DEPART
12808 void ai_bay_depart()
12812 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12814 // if no path to follow, leave this mode
12815 if ( aip->path_start < 0 ) {
12816 aip->mode = AIM_NONE;
12820 // check if parent ship still exists, if not abort depart
12821 if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12822 aip->mode = AIM_NONE;
12826 // follow the path to the final point
12829 // if the final point is reached, let default AI take over
12830 if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12834 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12836 if ( sb != NULL ) {
12837 sb->depart_flags &= ~(1<<aip->submode_parm0);
12840 // make ship disappear
12841 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12842 ship_departed( Pl_objp->instance );
12844 // clean up path stuff
12845 aip->path_start = -1;
12846 aip->path_cur = -1;
12847 aip->path_length = 0;
12848 aip->mode = AIM_NONE;
12852 // Handler for AIM_SENTRYGUN. This AI mode is for sentry guns only (ie floating turrets).
12853 void ai_sentrygun()
12855 // Nothing to do here. Turret firing is handled via process_subobjects().
12856 // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12859 // --------------------------------------------------------------------------
12860 // Execute behavior given by aip->mode.
12861 void ai_execute_behavior(ai_info *aip)
12863 switch (aip->mode) {
12867 } else if (aip->submode == SM_EVADE_WEAPON) {
12869 // maybe reset submode
12870 if (aip->danger_weapon_objnum == -1) {
12871 aip->submode = SM_ATTACK;
12872 aip->submode_start_time = Missiontime;
12873 aip->last_attack_time = Missiontime;
12876 // Don't circle if this is the instructor.
12877 ship *shipp = &Ships[aip->shipnum];
12878 ship_info *sip = &Ship_info[shipp->ship_info_index];
12880 if (SDL_strncasecmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12881 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12882 aip->mode = AIM_NONE;
12884 ai_chase_circle(Pl_objp);
12894 vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.v.rvec, 100.0f);
12895 turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12896 accelerate_ship(aip, 0.5f);
12902 case AIM_STAY_NEAR:
12908 case AIM_WAYPOINTS:
12918 ai_big_ship(Pl_objp);
12922 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12923 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12930 case AIM_EVADE_WEAPON:
12935 SDL_assert(En_objp->type == OBJ_SHIP);
12936 ai_big_strafe(); // strafe a big ship
12938 aip->mode = AIM_NONE;
12941 case AIM_BAY_EMERGE:
12944 case AIM_BAY_DEPART:
12947 case AIM_SENTRYGUN:
12951 break; // Note, handled directly from ai_frame().
12953 Int3(); // This should never happen -- MK, 5/12/97
12957 if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12958 maybe_evade_dumbfire_weapon(aip);
12962 // Auxiliary function for maybe_request_support.
12963 // Return 1 if subsystem "type" is worthy of repair, else return 0.
12964 // Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12965 int mrs_subsystem(ship *shipp, int type)
12969 t = ship_get_subsystem_strength(shipp, type);
12972 return (int) ((1.0f - t) * 3);
12978 // Return number of ships on *objp's team that are currently rearming.
12979 int num_allies_rearming(object *objp)
12985 team = Ships[objp->instance].team;
12987 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12990 SDL_assert (so->objnum != -1);
12991 A = &Objects[so->objnum];
12993 if (Ships[A->instance].team == team) {
12994 if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
13004 // Maybe ship *objp should request support (rearm/repair).
13005 // If it does, return TRUE, else return FALSE.
13006 int maybe_request_support(object *objp)
13013 SDL_assert(objp->type == OBJ_SHIP);
13014 shipp = &Ships[objp->instance];
13015 aip = &Ai_info[shipp->ai_index];
13016 sip = &Ship_info[shipp->ship_info_index];
13018 if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
13021 // Only fighters and bombers request support.
13022 if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
13025 // A ship that is currently awaiting does not need support!
13026 if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13029 if (!is_support_allowed(objp))
13032 //if (shipp->team != TEAM_FRIENDLY)
13035 // Compute a desire value.
13036 // Desire of 0 means no reason to request support.
13037 // 1 is slight, 2 more, etc. Maximum is around 20. Anything larger than 3 is pretty strong.
13040 // Set desire based on hull strength.
13041 // No: We no longer repair hull, so this would cause repeated repair requests.
13042 //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
13044 // Set desire based on key subsystems.
13045 desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE); // Note, disabled engine forces repair request, regardless of nearby enemies.
13046 desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
13047 desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
13048 desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
13050 // Set desire based on percentage of secondary weapons.
13051 ship_weapon *swp = &shipp->weapons;
13053 for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
13054 if (swp->secondary_bank_start_ammo[i] > 0) {
13055 // float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
13056 float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
13057 desire += (int) ((1.0f - r) * 3.0f);
13061 // If no reason to repair, don't bother to see if it's safe to repair.
13066 // Compute danger threshold.
13067 // Balance this with desire and maybe request support.
13068 if (ai_good_time_to_rearm( objp )) {
13069 ai_issue_rearm_request(objp);
13071 } else if (num_allies_rearming(objp) < 2) {
13072 if (desire >= 8) { // guarantees disabled will cause repair request
13073 ai_issue_rearm_request(objp);
13074 } else if (desire >= 3) { // >= 3 means having a single subsystem fully blown will cause repair.
13076 int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13078 if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13079 ai_issue_rearm_request(objp);
13082 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13091 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13093 ai_abort_rearm_request(objp);
13094 if (aip->mode != AIM_WARP_OUT) {
13095 aip->mode = AIM_WARP_OUT;
13096 aip->submode = AIS_WARP_1;
13100 // Maybe warp ship out.
13101 // Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13102 void ai_maybe_warp_out(object *objp)
13106 // don't do anything if in a training mission.
13107 if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13110 SDL_assert(objp->type == OBJ_SHIP);
13112 shipp = &Ships[objp->instance];
13113 ai_info *aip = &Ai_info[shipp->ai_index];
13115 if (aip->mode == AIM_WARP_OUT)
13118 // If a support ship with no goals and low hull, warp out. Be sure that there are no pending goals
13119 // in the support ships ai_goal array. Just process this ships goals.
13120 ship_info *sip = &Ship_info[shipp->ship_info_index];
13121 if (sip->flags & SIF_SUPPORT) {
13122 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13123 ai_process_mission_orders( OBJ_INDEX(objp), aip );
13124 if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13125 ai_set_mode_warp_out(objp, aip);
13130 // Friendly don't warp out, they'll eventually request support.
13131 if (shipp->team == TEAM_FRIENDLY)
13134 if (!(shipp->flags & SF_DEPARTING)) {
13137 sip = &Ship_info[shipp->ship_info_index];
13138 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13139 if (aip->warp_out_timestamp == 0) {
13140 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13141 // aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13143 } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13144 ai_set_mode_warp_out(objp, aip);
13150 // Warp this ship out.
13151 void ai_warp_out(object *objp)
13153 // if dying, don't warp out.
13154 if (Ships[objp->instance].flags & SF_DYING) {
13160 aip = &Ai_info[Ships[objp->instance].ai_index];
13162 switch (aip->submode) {
13164 aip->force_warp_time = timestamp(10*1000); // Try to avoid a collision for up to ten seconds.
13165 aip->submode = AIS_WARP_2;
13167 case AIS_WARP_2: // Make sure won't collide with any object.
13168 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13169 aip->submode = AIS_WARP_3;
13171 // maybe recalculate collision pairs.
13172 if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13173 // recalculate collision pairs
13174 OBJ_RECALC_PAIRS(objp);
13177 aip->force_warp_time = timestamp(4*1000); // Try to attain target speed for up to 4 seconds.
13180 vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.v.uvec, 100.0f);
13181 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13182 accelerate_ship(aip, 0.0f);
13186 // Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13187 // desired velocity is computed in shipfx_calculate_warp_time(). See shipfx#572 for sample code.
13188 float speed, goal_speed;
13189 float shipfx_calculate_warp_speed(object*);
13190 goal_speed = shipfx_calculate_warp_speed(objp);
13192 // HUGE ships go immediately to AIS_WARP_4
13193 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13194 aip->submode = AIS_WARP_4;
13197 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13198 //goal_speed = 80.0f;
13199 //set_accel_for_target_speed(objp, 40.0f);
13200 // DKA 8/11/99 objp->phys_info.flags |= PF_USE_VEL; This flag is set in object code if warping out and AIS_WARP >= 3, properly accounting for blown engines
13201 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13202 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.v.fvec, speed);
13203 objp->phys_info.desired_vel = objp->phys_info.vel;
13204 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13205 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13206 aip->submode = AIS_WARP_4;
13209 shipfx_warpout_start(objp);
13210 aip->submode = AIS_WARP_5;
13216 Int3(); // Illegal submode for warping out.
13220 // Return object index of weapon that could produce a shockwave that should be known about to *objp.
13221 // Return nearest one.
13222 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13225 float nearest_dist = 999999.9f;
13226 int nearest_index = -1;
13228 for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13233 SDL_assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13234 A = &Objects[mo->objnum];
13236 SDL_assert(A->type == OBJ_WEAPON);
13237 SDL_assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13238 wp = &Weapons[A->instance];
13239 wip = &Weapon_info[wp->weapon_info_index];
13240 SDL_assert( wip->subtype == WP_MISSILE );
13242 if (wip->shockwave_speed > 0.0f) {
13245 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13246 if (dist < nearest_dist) {
13247 nearest_dist = dist;
13248 nearest_index = mo->objnum;
13253 return nearest_index;
13257 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD 100.0f
13259 // Tell all ships to avoid a big ship that is blowing up.
13260 // Only avoid if shockwave is fairly large.
13261 // OK to tell everyone to avoid. If they're too far away, that gets cleaned up in the frame interval.
13262 void ai_announce_ship_dying(object *dying_objp)
13264 float damage = ship_get_exp_damage(dying_objp);
13265 if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13268 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13269 if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13272 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13274 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13275 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13283 // Return object index of weapon that could produce a shockwave that should be known about to *objp.
13284 // Return nearest one.
13285 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13288 float nearest_dist = 999999.9f;
13289 int nearest_index = -1;
13291 for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13295 SDL_assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13296 A = &Objects[so->objnum];
13298 SDL_assert(A->type == OBJ_SHIP);
13299 SDL_assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13300 shipp = &Ships[A->instance];
13301 // Only look at objects in the process of dying.
13302 if (shipp->flags & SF_DYING) {
13303 float damage = ship_get_exp_damage(objp);
13305 if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) { // Only evade quite large blasts
13308 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13309 if (dist < nearest_dist) {
13310 nearest_dist = dist;
13311 nearest_index = so->objnum;
13317 return nearest_index;
13321 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13323 // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13324 if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13325 // If we don't currently know of a weapon to avoid, try to find one.
13326 // If we can't find one, then clear the bit so we don't keep coming here.
13327 if (aip->shockwave_object == -1) {
13328 int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13329 if (shockwave_weapon == -1) {
13330 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13333 aip->shockwave_object = shockwave_weapon;
13337 // OK, we have reason to believe we should avoid aip->shockwave_object.
13338 SDL_assert(aip->shockwave_object > -1);
13339 object *weapon_objp = &Objects[aip->shockwave_object];
13340 if (weapon_objp->type != OBJ_WEAPON) {
13341 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13342 aip->shockwave_object = -1;
13346 weapon *weaponp = &Weapons[weapon_objp->instance];
13347 weapon_info *wip = &Weapon_info[weaponp->weapon_info_index];
13348 object *target_ship_obj = NULL;
13350 if (wip->shockwave_speed == 0.0f) {
13351 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13352 aip->shockwave_object = -1;
13357 vector expected_pos; // Position at which we expect the weapon to detonate.
13360 danger_dist = wip->outer_radius;
13361 // Set predicted position of detonation.
13362 // If an aspect locked missile, assume it will detonate at the homing position.
13363 // If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13364 // time in the future, this time based on max lifetime and life left.
13365 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13366 expected_pos = weaponp->homing_pos;
13367 if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13368 target_ship_obj = weaponp->homing_object;
13371 if (IS_VEC_NULL(&weaponp->homing_pos)) {
13373 if (weaponp->target_num != -1) {
13374 if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13375 target_ship_obj = &Objects[weaponp->target_num];
13376 expected_pos = target_ship_obj->pos;
13386 if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13389 time_scale = weaponp->lifeleft/2.0f;
13392 vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, time_scale);
13395 // See if too far away to care about shockwave.
13396 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13397 //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13400 // try to find a safe position
13401 vector vec_from_exp;
13403 vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13404 float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.v.fvec);
13406 // if we're already on the other side of the explosion, don't try to fly behind it
13410 // Fly towards a point behind the weapon.
13411 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.v.fvec, -50000.0f*dir);
13413 // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13414 // don't bang your head, else go
13415 // int go_safe = FALSE;
13416 int go_safe = TRUE;
13417 /* if (target_ship_obj) {
13418 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13419 // try up to 2 other random directions
13420 vector dir_vec, rand_vec;
13422 for (idx=0; idx<2; idx++) {
13423 vm_vec_rand_vec_quick(&rand_vec);
13424 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.v.fvec, &rand_vec, 0.5f);
13425 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13426 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13431 } else { // direct path is safe
13434 } else { // no target_obj_ship
13441 // can't figure out a good way to go
13445 } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13446 if (aip->shockwave_object == -1) {
13447 int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13448 if (shockwave_ship == -1) {
13449 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13452 aip->shockwave_object = shockwave_ship;
13456 SDL_assert(aip->shockwave_object > -1);
13457 object *ship_objp = &Objects[aip->shockwave_object];
13458 if (ship_objp == objp) {
13459 aip->shockwave_object = -1;
13463 if (ship_objp->type != OBJ_SHIP) {
13464 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13468 // Optimize note! Don't really have to normalize. We only need a point away from the blowing-up ship.
13471 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13472 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f); // Fly away from the ship.
13474 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13476 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13477 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13484 Int3(); // Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon. What is it!?
13493 void rand_chance_test()
13503 for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13506 nprintf(("AI", "%6.4f: ", frametime));
13507 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13510 for (i=0; i<100.0f/frametime; i++) {
13511 if (rand_chance(frametime, chance))
13514 nprintf(("AI", "%3i ", count));
13516 nprintf(("AI", "\n"));
13521 // --------------------------------------------------------------------------
13522 // Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13523 // If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13524 // Return 1 if avoiding a shockwave, else return 0.
13525 int ai_avoid_shockwave(object *objp, ai_info *aip)
13529 //rand_chance_test();
13530 // BIG|HUGE do not respond to shockwaves
13531 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13532 // don't come here again
13533 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13537 // Don't all react right away.
13538 if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13539 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f)) // Chance to avoid in 1 second is 0.25 + ai_class/4
13542 if (!aas_1(objp, aip, &safe_pos)) {
13543 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13547 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13549 // OK, evade the shockwave!
13550 turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13551 vector vec_to_safe_pos;
13554 vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13556 dot_to_goal = vm_vec_dot(&objp->orient.v.fvec, &vec_to_safe_pos);
13557 if (dot_to_goal < -0.5f)
13558 accelerate_ship(aip, 0.3f);
13560 accelerate_ship(aip, 1.0f + dot_to_goal);
13561 if (dot_to_goal > 0.2f) {
13562 if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13563 afterburners_start(objp);
13564 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13572 // Awaiting repair. Be useful.
13573 // Probably fly towards incoming repair ship.
13574 // Return true if this ship is close to being repaired, else return false.
13575 int ai_await_repair_frame(object *objp, ai_info *aip)
13577 if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13580 if (aip->dock_objnum == -1)
13586 shipp = &Ships[Objects[aip->dock_objnum].instance];
13587 sip = &Ship_info[shipp->ship_info_index];
13589 aip->ai_flags &= ~AIF_FORMATION_OBJECT; // Prevents endless rotation.
13591 if (!(sip->flags & SIF_SUPPORT))
13595 object *repair_objp;
13597 repair_objp = &Objects[aip->dock_objnum];
13599 if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13600 ai_abort_rearm_request(repair_objp);
13604 vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.v.uvec, -50.0f); // Fly towards point below repair ship.
13607 float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13608 float dot = vm_vec_dot(&vtr, &objp->orient.v.fvec);
13610 if (dist > 200.0f) {
13611 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13612 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13613 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13615 accelerate_ship(aip, 0.0f);
13616 //nprintf(("AI", "%s sitting still awaiting repair from %s, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13622 // Maybe cause this ship to self-destruct.
13623 // Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13624 // Maybe should only do this if they are preventing their wing from re-entering.
13625 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13627 // Friendly ships can be repaired, so no self-destruct.
13628 // In multiplayer, just don't self-destruct. I figured there would be a problem. -- MK, 3/19/98.
13629 if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13632 // Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13633 // Reason: Don't want them to prevent a re-emergence of the wing.
13634 // 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
13635 // mission would be broken.
13636 if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13637 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13638 (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13639 if (aip->self_destruct_timestamp < 0)
13640 aip->self_destruct_timestamp = timestamp(90 * 1000); // seconds until self-destruct
13642 aip->self_destruct_timestamp = -1;
13645 if (aip->self_destruct_timestamp < 0) {
13649 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13650 ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13655 // Determine if pl_objp needs a new target, called from ai_frame()
13656 int ai_need_new_target(object *pl_objp, int target_objnum)
13660 if ( target_objnum < 0 ) {
13664 objp = &Objects[target_objnum];
13666 if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13670 if ( objp->type == OBJ_SHIP ) {
13671 if ( Ships[objp->instance].flags & SF_DYING ) {
13673 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13680 // If *objp is recovering from a collision with a big ship, handle it.
13681 // Return true if recovering.
13682 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13687 if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13688 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);
13689 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13690 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13691 accelerate_ship(aip, dot);
13693 // If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13694 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13695 aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13696 aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13701 } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13702 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);
13703 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13704 dot = vm_vec_dot(&objp->orient.v.fvec, &v2g);
13705 accelerate_ship(aip, dot);
13707 // If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13708 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13709 aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13710 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13716 if (aip->ai_flags & AIF_TARGET_COLLISION) {
13717 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13722 void validate_mode_submode(ai_info *aip)
13724 switch (aip->mode) {
13726 // check valid submode
13727 switch (aip->submode) {
13728 case SM_CONTINUOUS_TURN:
13730 case SM_EVADE_SQUIGGLE:
13731 case SM_EVADE_BRAKE:
13733 case SM_SUPER_ATTACK:
13735 case SM_GET_BEHIND:
13737 case SM_EVADE_WEAPON:
13739 case SM_ATTACK_FOREVER:
13747 // check valid submode
13748 switch(aip->submode) {
13749 case AIS_STRAFE_ATTACK:
13750 case AIS_STRAFE_AVOID:
13751 case AIS_STRAFE_RETREAT1:
13752 case AIS_STRAFE_RETREAT2:
13753 case AIS_STRAFE_POSITION:
13762 // --------------------------------------------------------------------------
13763 // Process AI object "objnum".
13764 void ai_frame(int objnum)
13766 ship *shipp = &Ships[Objects[objnum].instance];
13767 ai_info *aip = &Ai_info[shipp->ai_index];
13770 // validate_mode_submode(aip);
13772 SDL_assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13774 // Set globals defining the current object and its enemy object.
13775 Pl_objp = &Objects[objnum];
13777 if (aip->mode == AIM_WARP_OUT) {
13778 ai_warp_out(Pl_objp);
13782 /* // HACK! TEST! REMOVE ME!
13783 if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13784 if (shipp->team == Player_ship->team)
13785 aip->mode = AIM_CHASE;
13788 // if (!SDL_strncasecmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13789 // nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13791 ai_maybe_self_destruct(Pl_objp, aip);
13793 // if ( timestamp_elapsed(aip->goal_check_time) ) {
13794 ai_process_mission_orders( objnum, aip );
13795 // aip->goal_check_time = timestamp_rand(1000,2000);
13798 // Avoid a shockwave, if necessary. If a shockwave and rearming, stop rearming.
13799 if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13800 if (ai_avoid_shockwave(Pl_objp, aip)) {
13801 aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13802 if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13803 ai_abort_rearm_request(Pl_objp);
13807 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13810 // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13811 // if the ship is getting repaired
13812 // If waiting to be repaired, just stop and sit.
13813 ai_do_repair_frame(Pl_objp, aip, flFrametime);
13814 if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13815 if (ai_await_repair_frame(Pl_objp, aip))
13819 if (aip->mode == AIM_PLAY_DEAD)
13822 // If recovering from a collision with a big ship, don't continue.
13823 if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13826 ai_preprocess_ignore_objnum(Pl_objp, aip);
13827 target_objnum = set_target_objnum(aip, aip->target_objnum);
13829 // nprintf(("AI", "Frame %i: Coords = %7.3f %7.3f %7.3f\n", AI_FrameCount, Pl_objp->pos.xyz.x, Pl_objp->pos.xyz.y, Pl_objp->pos.xyz.z));
13831 SDL_assert(objnum != target_objnum);
13833 ai_manage_shield(Pl_objp, aip);
13835 if ( maybe_request_support(Pl_objp) ) {
13836 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13837 ship_maybe_tell_about_rearm(shipp);
13841 ai_maybe_warp_out(Pl_objp);
13844 // If this ship is attacking an object's subsystems and someone else destroyed
13845 // the subsystem, it could continue attacking the ship. Need to invalidate the objnum.
13846 if (target_objnum >= 0)
13847 if (Objects[target_objnum].flags & OF_PROTECTED) {
13848 // if (aip->targeted_subsys != NULL)
13849 // ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13851 if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13852 target_objnum = -1;
13853 aip->target_objnum = -1;
13859 // Find an enemy if don't already have one.
13861 if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13862 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13863 aip->resume_goal_time = -1;
13864 aip->active_goal = AI_GOAL_NONE;
13865 } else if (aip->resume_goal_time == -1) {
13866 // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13867 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13868 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]); // Attack up to 25K units away.
13869 if (target_objnum != -1) {
13870 if (aip->target_objnum != target_objnum)
13871 aip->aspect_locked_time = 0.0f;
13872 set_target_objnum(aip, target_objnum);
13873 En_objp = &Objects[target_objnum];
13877 } else if (target_objnum >= 0) {
13878 En_objp = &Objects[target_objnum];
13881 // set base stealth info each frame
13882 aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13883 if (En_objp && En_objp->type == OBJ_SHIP) {
13884 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13885 int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13886 float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13888 if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13889 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13892 if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13893 aip->stealth_last_visible_stamp = timestamp();
13894 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13895 aip->stealth_last_pos = En_objp->pos;
13896 aip->stealth_velocity = En_objp->phys_info.vel;
13897 } else if (dist < 100) {
13898 // get cheat timestamp
13899 aip->stealth_last_cheat_visible_stamp = timestamp();
13901 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13902 update_ai_stealth_info_with_error(aip/*, 0*/);
13907 /* if ((Pl_objp != NULL) && (En_objp != NULL)) {
13912 // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13913 if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13914 aip->target_objnum = -1;
13917 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)) {
13918 mprintf(("Warning: Object and its enemy have same position. Object #%i\n", Pl_objp-Objects));
13922 if (aip->mode == AIM_CHASE) {
13923 if (En_objp == NULL) {
13924 aip->active_goal = -1;
13928 // If there is a goal to resume and enough time has elapsed, resume the goal.
13929 if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13930 aip->active_goal = AI_GOAL_NONE;
13931 aip->resume_goal_time = -1;
13932 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13933 if (target_objnum != -1) {
13934 if (aip->target_objnum != target_objnum) {
13935 aip->aspect_locked_time = 0.0f;
13937 set_target_objnum(aip, target_objnum);
13941 // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13942 // if trying to disable or disarm the target
13943 if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13944 SDL_assert(En_objp->type == OBJ_SHIP);
13945 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13948 if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13949 subsys_type = SUBSYSTEM_ENGINE;
13950 } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13951 subsys_type = SUBSYSTEM_TURRET;
13956 if ( subsys_type != -1 ) {
13957 ship_subsys *new_subsys;
13958 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13959 if ( new_subsys != NULL ) {
13960 set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13962 // AL 12-16-97: no more subsystems to attack... reset targeting info
13963 aip->target_objnum = -1;
13964 set_targeted_subsys(aip, NULL, -1);
13967 // targeted subsys is destroyed, so stop attacking it
13968 set_targeted_subsys(aip, NULL, -1);
13973 ai_maybe_launch_cmeasure(Pl_objp, aip);
13974 ai_maybe_evade_locked_missile(Pl_objp, aip);
13976 aip->target_time += flFrametime;
13978 int in_formation = 0;
13979 if (aip->ai_flags & AIF_FORMATION) {
13980 in_formation = !ai_formation();
13983 if ( !in_formation ) {
13984 ai_execute_behavior(aip);
13987 process_subobjects(objnum);
13988 maybe_resume_previous_mode(Pl_objp, aip);
13990 if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13991 if (Missiontime > aip->afterburner_stop_time) {
13992 //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13993 afterburners_stop(Pl_objp);
13996 // validate_mode_submode(aip);
13999 int Waypoints_created = 0;
14001 // Find the ship with the name *name in the Ship_info array.
14002 int find_ship_name(char *name)
14006 for (i=0; i<Num_ship_types; i++)
14007 if (!strcmp(Ship_info[i].name, name))
14013 void create_waypoints()
14017 // Waypoints_created = 1;
14019 if (Waypoints_created)
14022 for (j=0; j<Num_waypoint_lists; j++)
14023 for (i=0; i<Waypoint_lists[j].count; i++) {
14024 z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
14025 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
14028 Waypoints_created = 1;
14031 int Last_ai_obj = -1;
14033 void ai_process( object * obj, int ai_index, float frametime )
14035 // if (Ships[obj->instance].flags & SF_DYING)
14036 // nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
14038 if (obj->flags & OF_SHOULD_BE_DEAD)
14041 // 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.
14042 if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
14046 int rfc = 1; // Assume will be Reading Flying Controls.
14048 SDL_assert( obj->type == OBJ_SHIP );
14049 SDL_assert( ai_index >= 0 );
14053 create_waypoints();
14055 AI_frametime = frametime;
14056 if (obj-Objects <= Last_ai_obj) {
14060 memset( &AI_ci, 0, sizeof(AI_ci) );
14062 ai_frame(obj-Objects);
14064 AI_ci.pitch = 0.0f;
14066 AI_ci.heading = 0.0f;
14068 // the ships maximum velocity now depends on the energy flowing to engines
14069 obj->phys_info.max_vel.xyz.z = Ships[obj->instance].current_max_speed;
14070 ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14072 // In certain circumstances, the AI says don't fly in the normal way.
14073 // One circumstance is in docking and undocking, when the ship is moving
14074 // under thruster control.
14075 switch (aip->mode) {
14077 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14081 if (aip->submode >= AIS_WARP_3)
14085 // if (aip->submode == AIS_NONE_FORMATION)
14093 vector copy_desired_rotvel = obj->phys_info.rotvel;
14094 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14095 // if obj is in formation and not flight leader, don't update rotvel
14096 if (aip->ai_flags & AIF_FORMATION) {
14097 if (&Objects[aip->goal_objnum] != obj) {
14098 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14099 obj->phys_info.rotvel = copy_desired_rotvel;
14104 Last_ai_obj = obj-Objects;
14107 // Initialize ai_info struct of object objnum.
14108 void init_ai_object(int objnum)
14110 int ship_index, ai_index;
14114 vector near_vec; // A vector nearby and mainly in front of this object.
14116 objp = &Objects[objnum];
14117 ship_index = objp->instance;
14118 ai_index = Ships[ship_index].ai_index;
14119 SDL_assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14121 aip = &Ai_info[ai_index];
14123 ship_type = Ships[ship_index].ship_info_index;
14125 vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.v.fvec, 100.0f);
14126 vm_vec_scale_add2(&near_vec, &objp->orient.v.rvec, 10.0f);
14128 // Things that shouldn't have to get initialized, but initialize them just in case!
14130 aip->previous_mode = AIM_NONE;
14131 aip->mode_time = -1;
14132 aip->target_objnum = -1;
14133 aip->target_signature = -1;
14134 aip->previous_target_objnum = -1;
14135 aip->target_time = 0.0f;
14136 aip->enemy_wing = -1;
14137 aip->attacker_objnum = -1;
14138 aip->goal_objnum = -1;
14139 aip->goal_signature = -1;
14140 aip->guard_objnum = -1;
14141 aip->guard_signature = -1;
14142 aip->guard_wingnum = -1;
14143 aip->dock_signature = -1;
14145 aip->previous_submode = 0;
14146 aip->best_dot_to_enemy = -1.0f;
14147 aip->best_dot_from_enemy = -1.0f;
14148 aip->best_dot_to_time = 0;
14149 aip->best_dot_from_time = 0;
14150 aip->submode_start_time = 0;
14151 aip->submode_parm0 = 0;
14152 aip->active_goal = -1;
14153 aip->goal_check_time = timestamp(0);
14154 aip->last_predicted_enemy_pos = near_vec;
14155 aip->prev_goal_point = near_vec;
14156 aip->goal_point = near_vec;
14157 aip->time_enemy_in_range = 0.0f;
14158 aip->last_attack_time = 0;
14159 aip->last_hit_time = 0;
14160 aip->last_hit_quadrant = 0;
14161 aip->hitter_objnum = -1;
14162 aip->hitter_signature = -1;
14163 aip->resume_goal_time = -1;
14164 aip->prev_accel = 0.0f;
14165 aip->prev_dot_to_goal = 0.0f;
14167 aip->ignore_objnum = UNUSED_OBJNUM;
14168 aip->ignore_signature = -1;
14170 // aip->mode = AIM_NONE;
14172 // End of Things that shouldn't have to get initialized, but initialize them just in case!
14174 aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14175 aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14176 aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14177 aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14179 if (Num_waypoint_lists > 0) {
14180 aip->wp_index = -1;
14183 aip->wp_index = -1;
14187 aip->attacker_objnum = -1;
14188 aip->goal_signature = -1;
14190 Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.v.fvec;
14192 aip->last_predicted_enemy_pos.xyz.x = 0.0f; // Says this value needs to be recomputed!
14193 aip->time_enemy_in_range = 0.0f;
14195 aip->resume_goal_time = -1; // Say there is no goal to resume.
14197 aip->active_goal = -1;
14198 aip->path_start = -1;
14199 aip->path_goal_dist = -1;
14200 aip->path_length = 0;
14201 aip->path_subsystem_next_check = 1;
14202 aip->dock_path_index = -1;
14203 aip->dock_index = -1;
14204 aip->dock_objnum = -1;
14206 aip->danger_weapon_objnum = -1;
14207 aip->danger_weapon_signature = -1;
14209 aip->lead_scale = 0.0f;
14210 aip->last_hit_target_time = Missiontime;
14212 aip->nearest_locked_object = -1;
14213 aip->nearest_locked_distance = 99999.0f;
14215 aip->targeted_subsys = NULL;
14216 aip->last_subsys_target = NULL;
14217 aip->targeted_subsys_parent = -1;
14219 // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14220 aip->rearm_first_missile = TRUE; // flag to indicate that next missile to load is the first missile
14221 aip->rearm_release_delay = 0; // timestamp to delay the separation of docked ships after rearm
14223 aip->next_predict_pos_time = 0;
14225 aip->afterburner_stop_time = 0;
14226 aip->last_objsig_hit = -1; // object signature of the ship most recently hit by aip
14228 aip->path_next_create_time = timestamp(1);
14229 aip->path_create_pos = Objects[objnum].pos;
14230 aip->path_create_orient = Objects[objnum].orient;
14232 aip->ignore_expire_timestamp = timestamp(1);
14233 aip->warp_out_timestamp = 0;
14234 aip->next_rearm_request_timestamp = timestamp(1);
14235 aip->primary_select_timestamp = timestamp(1);
14236 aip->secondary_select_timestamp = timestamp(1);
14237 aip->scan_for_enemy_timestamp = timestamp(1);
14239 aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14241 aip->shockwave_object = -1;
14242 aip->shield_manage_timestamp = timestamp(1);
14243 aip->self_destruct_timestamp = -1; // This is a flag that we have not yet set this.
14244 aip->ok_to_target_timestamp = timestamp(1);
14245 aip->pick_big_attack_point_timestamp = timestamp(1);
14246 vm_vec_zero(&aip->big_attack_point);
14248 aip->avoid_check_timestamp = timestamp(1);
14250 aip->abort_rearm_timestamp = -1;
14253 aip->artillery_objnum = -1;
14254 aip->artillery_sig = -1;
14256 // waypoint speed cap
14257 aip->waypoint_speed_cap = -1;
14259 // set lethality to enemy team
14260 aip->lethality = 0.0f;
14263 void init_ai_objects()
14267 for (i=0; i<num_objects; i++){
14268 if (Objects[i].type == OBJ_SHIP){
14274 void init_ai_system()
14276 // MWA -- removed next line of code on 11/12/97. When a ship is created
14277 // it calls init_ai_object() on it's objnum. Doing this init at the point where
14278 // this function gets called messes things up.
14279 //init_ai_objects();
14281 Ppfp = Path_points;
14282 Waypoints_created = 0;
14284 Dock_path_warning_given = 0;
14286 /* for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14287 Ignore_objects[i].objnum = -1;
14288 Ignore_objects[i].signature = -1;
14294 void ai_set_default_behavior(object *obj, int classnum)
14298 SDL_assert(obj != NULL);
14299 SDL_assert(obj->instance != -1);
14300 SDL_assert(Ships[obj->instance].ai_index != -1);
14302 aip = &Ai_info[Ships[obj->instance].ai_index];
14304 aip->behavior = classnum;
14308 void ai_do_default_behavior(object *obj)
14313 SDL_assert(obj != NULL);
14314 SDL_assert(obj->instance != -1);
14315 SDL_assert(Ships[obj->instance].ai_index != -1);
14317 aip = &Ai_info[Ships[obj->instance].ai_index];
14319 ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14320 if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14321 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14322 set_target_objnum(aip, enemy_objnum);
14323 aip->mode = AIM_CHASE;
14324 aip->submode = SM_ATTACK;
14325 } else if (ship_flags & (SIF_SUPPORT)) {
14326 aip->mode = AIM_SAFETY;
14327 aip->submode = AISS_1;
14328 aip->ai_flags &= ~(AIF_REPAIRING);
14329 } else if ( ship_flags & SIF_SENTRYGUN ) {
14330 aip->mode = AIM_SENTRYGUN;
14332 aip->mode = AIM_NONE;
14335 aip->submode_start_time = Missiontime;
14336 aip->active_goal = AI_GOAL_NONE;
14339 #define FRIENDLY_DAMAGE_THRESHOLD 50.0f // Display a message at this threshold. Note, this gets scaled by Skill_level
14341 // send the given message from objp. called from the maybe_process_friendly_hit
14342 // code below when a message must get send to the player when he fires on friendlies
14343 void process_friendly_hit_message( int message, object *objp )
14347 // no traitor in multiplayer
14348 if(Game_mode & GM_MULTIPLAYER){
14352 // don't send this message if a player ship was hit.
14353 if ( objp->flags & OF_PLAYER_SHIP ){
14357 // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14358 index = objp->instance;
14359 if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14363 // if the message is "oops" (the don't hit me message), always make come from Terran command
14364 if ( message == MESSAGE_OOPS ){
14369 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14371 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14375 extern void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14377 // Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14378 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14380 // no turning traitor in multiplayer
14381 if ( Game_mode & GM_MULTIPLAYER ) {
14385 // ditto if mission says no traitors allowed
14386 if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14390 if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14392 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point. If so, bail out.
14393 if ( objp_hitter->type != OBJ_SHIP ) {
14397 SDL_assert(objp_hitter->type == OBJ_SHIP);
14398 SDL_assert(objp_hit->type == OBJ_SHIP);
14399 SDL_assert(objp_weapon->type == OBJ_WEAPON);
14401 ship *shipp_hitter = &Ships[objp_hitter->instance];
14402 ship *shipp_hit = &Ships[objp_hit->instance];
14404 if (shipp_hitter->team != shipp_hit->team) {
14409 player *pp = &Players[Player_num];
14411 // wacky stuff here
14412 if (pp->friendly_hits != 0) {
14413 float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14414 if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14415 if (time_since_last_hit > 60.0f) {
14416 pp->friendly_hits = 0;
14417 pp->friendly_damage = 0.0f;
14418 } else if (time_since_last_hit > 2.0f) {
14419 pp->friendly_hits -= (int) time_since_last_hit/2;
14420 pp->friendly_damage -= time_since_last_hit;
14423 if (pp->friendly_damage < 0.0f) {
14424 pp->friendly_damage = 0.0f;
14427 if (pp->friendly_hits < 0) {
14428 pp->friendly_hits = 0;
14433 float damage; // Damage done by weapon. Gets scaled down based on size of ship.
14435 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14437 // wacky stuff here
14438 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14439 if (sip->initial_hull_strength > 1000.0f) {
14440 float factor = sip->initial_hull_strength / 1000.0f;
14441 factor = min(100.0f, factor);
14445 // Don't penalize much at all for hitting cargo
14446 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14450 // Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14451 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14455 pp->friendly_last_hit_time = Missiontime;
14456 pp->friendly_hits++;
14458 // cap damage and number of hits done this frame
14459 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14460 pp->friendly_damage += accredited_damage;
14461 pp->damage_this_burst += accredited_damage;
14463 // Done with adjustments to damage. Evaluate based on current friendly_damage
14464 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 ));
14466 if (is_instructor(objp_hit)) {
14467 // it's not nice to hit your instructor
14468 if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14469 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14470 pp->last_warning_message_time = Missiontime;
14471 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14475 // Instructor warp out.
14476 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14477 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
14479 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 2*(get_shield_strength(objp_hitter) + Ship_info[shipp_hitter->ship_info_index].initial_hull_strength) );
14480 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14481 } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14482 // warning every 4 sec
14483 // use NULL as the message sender here since it is the Terran Command persona
14484 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14485 pp->last_warning_message_time = Missiontime;
14488 // not nice to hit your friends
14489 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14490 process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14491 mission_goal_fail_all();
14492 ai_abort_rearm_request( Player_obj );
14494 Player_ship->team = TEAM_TRAITOR;
14496 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14497 // no closer than 4 sec intervals
14498 // Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
14499 // many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
14500 process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14501 pp->last_warning_message_time = Missiontime;
14506 // Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14507 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14509 SDL_assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14511 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14512 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14516 // only set as target if can be targeted.
14517 if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14521 if (aip->target_objnum != hitter_objnum)
14522 aip->aspect_locked_time = 0.0f;
14523 set_target_objnum(aip, hitter_objnum);
14524 aip->resume_goal_time = Missiontime + i2f(20); // Only chase up to 20 seconds.
14525 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14527 set_targeted_subsys(aip, NULL, -1); // Say not attacking any particular subsystem.
14529 aip->previous_submode = aip->mode;
14530 aip->mode = AIM_CHASE;
14531 aip->submode = SM_ATTACK;
14535 // Return true if *objp has armed an aspect seeking bomb.
14536 // This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14537 int firing_aspect_seeking_bomb(object *objp)
14543 shipp = &Ships[objp->instance];
14545 swp = &shipp->weapons;
14547 bank_index = swp->current_secondary_bank;
14549 if (bank_index != -1)
14550 if (swp->secondary_bank_ammo[bank_index] > 0) {
14551 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14552 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14561 // *objp collided with big ship *big_objp at global point *collide_pos
14562 // Make it fly away from the collision point.
14563 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14564 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14568 SDL_assert(objp->type == OBJ_SHIP);
14570 aip = &Ai_info[Ships[objp->instance].ai_index];
14572 if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14575 //nprintf(("AI", "Ship %s beginning to avoid ship %s at time %7.3f", Ships[objp->instance].ship_name, Ships[big_objp->instance].ship_name, f2fl(Missiontime)));
14576 if (collision_normal) {
14577 aip->big_recover_timestamp = timestamp(2000);
14578 aip->big_collision_normal = *collision_normal;
14579 // nprintf(("AI", " normal\n"));
14581 aip->big_recover_timestamp = timestamp(500);
14582 // nprintf(("AI", " no normal\n"));
14586 aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14587 aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14590 // vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14592 // big_recover_pos_1 is 100 m out along normal
14594 if (collision_normal) {
14595 direction = *collision_normal;
14597 vm_vec_copy_scale(&direction, &objp->orient.v.fvec, -1.0f);
14599 vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14601 // go out 200 m from box closest box point
14602 get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14604 accelerate_ship(aip, 0.0f);
14606 if (vm_vec_dot(collision_normal, &objp->orient.v.fvec) > 0.5f) {
14607 // vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14608 // vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14609 // vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14610 accelerate_ship(aip, 2.0f);
14612 // vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14613 // vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.v.uvec, big_objp->radius/2.0f);
14614 accelerate_ship(aip, 0.0f);
14618 float max_lethality = 0.0f;
14620 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14622 SDL_assert(ship_obj->type == OBJ_SHIP);
14623 SDL_assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14624 int dont_count = FALSE;
14626 int parent = other_obj->parent;
14627 if (Objects[parent].type == OBJ_SHIP) {
14628 if (Objects[parent].signature == other_obj->parent_sig) {
14630 // check damage done to enemy team
14631 if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14634 if (other_obj->type == OBJ_WEAPON) {
14635 weapon *wp = &Weapons[other_obj->instance];
14636 weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14638 // if parent is BIG|HUGE, don't count beam
14639 if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14640 if (wif->wi_flags & WIF_BEAM) {
14647 float lethality = 0.025f * damage; // 2 cyclops (@2000) put you at 100 lethality
14649 // increase lethality weapon's parent ship
14650 ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14651 aip->lethality += lethality;
14652 aip->lethality = min(110.0f, aip->lethality);
14653 // if you hit, don;t be less than 0
14654 aip->lethality = max(0.0f, aip->lethality);
14656 // if (aip->lethality > max_lethality) {
14657 // max_lethality = aip->lethality;
14658 // mprintf(("new lethalilty high: %.1f\n", max_lethality));
14661 // if parent is player, show his lethality
14662 // if (Objects[parent].flags & OF_PLAYER_SHIP) {
14663 // mprintf(("Player lethality: %.1f\n", aip->lethality));
14672 // Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14673 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14675 int hitter_objnum = -2;
14676 object *objp_hitter = NULL;
14678 ai_info *aip, *hitter_aip;
14680 shipp = &Ships[objp_ship->instance];
14681 aip = &Ai_info[shipp->ai_index];
14683 if (objp_ship->flags & OF_PLAYER_SHIP)
14686 if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14689 if (hit_objp->type == OBJ_SHIP) {
14690 // If the object that this ship collided with is a big ship
14691 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14692 // And the current object is _not_ a big ship
14693 if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14694 // 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.
14695 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14700 if (hit_objp->type == OBJ_WEAPON) {
14701 // Make sure the object that fired this weapon is still alive. If not, abort.
14702 // SDL_assert(hit_objp->parent >= 0);
14703 if(hit_objp->parent < 0){
14706 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14710 // Hit by a protected ship, don't attack it.
14711 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14712 if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14713 if (aip->mode == AIM_CHASE) {
14714 if (aip->submode != SM_EVADE_WEAPON) {
14715 aip->mode = AIM_CHASE;
14716 aip->submode = SM_EVADE_WEAPON;
14717 aip->submode_start_time = Missiontime;
14719 } else if (aip->mode != AIM_EVADE_WEAPON) {
14720 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14721 aip->previous_mode = aip->mode;
14722 aip->previous_submode = aip->submode;
14723 aip->mode = AIM_EVADE_WEAPON;
14725 aip->submode_start_time = Missiontime;
14726 aip->mode_time = timestamp(MAX_EVADE_TIME); // Evade for up to five seconds.
14733 hitter_objnum = hit_objp->parent;
14734 SDL_assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14735 objp_hitter = &Objects[hitter_objnum];
14736 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp); // Deal with player's friendly fire.
14738 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14739 ship_maybe_ask_for_help(shipp);
14741 } else if (hit_objp->type == OBJ_SHIP) {
14742 if (shipp->team == Ships[hit_objp->instance].team) // Don't have AI react to collisions between teammates.
14744 objp_hitter = hit_objp;
14745 hitter_objnum = hit_objp-Objects;
14747 Int3(); // Hmm, what kind of object hit this if not weapon or ship? Get MikeK.
14751 // Collided into a protected ship, don't attack it.
14752 if (hit_objp->flags & OF_PROTECTED)
14755 SDL_assert(objp_hitter != NULL);
14756 hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14757 hitter_aip->last_hit_target_time = Missiontime;
14759 // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14760 hitter_aip->last_objsig_hit = objp_ship->signature;
14762 aip->last_hit_time = Missiontime;
14764 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
14767 // If this ship is awaiting repair, abort!
14768 if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14769 ship_info *sip = &Ship_info[shipp->ship_info_index];
14771 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14772 // No, only abort if hull below a certain level.
14773 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2); // Might request again after 15 seconds.
14774 if ( !(objp_ship->flags & OF_PLAYER_SHIP) ) // mwa -- don't abort rearm for a player
14775 ai_abort_rearm_request(objp_ship);
14779 // If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14780 // Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14781 if (firing_aspect_seeking_bomb(objp_ship)) {
14782 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14786 // If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14787 if (aip->mode == AIM_STRAFE) {
14788 SDL_assert(hitter_objnum != -2);
14789 if (aip->target_objnum == hitter_objnum) {
14790 if ( hit_objp->type == OBJ_WEAPON ) {
14791 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14797 ; // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14798 // in code later in this function
14802 if (objp_ship == Player_obj)
14803 return; // We don't do AI for the player.
14805 maybe_update_guard_object(objp_ship, objp_hitter);
14807 // Big ships don't go any further.
14808 if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14811 // If the hitter object is the ignore object, don't attack it.
14812 ship_info *sip = &Ship_info[shipp->ship_info_index];
14813 if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14814 if (aip->mode == AIM_NONE) {
14815 aip->mode = AIM_CHASE; // This will cause the ship to move, if not attack.
14816 aip->submode = SM_EVADE;
14821 // Maybe abort based on mode.
14822 switch (aip->mode) {
14824 if (aip->submode == SM_ATTACK_FOREVER)
14827 if ( hit_objp->type == OBJ_WEAPON ) {
14828 if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14833 // If in guard mode and far away from guard object, don't pursue guy that hit me.
14834 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14835 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14840 case AIM_STAY_NEAR:
14841 // Note: Dealt with above, at very top. case AIM_PLAY_DEAD:
14844 case AIM_EVADE_WEAPON:
14846 case AIM_GET_BEHIND:
14852 case AIM_BAY_DEPART:
14853 case AIM_SENTRYGUN:
14855 case AIM_BAY_EMERGE:
14856 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14857 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14861 case AIM_WAYPOINTS:
14862 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14868 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14869 aip->submode = AISS_1;
14870 aip->submode_start_time = Missiontime;
14878 Int3(); // Bogus mode!
14881 if (timestamp_elapsed(aip->ok_to_target_timestamp))
14882 aip->ai_flags &= ~AIF_FORMATION; // If flying in formation, bug out!
14884 aip->hitter_objnum = hitter_objnum;
14885 aip->hitter_signature = Objects[hitter_objnum].signature;
14887 // If the hitter is not on the same team as the hittee, do some stuff.
14888 if (shipp->team != Ships[objp_hitter->instance].team) {
14889 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14891 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14892 maybe_set_dynamic_chase(aip, hitter_objnum);
14893 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14895 if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14896 switch (aip->submode) {
14898 case SM_SUPER_ATTACK:
14902 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14903 maybe_set_dynamic_chase(aip, hitter_objnum);
14905 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14908 } else if (aip->mode == AIM_CHASE) {
14909 switch (aip->submode) {
14911 aip->submode = SM_EVADE;
14912 aip->submode_start_time = Missiontime;
14914 case SM_SUPER_ATTACK:
14915 if (Missiontime - aip->submode_start_time > i2f(1)) {
14916 aip->submode = SM_EVADE;
14917 aip->submode_start_time = Missiontime;
14920 case SM_EVADE_BRAKE:
14922 case SM_EVADE_SQUIGGLE:
14923 aip->submode = SM_EVADE;
14924 aip->submode_start_time = Missiontime;
14927 if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14928 maybe_set_dynamic_chase(aip, hitter_objnum);
14929 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14935 // AL 3-15-98: Prevent escape pods from entering chase mode
14936 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14937 maybe_set_dynamic_chase(aip, hitter_objnum);
14939 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14945 // Ship shipnum has been destroyed.
14947 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14948 // This function will get called in either case, and there are things that should be done if
14949 // the ship actually gets destroyed which shouldn't get done if it departed.
14950 void ai_ship_destroy(int shipnum, int method)
14953 object *other_objp;
14958 SDL_assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14959 objnum = Ships[shipnum].objnum;
14960 dead_aip = &Ai_info[Ships[shipnum].ai_index];
14962 // if I was getting repaired, or awaiting repair, then cleanup the repair mode. When awaiting repair, the dock_objnum
14963 // is -1. When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14964 if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14965 if ( dead_aip->dock_objnum >= 0 )
14966 ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14968 ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14971 // For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14972 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14973 other_objp = &Objects[so->objnum];
14974 SDL_assert(other_objp->instance != -1);
14976 shipp = &Ships[other_objp->instance];
14977 SDL_assert(shipp->ai_index != -1);
14979 ai_info *aip = &Ai_info[shipp->ai_index];
14982 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14983 // support ship starts it's death roll.
14985 // If the destroyed ship was on its way to repair the current ship
14986 if (aip->dock_objnum == objnum) {
14988 // clean up the flags for any kind of docking mode. If aip was part of a goal of dock/undock
14989 // then it will get cleaned up by the goal code.
14990 ai_do_objects_undocked_stuff( other_objp, NULL );
14992 if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14994 if ( method == SEF_DEPARTED ) {
14995 abort_reason = REPAIR_INFO_ABORT;
14997 abort_reason = REPAIR_INFO_KILLED;
14999 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
15003 if (aip->target_objnum == objnum) {
15004 set_target_objnum(aip, -1);
15005 // If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
15006 if (aip->resume_goal_time != -1)
15007 aip->active_goal = AI_GOAL_NONE;
15010 if (aip->goal_objnum == objnum) {
15011 aip->goal_objnum = -1;
15012 aip->goal_signature = -1;
15015 if (aip->guard_objnum == objnum) {
15016 aip->guard_objnum = -1;
15017 aip->guard_signature = -1;
15020 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
15021 if (aip->guard_wingnum != aip->wing)
15022 ai_set_guard_wing(other_objp, aip->guard_wingnum);
15025 if (aip->hitter_objnum == objnum)
15026 aip->hitter_objnum = -1;
15033 // Interface function to goals code.
15034 // Make object *objp fly to point *vp and warp out.
15035 void ai_warp_out(object *objp, vector *vp)
15039 aip = &Ai_info[Ships[objp->instance].ai_index];
15041 if (aip->mode != AIM_WARP_OUT) {
15042 ai_set_mode_warp_out(objp, aip);
15049 dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
15051 if (dist < objp->radius + 5.0f) {
15053 // Start the warp out effect
15054 shipfx_warpout_start(objp);
15057 dot = vm_vec_dot(&objp->orient.v.fvec, &v2v);
15059 aip = &Ai_info[Ships[objp->instance].ai_index];
15062 accelerate_ship(aip, 1.0f);
15064 accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
15066 turn_towards_point(objp, vp, NULL, 0.0f);
15072 // Do stuff at start of deathroll.
15073 void ai_deathroll_start(object *ship_obj)
15076 ship *shipp, *other_ship;
15078 shipp = &Ships[ship_obj->instance];
15079 aip = &Ai_info[shipp->ai_index];
15081 // mark object we are docked with so we can do damage and separate during deathroll
15082 // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15083 if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15084 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15085 // set other_ship dock_objnum_when_dead, if other_ship exits.
15086 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15087 other_ship = &Ships[Objects[aip->dock_objnum].instance];
15088 other_ship->dock_objnum_when_dead = shipp->objnum;
15092 ai_cleanup_dock_mode(aip, shipp);
15094 aip->mode = AIM_NONE;
15097 // Object *requester_objp tells rearm ship to abort rearm.
15098 // Returns true if it succeeded, else false.
15099 // To succeed means you were previously rearming.
15100 int ai_abort_rearm_request(object *requester_objp)
15102 ship *requester_shipp;
15103 ai_info *requester_aip;
15105 SDL_assert(requester_objp->type == OBJ_SHIP);
15106 if(requester_objp->type != OBJ_SHIP){
15109 SDL_assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15110 if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15113 requester_shipp = &Ships[requester_objp->instance];
15114 SDL_assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15115 if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15118 requester_aip = &Ai_info[requester_shipp->ai_index];
15120 if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15122 // dock_objnum is always valid once a rearm repair has been requested. It points to the
15123 // ship that is coming to repair me.
15124 if (requester_aip->dock_objnum != -1) {
15125 object *repair_objp;
15126 ai_info *repair_aip;
15128 repair_objp = &Objects[requester_aip->dock_objnum];
15129 repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15131 // Make sure signatures match. This prevents nasty bugs in which an object
15132 // that was repairing another is destroyed and is replaced by another ship
15133 // before this code comes around.
15134 if (repair_objp->signature == requester_aip->dock_signature) {
15136 SDL_assert( repair_objp->type == OBJ_SHIP );
15138 // if support ship is in the process of undocking, don't do anything.
15139 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15140 ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15142 if ( repair_aip->submode == AIS_DOCK_4 )
15143 repair_aip->submode = AIS_UNDOCK_0;
15145 repair_aip->submode = AIS_UNDOCK_3;
15147 repair_aip->submode_start_time = Missiontime;
15149 nprintf(("AI", "Not aborting rearm since already undocking\n"));
15153 // setting these flags is the safe things to do. There may not be a corresponding repair
15154 // ship for this guys since a repair ship may be currently repairing someone else.
15155 ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15157 // try and remove this guy from an arriving support ship.
15158 mission_remove_scheduled_repair(requester_objp);
15162 } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15163 // a support ship can request to abort when he is told to do something else (like warp out).
15164 // see if this support ships goal_objnum is valid. If so, then issue this ai_abort comment
15165 // for the ship that he is enroute to repair
15166 if ( requester_aip->goal_objnum != -1 ) {
15169 val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15177 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15178 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15179 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15181 ship *support_shipp, *requester_shipp;
15182 ai_info *support_aip, *requester_aip;
15184 support_shipp = &Ships[support_objp->instance];
15185 requester_shipp = &Ships[requester_objp->instance];
15186 requester_aip = &Ai_info[requester_shipp->ai_index];
15188 SDL_assert( support_shipp->ai_index != -1 );
15189 support_aip = &Ai_info[support_shipp->ai_index];
15191 // if the requester is a player object, issue the order as the squadmate messaging code does. Doing so
15192 // ensures that the player get a higher priority!
15193 requester_aip->ai_flags |= AIF_AWAITING_REPAIR; // Tell that I'm awaiting repair.
15194 if ( requester_objp->flags & OF_PLAYER_SHIP )
15195 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15197 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15201 // Object *requester_objp requests rearming.
15202 // Returns objnum of ship coming to repair requester on success
15203 // Success means you found someone to rearm you and you weren't previously rearming.
15204 int ai_issue_rearm_request(object *requester_objp)
15207 ship *requester_shipp;
15208 ai_info *requester_aip;
15210 SDL_assert(requester_objp->type == OBJ_SHIP);
15211 SDL_assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15212 requester_shipp = &Ships[requester_objp->instance];
15213 SDL_assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15214 requester_aip = &Ai_info[requester_shipp->ai_index];
15216 // Make sure not already awaiting repair.
15217 if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15218 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));
15222 if ( !is_support_allowed(requester_objp) )
15225 //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15226 requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP); // Might request again after this much time.
15228 // call ship_find_repair_ship to get a support ship. If none is found, then we will warp one in. This
15229 // function will return the next available ship which can repair requester
15230 objp = ship_find_repair_ship( requester_objp );
15231 ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15234 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15235 // would properly update their hud support view
15236 //ai_add_rearm_goal( requester_objp, objp );
15237 return OBJ_INDEX(objp);
15240 // call to warp in repair ship!!!! for now, warp in any number of ships needed. Should cap it to
15241 // some reasonable max (or let support ships warp out). We should assume here that ship_find_repair_ship()
15242 // would have returned a valid object if there are too many support ships already in the mission
15243 mission_warp_in_support_ship( requester_objp );
15250 // make objp rearm and repair goal_objp
15251 void ai_rearm_repair( object *objp, object *goal_objp, int priority, int docker_index, int dockee_index )
15253 ai_info *aip, *goal_aip;
15255 aip = &Ai_info[Ships[objp->instance].ai_index];
15256 aip->goal_objnum = goal_objp-Objects;
15258 // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15260 ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15261 aip->ai_flags |= AIF_REPAIRING; // Tell that repair guy is busy trying to repair someone.
15263 goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15264 goal_aip->dock_objnum = objp-Objects; // Tell which object is coming to repair.
15265 goal_aip->dock_signature = objp->signature;
15267 ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15269 goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15272 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15273 // into polymodel->dockbays[] for the model associated with the object), return the index
15274 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15275 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15277 if ( dockbay_index < 0 || dockee_objp == NULL ) {
15278 Int3(); // should never happen
15282 if ( dockee_objp->type == OBJ_SHIP ) {
15286 pm = model_get( Ships[dockee_objp->instance].modelnum );
15289 SDL_assert(pm->n_docks > dockbay_index);
15290 SDL_assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15291 SDL_assert(pm->docking_bays[dockbay_index].splines != NULL);
15292 if(pm->n_docks <= dockbay_index){
15295 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15298 if(pm->docking_bays[dockbay_index].splines == NULL){
15302 // We only need to return one path for the dockbay, so return the first
15303 path_num = pm->docking_bays[dockbay_index].splines[0];
15310 // Actually go ahead and fire the synaptics.
15311 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15314 swp = &shipp->weapons;
15315 int current_bank = swp->current_secondary_bank;
15317 ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15318 if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15319 if (ship_fire_secondary(objp)) {
15320 nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15321 swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15326 // For the subspace mission (sm3-09a)
15328 // if they're sufficiently far into the mission
15329 // if they're near one or more enemies
15331 // fire a synaptic if they have one.
15332 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15334 // Only do in subspace missions.
15335 if ( The_mission.flags & MISSION_FLAG_SUBSPACE ) {
15339 shipp = &Ships[objp->instance];
15341 if (!(SDL_strncasecmp(shipp->ship_name, NOX("delta"), 5))) {
15342 num = shipp->ship_name[6] - '1';
15344 if ((num >= 0) && (num <= 3)) {
15345 time = Missiontime >> 16; // Convert to seconds.
15347 time -= 2*60; // Subtract off two minutes.
15350 int modulus = 17 + num*3;
15352 if ((time % modulus) < 2) {
15353 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15356 cheat_fire_synaptic(objp, shipp, aip);