Additional AI code, including stuff taking advantage of glide and sidethrust. Includes: Flag for allowing fighters to dodge vertically as well as horizontally, "stalemate detection" to help AI break out of circle fights and other stalemate situations, "glide attack" where AI fighters will use glide to fly away while shooting back at you, "circle strafe" where AI fighters will use sidethrusters to jink around while shooting, and an (incomplete) version of "glide strafe" that allows fighters/bombers to use glide to attack capital ships. All new stuff is off by default, and can be turned on with AI_Profiles flags.

This commit is contained in:
Sushi committed 2009-07-13 20:08:32 +00:00
1 parent 915a9270c9
commit 9246a2f74d
11 files changed
+419 -33

No files matched your search

+8 -1
View File
@@ -196,6 +196,10 @@ typedef struct ai_class {
#define SM_BIG_CIRCLE 16 // Big ship flies circle around other big ship to get good angle to go parallel
#define SM_BIG_PARALLEL 17 // Big ship flies parallel to another
//SUSHI: Attack submodes (besides those implicitly listed above)
#define AIS_CHASE_GLIDEATTACK 18 // Ship uses glide to move in a constant direction while pointing and shooting at target
#define AIS_CHASE_CIRCLESTRAFE 19 // Attempt a circle-strafe on the target
// Submodes for docking behavior
#define AIS_DOCK_0 21
#define AIS_DOCK_1 22
@@ -222,6 +226,7 @@ typedef struct ai_class {
#define AIS_STRAFE_RETREAT1 203 // fly away from attack point
#define AIS_STRAFE_RETREAT2 204
#define AIS_STRAFE_POSITION 205 // re-position to resume strafing attack
#define AIS_STRAFE_GLIDE_ATTACK 206 // SUSHI: Glide strafe atack
#define WPF_REPEAT (1 << 0)
#define WPF_BACKTRACK (1 << 1)
@@ -330,6 +335,7 @@ typedef struct ai_info {
fix best_dot_from_time; // time at which best dot occurred
fix submode_start_time; // time at which we entered the current submode
int submode_parm0; // parameter specific to current submode
int submode_parm1; // SUSHI: Another optional parameter
fix next_predict_pos_time; // Next time to predict position.
ai_goal goals[MAX_AI_GOALS];
@@ -337,7 +343,8 @@ typedef struct ai_info {
int goal_check_time; // timer used for processing goals for this ai object
vec3d last_predicted_enemy_pos; // Where he thought enemy was last time.
float time_enemy_in_range; // Amount of time enemy continuously in "sight", near crosshair.
float time_enemy_in_range; // Amount of time enemy continuously in "sight", near crosshair.
float time_enemy_near; // SUSHI: amount of time enemy continuously "near" the player
fix last_attack_time; // Missiontime of last time this ship attacked its enemy.
fix last_hit_time; // Missiontime of last time this ship was hit by anyone.
int last_hit_quadrant; // Shield section of last hit.
+17
View File
@@ -224,6 +224,21 @@ void parse_ai_profiles_tbl(char *filename)
if (optional_string("$AI Turn Time Scale:"))
parse_float_list(profile->turn_time_scale, NUM_SKILL_LEVELS);
if (optional_string("$Glide Attack Percent:"))
parse_float_list(profile->glide_attack_percent, NUM_SKILL_LEVELS);
if (optional_string("$Circle Strafe Percent:"))
parse_float_list(profile->circle_strafe_percent, NUM_SKILL_LEVELS);
if (optional_string("$Glide Strafe Percent:"))
parse_float_list(profile->glide_strafe_percent, NUM_SKILL_LEVELS);
if (optional_string("$Stalemate Time Threshold:"))
parse_float_list(profile->stalemate_time_thresh, NUM_SKILL_LEVELS);
if (optional_string("$Stalemate Distance Threshold:"))
parse_float_list(profile->stalemate_dist_thresh, NUM_SKILL_LEVELS);
if (optional_string("$Player Weapon Recharge Scale:"))
parse_float_list(profile->weapon_energy_scale, NUM_SKILL_LEVELS);
@@ -307,6 +322,8 @@ void parse_ai_profiles_tbl(char *filename)
set_flag(profile, "$use only single fov for turrets:", AIPF_USE_ONLY_SINGLE_FOV_FOR_TURRETS);
set_flag(profile, "$allow vertical dodge:", AIPF_ALLOW_VERTICAL_DODGE);
// if we've been through once already and are at the same place, force a move
if ( saved_Mp && (saved_Mp == Mp) )
Mp++;
+6
View File
@@ -43,6 +43,7 @@
#define AIPF_MULTI_ALLOW_EMPTY_SECONDARIES (1 << 25)
#define AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY (1 << 26)
#define AIPF_USE_ONLY_SINGLE_FOV_FOR_TURRETS (1 << 27)
#define AIPF_ALLOW_VERTICAL_DODGE (1 << 27) //Allows AI ships to evade weapons vertically as well as horizontally
#define MAX_AI_PROFILES 5
@@ -79,6 +80,11 @@ typedef struct ai_profile_t {
float subsys_damage_scale[NUM_SKILL_LEVELS]; // damage applied to a player subsystem
float beam_friendly_damage_cap[NUM_SKILL_LEVELS]; // damage cap values for friendly beam fire
float turn_time_scale[NUM_SKILL_LEVELS]; // speed at which enemy ships turn
float glide_attack_percent[NUM_SKILL_LEVELS]; // SUSHI: The likelihood (0.0-1.0) of the AI to use the "glide attack" move
float circle_strafe_percent[NUM_SKILL_LEVELS]; // SUSHI: The likelihood (0.0-1.0) of the AI to use the "circle strafe" move
float glide_strafe_percent[NUM_SKILL_LEVELS]; // SUSHI: The likelihood (0.0-1.0) of the AI to use glide when strafing capships
float stalemate_time_thresh[NUM_SKILL_LEVELS]; // SUSHI: The amount of time required for the AI to detect (and try to break) dogfight stalemate
float stalemate_dist_thresh[NUM_SKILL_LEVELS]; // SUSHI: The maximum distance the AI and target must be within for a stalemate
// Multiplicative delay factors for increasing skill levels.
float ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
+162 -11
View File
@@ -50,12 +50,16 @@
#define ATTACK_COLLIDE_SLOW_DIST 150 // perpendicular distance to attack surface at which begin slowing down
#define ATTACK_COLLIDE_SLOW_TIME 1.5 // anticipated collision time at which to begin slowing down
#define GLIDE_STRAFE_DISTANCE 50.0f //Distance from the ship to pass when glide strafing
#define GLIDE_STRAFE_MIN_TIME 2 //Minimum amount of time to stay on one glide strafe approach vector
#define GLIDE_STRAFE_MAX_TIME 15 //Maximum amount of time for each glide strafe pass
// forward declarations
void ai_big_evade_ship();
void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float dist_to_enemy, int modelnum);
void ai_big_avoid_ship();
int ai_big_maybe_follow_subsys_path(int do_dot_check=1);
void ai_big_strafe_position();
extern int model_which_octant_distant_many( vec3d *pnt, int model_num,matrix *model_orient, vec3d * model_pos, polymodel **pm, int *octs);
extern void compute_desired_rvec(vec3d *rvec, vec3d *goal_pos, vec3d *cur_pos);
@@ -571,9 +575,8 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
if ( ai_big_maybe_start_strafe(aip, sip) ) {
aip->previous_mode = aip->mode;
aip->mode = AIM_STRAFE;
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
aip->submode = AIS_STRAFE_ATTACK;
aip->submode_start_time = Missiontime;
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
ai_big_strafe_position();
return;
}
@@ -799,6 +802,9 @@ void ai_big_chase()
enemy_ship_type = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
//Default to glide OFF
Pl_objp->phys_info.flags &= ~PF_GLIDING;
ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
player_pos = Pl_objp->pos;
@@ -822,7 +828,7 @@ void ai_big_chase()
if (aip->ai_flags & AIF_TARGET_COLLISION) {
if ( ai_big_strafe_maybe_retreat(dist_to_enemy, &enemy_pos) ) {
aip->mode = AIM_STRAFE;
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
aip->submode = AIS_STRAFE_AVOID;
aip->submode_start_time = Missiontime;
return;
@@ -1302,6 +1308,142 @@ void ai_big_strafe_attack()
}
}
// SUSHI: Strafe attack using glide
//submode_parm1 encodes the stage we're in.
//0: Choosing goal point to fly towards
//1: Accelerating towards the goal point.
//2: Raining down fire and brimstone on the target.
//3: Dummy stage used when resetting or exiting the mode
//TODO: **NOTE** This functionality is still WIP. Expect another commit soon polishing it up.
void ai_big_strafe_glide_attack()
{
ai_info *aip;
vec3d target_pos, vec_to_goal, predicted_enemy_pos, norm_vel_vec;
float target_dist, target_dot, target_ship_dist, dot_to_goal, flight_dot_to_enemy;
object *target_objp;
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
target_objp = &Objects[aip->target_objnum];
vm_vec_sub(&vec_to_goal, &aip->goal_point, &Pl_objp->pos);
vm_vec_normalize(&vec_to_goal);
if (vm_vec_mag(&Pl_objp->phys_info.vel) > 0.0f)
vm_vec_copy_normalize(&norm_vel_vec, &Pl_objp->phys_info.vel);
else
vm_vec_copy_normalize(&norm_vel_vec, &Pl_objp->orient.vec.fvec);
dot_to_goal = vm_vec_dot(&vec_to_goal, &norm_vel_vec); //Angle between flight path and goal point
flight_dot_to_enemy = vm_vec_dot(&target_objp->pos, &norm_vel_vec); //Angle between flight path and target ship
//If we're following a path, then we shouldn't be doing glide strafe
if ( ai_big_maybe_follow_subsys_path(0) ) {
return;
}
//Gets a point on the target ship to attack, as well as distance and the angle between the nose of the attacker and that point.
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
target_ship_dist = vm_vec_dist(&target_objp->pos, &Pl_objp->pos);
//TODO: Maybe retreat if we're too close or collision imminent? (check for imminent collision?)
//TODO: Doesn't always trigger for moving targets? (Maybe change the way we get into glide strafe?)
//TODO: Somehow deal with the target's motion better? (Don't pick a point that will lead to collision?) (Move the target point based on projected position by the time the AI gets there?)
//TODO: Subsytem attacks are a bit wonky? Maybe?
//TODO: Don't wig out if target is destroyed.
//TODO: Fix problems switching from glide strafe to chase mid-run
//TODO: Instead of using the ->radius of the target, use the APPARENT radius from the current point of attack?
//TODO: Make sure this scales well for different speeds of attacking ship
//TODO: Make sure this scales well for different sizes/speeds of target ship
//If we haven't chosen the goal point yet, do so now
if (aip->submode_parm1 == 0) {
//Pick goal point. This should be a random point that passes through the circle whose normal is the vector between attacker and target
vec3d targetToAttacker;
vec3d tangentPoint;
vm_vec_sub(&targetToAttacker, &Pl_objp->pos, &target_objp->pos);
matrix orient;
vm_vector_2_matrix(&orient, &targetToAttacker, NULL, NULL);
vm_vec_random_in_circle(&tangentPoint, &target_objp->pos, &orient, target_objp->radius + GLIDE_STRAFE_DISTANCE, 1);
//Get tangent point in coords relative to ship, scale it up so the actual goal point is far away, then put back in world coords
vm_vec_sub2(&tangentPoint, &Pl_objp->pos);
vm_vec_scale(&tangentPoint, 1000.0f);
vm_vec_add(&aip->goal_point, &tangentPoint, &Pl_objp->pos);
//TODO: Try several points and pick the one closest to the current target point? Might help vs subsystems.
aip->submode_parm1 = 1;
}
if (aip->submode_parm1 == 1) {
//Stay in acceleration stage?
//Accelerate towards goal point until:
// Mostly up to speed AND
// Moving in the correct direction
if ((Pl_objp->phys_info.fspeed > (Pl_objp->phys_info.max_vel.xyz.z * 0.85f)) && (dot_to_goal > 0.99f))
aip->submode_parm1 = 2;
}
if (aip->submode_parm1 == 2) {
//Should we stay in the fire stage?
//Keep going until we are too far away.
//If we are still on approach but too far away, this will still trigger. This will allow us to reposition the target
//point and allow for a "jinking" effect.
//TODO: Replace GLIDE_STRAFE_MIN_TIME with the same logic for normal jinking amount based on difficulty level?
if (target_ship_dist > (STRAFE_RETREAT_BOX_DIST + target_objp->radius) &&
Missiontime - aip->submode_start_time > i2f(GLIDE_STRAFE_MIN_TIME)) {
//This checks whether we are moving toward the target or away from it. If moving towards, we reset the stage so that we
//pick a new attack vector (jinking). If moving away, we're at the end of a run so do a full reset (possibly allowing a
//switch back to conventional strafe).
if (flight_dot_to_enemy > 0.0f) {
aip->submode_parm1 = 0;
}
else {
aip->submode_parm1 = 3;
aip->submode = AIS_STRAFE_POSITION;
}
aip->submode_start_time = Missiontime;
}
}
//Time limit: if we've taken too long, reset
if (Missiontime - aip->submode_start_time > i2f(GLIDE_STRAFE_MAX_TIME)) {
aip->submode_parm1 = 3;
aip->submode = AIS_STRAFE_POSITION;
aip->submode_start_time = Missiontime;
}
//Acceleration stage
if (aip->submode_parm1 == 1) {
accelerate_ship(aip, 1.0f);
//Use afterburners if we have them and are pointed the right way
if (dot_to_goal > 0.99f) {
afterburners_start(Pl_objp);
aip->afterburner_stop_time = Missiontime + 3*F1_0;
}
turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
}
//Fire stage
if (aip->submode_parm1 == 2) {
Pl_objp->phys_info.flags |= PF_GLIDING;
accelerate_ship(aip, 0.0f);
afterburners_stop(Pl_objp);
//Compensate for motion of ship and target
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
vm_vec_add2(&target_pos, &predicted_enemy_pos);
vm_vec_sub2(&target_pos, &En_objp->pos);
turn_towards_point(Pl_objp, &target_pos, NULL, 0.0f);
ai_big_maybe_fire_weapons(target_dist, target_dot, &Pl_objp->pos, &En_objp->pos, &En_objp->phys_info.vel);
}
// if haven't been hit in quite a while, leave strafe mode
// (same as ai_big_strafe_attack)
//TODO: Alternately, have a different criteria for leaving glide strafe?
fix long_enough = F1_0*20;
if ( (Missiontime - aip->last_hit_time > long_enough) && ( (Missiontime - aip->submode_parm0) > long_enough) ) {
ai_big_switch_to_chase_mode(aip);
}
}
// pick a new attack point when entering this state, and keep using it
void ai_big_strafe_avoid()
{
@@ -1319,9 +1461,7 @@ void ai_big_strafe_avoid()
return;
if ( mode_time > fl2f(0.5)) {
aip->submode = AIS_STRAFE_ATTACK;
aip->submode_start_time = Missiontime;
// nprintf(("Alan","Ship %s entering AIS_STRAFE_ATTACK at frame %d\n", Ships[aip->shipnum].ship_name, Framecount));
ai_big_strafe_position();
}
turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
@@ -1416,13 +1556,19 @@ void ai_big_strafe_retreat2()
// reposition self to begin another strafing run
void ai_big_strafe_position()
{
ship_info *sip;
ai_info *aip;
sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
// TODO: loop around or something!!
aip->submode = AIS_STRAFE_ATTACK;
//Maybe use AIS_STRAFE_GLIDE_ATTACK
if ((sip->can_glide == true) && !(aip->ai_flags & AIF_KAMIKAZE) && (frand() < The_mission.ai_profile->glide_strafe_percent[Game_skill_level])) {
aip->submode = AIS_STRAFE_GLIDE_ATTACK;
aip->submode_parm1 = 0;
} else {
aip->submode = AIS_STRAFE_ATTACK;
}
aip->submode_start_time = Missiontime;
// nprintf(("Alan","Ship %s entering AIS_STRAFE_ATTACK\n", Ships[aip->shipnum].ship_name));
}
// --------------------------------------------------------------------------
@@ -1447,6 +1593,8 @@ void ai_big_strafe()
return;
}
*/
//Default to glide OFF
Pl_objp->phys_info.flags &= ~PF_GLIDING;
// check if target is still a big ship... if not enter chase mode
if ( !(Ship_info[Ships[En_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
@@ -1470,6 +1618,9 @@ void ai_big_strafe()
case AIS_STRAFE_POSITION:
ai_big_strafe_position();
break;
case AIS_STRAFE_GLIDE_ATTACK:
ai_big_strafe_glide_attack();
break;
default:
Int3(); // Illegal submode for AIM_STRAFE
@@ -1545,7 +1696,7 @@ int ai_big_maybe_enter_strafe_mode(object *pl_objp, int weapon_objnum, int consi
// big/capital ship... so enter strafe mode
aip->previous_mode = aip->mode;
aip->mode = AIM_STRAFE;
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered (i.e. MODE start time)
aip->submode = AIS_STRAFE_AVOID;
aip->submode_start_time = Missiontime;
// nprintf(("Alan","%s Accepted strafe mode\n", Ships[pl_objp->instance].ship_name));
+177 -19
View File
@@ -77,6 +77,8 @@
#define NEXT_REARM_TIMESTAMP (60*1000) // Ships will re-request rearm, typically, after this long.
#define CIRCLE_STRAFE_DIST 250.0f //Maximum distance for circle strafe behavior.
// AIM_CHASE submode defines
// SM_STEALTH_FIND
#define SM_SF_AHEAD 0
@@ -1178,6 +1180,8 @@ int set_target_objnum(ai_info *aip, int objnum)
aip->target_objnum = objnum;
aip->target_time = 0.0f;
aip->time_enemy_near = 0.0f; //SUSHI: Reset the "time near" counter whenever the target is changed
aip->last_hit_target_time = Missiontime; //Also, the "last hit target" time should be reset when the target changes
aip->target_signature = (objnum >= 0) ? Objects[objnum].signature : -1;
// clear targeted subsystem
set_targeted_subsys(aip, NULL, -1);
@@ -4606,7 +4610,7 @@ int static_rand_timed(int num, int modulus)
else {
int t;
t = Missiontime >> 18; // Get time in quarters of a second
t = Missiontime >> 18; // Get time in quarters of a second (SUSHI: this comment is wrong! It's actually every 4 seconds!)
t += num;
return !(t % modulus);
@@ -4761,15 +4765,28 @@ void evade_weapon()
// If we're sort of pointing towards it...
if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
float rdot;
float rdot;
float udot;
// Turn hard left or right, depending on which gets out of way quicker.
//SUSHI: Also possibly turn up or down.
rdot = vm_vec_dot(&Pl_objp->orient.vec.rvec, &vec_from_enemy);
udot = vm_vec_dot(&Pl_objp->orient.vec.uvec, &vec_from_enemy);
if ((rdot < -0.5f) || (rdot > 0.5f))
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, -200.0f);
if (The_mission.ai_profile->flags & AIPF_ALLOW_VERTICAL_DODGE && abs(udot) > abs(rdot))
{
if ((udot < -0.5f) || (udot > 0.5f))
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.uvec, -200.0f);
else
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.uvec, 200.0f);
}
else
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, 200.0f);
{
if ((rdot < -0.5f) || (rdot > 0.5f))
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, -200.0f);
else
vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.vec.rvec, 200.0f);
}
turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
}
@@ -5465,7 +5482,7 @@ int ai_fire_primary_weapon(object *objp)
{
ship *shipp = &Ships[objp->instance];
ship_weapon *swp = &shipp->weapons;
ship_info *sip;
ship_info *sip, *enemy_sip;
ai_info *aip;
object *enemy_objp;
@@ -5488,8 +5505,10 @@ int ai_fire_primary_weapon(object *objp)
if (aip->target_objnum != -1){
enemy_objp = &Objects[aip->target_objnum];
enemy_sip = &Ship_info[Ships[enemy_objp->instance].ship_info_index];
} else {
enemy_objp = NULL;
enemy_sip = NULL;
}
if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
@@ -6447,6 +6466,38 @@ void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, flo
aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
}
//Glide attack: we turn on glide to maintain current vector while aiming at the enemy
//The aiming part should already be taken care of.
if (aip->submode == AIS_CHASE_GLIDEATTACK) {
Pl_objp->phys_info.flags |= PF_GLIDING;
accelerate_ship(aip, 0.0f);
return;
}
//Circle Strafe: We try to maintain a constant distance from the target while using sidethrust to move in a circle
//around the target
if (aip->submode == AIS_CHASE_CIRCLESTRAFE) {
accelerate_ship(aip, 0.0f); //Just maintain current distance
//Sidethrust vector is initially based on the velocity vector representing the ship's current sideways motion.
vec2d side_vec;
//TODO: Leaving this hardcoded for now, but it might be good to make it configurable at some point.
int strafeHoldDirAmount = 3;
//Get a random float using some of the more significant chunks of the missiontime as a seed
//This means that we get the same random values for a little bit.
//Using static_rand(shipnum) as a crude hash function to make sure that the seed is different for each ship and direction
//The *2 ensures that y and x stay separate.
side_vec.x = static_randf_range((((Missiontime + static_rand(aip->shipnum)) >> 16) / strafeHoldDirAmount) , -1.0f, 1.0f);
side_vec.y = static_randf_range((((Missiontime + static_rand(aip->shipnum)) >> 16) / strafeHoldDirAmount) * 2, -1.0f, 1.0f);
//Scale it up so that the longest dimension is length 1.0. This ensures we are always getting as much use out of sidethrust as possible.
vm_vec_boxscale(&side_vec, 1.0f);
AI_ci.sideways = side_vec.x;
AI_ci.vertical = side_vec.y;
return;
}
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) {
//nprintf(("AI", "1"));
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
@@ -7017,10 +7068,9 @@ void ai_chase_attack(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, f
} else
new_pos = *predicted_enemy_pos;
if (dist_to_enemy < 250.0f) {
if (dot_from_enemy > 0.7f) {
bank_override = Pl_objp->phys_info.speed;
}
//SUSHI: Don't change bank while circle strafing or glide attacking
if (dist_to_enemy < 250.0f && dot_from_enemy > 0.7f && aip->submode != AIS_CHASE_CIRCLESTRAFE && aip->submode != AIS_CHASE_GLIDEATTACK) {
bank_override = Pl_objp->phys_info.speed;
}
// If enemy more than 500 meters away, all ships flying there will tend to match bank.
@@ -8068,6 +8118,9 @@ void ai_chase()
dot_to_enemy = vm_vec_dot(&Pl_objp->orient.vec.fvec, &predicted_vec_to_enemy);
dot_from_enemy= - vm_vec_dot(&En_objp->orient.vec.fvec, &real_vec_to_enemy);
//Default to glide OFF
Pl_objp->phys_info.flags &= ~PF_GLIDING;
//
// Set turn and acceleration based on submode.
//
@@ -8087,6 +8140,8 @@ void ai_chase()
case SM_ATTACK:
case SM_SUPER_ATTACK:
case SM_ATTACK_FOREVER:
case AIS_CHASE_GLIDEATTACK:
case AIS_CHASE_CIRCLESTRAFE:
if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
return;
@@ -8135,18 +8190,43 @@ void ai_chase()
//
if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
// If a very long time since attacked, attack no matter what!
if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSUIT) ) {
if (Missiontime - aip->last_attack_time > i2f(6)) {
if (Missiontime - aip->last_attack_time > i2f(6)) {
if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSUIT) ) {
aip->submode = SM_SUPER_ATTACK;
aip->submode_start_time = Missiontime;
aip->last_attack_time = Missiontime;
}
}
//SUSHI: Alternate stalemate dection method: if nobody has hit each other for a while
//(and we've been near that whole time), shake things up somehow
//Only do this if stalemate time threshold > 0
if (The_mission.ai_profile->stalemate_time_thresh[Game_skill_level] > 0.0f &&
Missiontime - aip->last_hit_target_time > fl2f(The_mission.ai_profile->stalemate_time_thresh[Game_skill_level]) &&
Missiontime - aip->last_hit_time > fl2f(The_mission.ai_profile->stalemate_time_thresh[Game_skill_level]) &&
aip->time_enemy_near > The_mission.ai_profile->stalemate_time_thresh[Game_skill_level] &&
(dot_to_enemy < 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)))
{
if ((sip->can_glide == true) && (frand() < The_mission.ai_profile->glide_attack_percent[Game_skill_level])) {
//Maybe use glide attack
aip->submode = AIS_CHASE_GLIDEATTACK;
aip->submode_start_time = Missiontime;
aip->last_hit_target_time = Missiontime;
aip->time_enemy_near = 0.0f;
} else if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
//Otherwise, possibly try to get away
aip->submode = SM_GET_AWAY;
aip->submode_start_time = Missiontime;
aip->last_hit_target_time = Missiontime;
aip->time_enemy_near = 0.0f;
}
}
// If a collision is expected, pull out!
// If enemy is pointing away and moving a bit, don't worry about collision detection.
if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
//If we're in circle strafe mode, don't worry about colliding with the target
if (aip->submode != AIS_CHASE_CIRCLESTRAFE && might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
accelerate_ship(aip, -1.0f);
} else {
@@ -8167,11 +8247,17 @@ void ai_chase()
break;
case SM_ATTACK:
// if taraget is stealth and stealth not visible, then enter stealth find mode
// if target is stealth and stealth not visible, then enter stealth find mode
if ( (aip->ai_flags & AIF_STEALTH_PURSUIT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
aip->submode = SM_STEALTH_FIND;
aip->submode_start_time = Missiontime;
aip->submode_parm0 = SM_SF_AHEAD;
} else if (dist_to_enemy < CIRCLE_STRAFE_DIST + En_objp->radius &&
(En_objp->phys_info.speed < max(sip->max_vel.xyz.x, sip->max_vel.xyz.y) * 1.5f) &&
(static_randf((Missiontime + static_rand(aip->shipnum)) >> 19) < The_mission.ai_profile->circle_strafe_percent[Game_skill_level])) {
aip->submode = AIS_CHASE_CIRCLESTRAFE;
aip->submode_start_time = Missiontime;
aip->last_attack_time = Missiontime;
} else if (ai_near_full_strength(Pl_objp) && (Missiontime - aip->last_hit_target_time > i2f(3)) && (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.5f)) {
aip->submode = SM_SUPER_ATTACK;
aip->submode_start_time = Missiontime;
@@ -8226,12 +8312,14 @@ void ai_chase()
if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
aip->submode = SM_EVADE_BRAKE;
aip->submode_start_time = Missiontime;
} else if ((Pl_objp->phys_info.speed >= Pl_objp->phys_info.max_vel.xyz.z / 2.0f) && (sip->can_glide == true) && (frand() < The_mission.ai_profile->glide_attack_percent[Game_skill_level])) {
aip->last_attack_time = Missiontime;
aip->submode = AIS_CHASE_GLIDEATTACK;
} else {
aip->last_attack_time = Missiontime;
aip->submode = SM_ATTACK;
aip->submode_start_time = Missiontime;
}
aip->submode_start_time = Missiontime;
}
break;
@@ -8276,6 +8364,12 @@ void ai_chase()
aip->submode = SM_STEALTH_FIND;
aip->submode_start_time = Missiontime;
aip->submode_parm0 = SM_SF_AHEAD;
} else if (dist_to_enemy < CIRCLE_STRAFE_DIST + En_objp->radius &&
(En_objp->phys_info.speed < max(sip->max_vel.xyz.x, sip->max_vel.xyz.y) * 1.5f) &&
(static_randf((Missiontime + static_rand(aip->shipnum)) >> 19) < The_mission.ai_profile->circle_strafe_percent[Game_skill_level])) {
aip->submode = AIS_CHASE_CIRCLESTRAFE;
aip->submode_start_time = Missiontime;
aip->last_attack_time = Missiontime;
} 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) )) {
aip->ai_flags &= ~AIF_ATTACK_SLOWLY; // Just in case, clear here.
@@ -8342,8 +8436,15 @@ void ai_chase()
rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f; // Some value in 200..500
if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
aip->ai_flags |= AIF_ATTACK_SLOWLY;
aip->submode = SM_ATTACK;
//Sometimes use a glide attack instead (if we can)
if ((sip->can_glide == true) && (frand() < The_mission.ai_profile->glide_attack_percent[Game_skill_level])) {
aip->submode = AIS_CHASE_GLIDEATTACK;
}
else {
aip->ai_flags |= AIF_ATTACK_SLOWLY;
aip->submode = SM_ATTACK;
}
aip->submode_start_time = Missiontime;
aip->time_enemy_in_range = 2.0f; // Cheat. Presumably if they were running away from you, they were monitoring you!
aip->last_attack_time = Missiontime;
@@ -8400,6 +8501,25 @@ void ai_chase()
case SM_ATTACK_FOREVER: // Engines blown, just attack.
break;
case AIS_CHASE_GLIDEATTACK:
//Glide attack lasts at least as long as it takes to turn around and fire for a couple seconds.
if (Missiontime - aip->submode_start_time > i2f((int)(sip->rotation_time.xyz.x) + 4 + static_rand_range(Missiontime >> 19, 0, 3))) {
aip->submode = SM_ATTACK;
aip->submode_start_time = Missiontime;
aip->last_attack_time = Missiontime;
}
aip->last_attack_time = Missiontime;
break;
case AIS_CHASE_CIRCLESTRAFE:
if ((dist_to_enemy > CIRCLE_STRAFE_DIST + En_objp->radius) || (Missiontime - aip->submode_start_time > i2f(4))) {
aip->submode = SM_ATTACK;
aip->submode_start_time = Missiontime;
aip->last_attack_time = Missiontime;
}
aip->last_attack_time = Missiontime;
break;
default:
//Int3();
aip->submode = SM_ATTACK;
@@ -8407,6 +8527,25 @@ void ai_chase()
aip->last_attack_time = Missiontime;
}
//Update time enemy near
//Ignore for stealth ships that can't be seen
//If we're trying to get away, recent counter
//Only do this if stalemate distance threshold > 0
if (The_mission.ai_profile->stalemate_dist_thresh[Game_skill_level] > 0.0f &&
dist_to_enemy < The_mission.ai_profile->stalemate_dist_thresh[Game_skill_level] &&
aip->submode != SM_GET_AWAY && aip->submode != AIS_CHASE_GLIDEATTACK && aip->submode != SM_FLY_AWAY &&
(!(aip->ai_flags & AIF_STEALTH_PURSUIT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE)))
{
aip->time_enemy_near += flFrametime;
}
else
{
aip->time_enemy_near *= (1.0f - flFrametime);
if (aip->time_enemy_near < 0.0f)
aip->time_enemy_near = 0.0f;
}
//
// Maybe fire primary weapon and update time_enemy_in_range
//
@@ -14019,6 +14158,7 @@ void init_ai_object(int objnum)
aip->prev_goal_point = near_vec;
aip->goal_point = near_vec;
aip->time_enemy_in_range = 0.0f;
aip->time_enemy_near = 0.0f;
aip->last_attack_time = 0;
aip->last_hit_time = 0;
aip->last_hit_quadrant = 0;
@@ -14062,6 +14202,7 @@ void init_ai_object(int objnum)
aip->last_predicted_enemy_pos.xyz.x = 0.0f; // Says this value needs to be recomputed!
aip->time_enemy_in_range = 0.0f;
aip->time_enemy_near = 0.0f;
aip->resume_goal_time = -1; // Say there is no goal to resume.
@@ -14079,6 +14220,7 @@ void init_ai_object(int objnum)
aip->lead_scale = 0.0f;
aip->last_hit_target_time = Missiontime;
aip->last_hit_time = Missiontime;
aip->nearest_locked_object = -1;
aip->nearest_locked_distance = 99999.0f;
@@ -14574,8 +14716,24 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hitpos, int shield_
shipp = &Ships[objp_ship->instance];
aip = &Ai_info[shipp->ai_index];
if (objp_ship->flags & OF_PLAYER_SHIP)
if (objp_ship->flags & OF_PLAYER_SHIP) {
//SUSHI: So that hitting a player ship actually resets the last_hit_target_time counter for whoever hit the player.
//This is all copypasted from code below
if (hit_objp->type == OBJ_WEAPON) {
hitter_objnum = hit_objp->parent;
Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
objp_hitter = &Objects[hitter_objnum];
} else if (hit_objp->type == OBJ_SHIP) {
objp_hitter = hit_objp;
} else {
Int3(); // Should never happen.
return;
}
Assert(objp_hitter != NULL);
hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
hitter_aip->last_hit_target_time = Missiontime;
return;
}
if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
return;
+23
View File
@@ -752,6 +752,26 @@ $Hostile AI Secondary Fire Delay Scale: 1.4, 1.2, 1.0, 0.8, 0.6 \n\
;; factor applied to time it takes for enemy ships to turn \n\
$AI Turn Time Scale: 3, 2.2, 1.6, 1.3, 1 \n\
\n\
;; Percentage of the time where AI ships will use the glide attack \n\
;; when it is an option. \n\
$Glide Attack Percent: 0, 0, 0, 0, 0 \n\
\n\
;; Percentage of the time where AI ships will use circle strafe \n\
;; when it is an option. \n\
$Circle Strafe Percent: 0, 0, 0, 0, 0 \n\
\n\
;; Percentage of the time where AI ships will use glide to strafe \n\
;; capital ships when it is an option. \n\
$Glide Strafe Percent: 0, 0, 0, 0, 0 \n\
\n\
;; The amount of time required for the AI to detect \n\
;; (and try to break) dogfight stalemate. \n\
$Stalemate Time Threshold: 0, 0, 0, 0, 0 \n\
\n\
;; The maximum distance the AI and target must be within \n\
;; for a dogfight stalemate \n\
$Stalemate Distance Threshold: 0, 0, 0, 0, 0 \n\
\n\
;; factor applied to the speed at which the player's shields recharge \n\
$Player Shield Recharge Scale: 4, 2, 1.5, 1.25, 1 \n\
\n\
@@ -904,5 +924,8 @@ $allow turrets target weapons freely: NO \n\
;; ignore hard coded limits (with 'fire_down_normals' flag) \n\
$use only single fov for turrets: NO \n\
\n\
;; allow AI ships to dodge weapons vertically as well as horizontally \n\
$allow vertical dodge: NO \n\
\n\
#End \n\
";
+3 -2
View File
@@ -111,12 +111,13 @@ typedef struct vec3d {
typedef struct vectora {
float xyz[3];
} vectora;
*/
typedef struct vec2d {
float i,j;
float x, y;
} vec2d;
/*
// Used for some 2d primitives, like gr_poly
typedef struct vert2df {
float x, y;
+9
View File
@@ -55,6 +55,15 @@ float static_randf(int num)
return (a & 0xffff) / 65536.0f;
}
// Note: min and max are inclusive
int static_rand_range(int num, int min, int max)
{
int rval = static_rand(num);
rval = (rval % (max - min + 1)) + min;
return rval;
}
float static_randf_range(int num, float min, float max)
{
float rval;
+1
View File
@@ -22,6 +22,7 @@ extern void init_semirand();
extern int static_rand(int num);
extern float static_randf(int num);
extern void static_randvec(int num, vec3d *vp);
extern int static_rand_range(int num, int min, int max);
extern float static_randf_range(int num, float min, float max);
void static_rand_cone(int num, vec3d *out, vec3d *in, float max_angle, matrix *orient = NULL);
+10
View File
@@ -2793,3 +2793,13 @@ void vm_vec_dist_squared_to_line(vec3d *p, vec3d *l0, vec3d *l1, vec3d *nearest,
// get the distance
*dist_squared = vm_vec_dist_squared(nearest, p);
}
//SUSHI: 2D vector "box" scaling
//Scales the vector in-place so that the longest dimension = scale
void vm_vec_boxscale(vec2d *vec, float scale)
{
float ratio = 1.0f / MAX(abs(vec->x), abs(vec->y));
vec->x *= ratio;
vec->y *= ratio;
}
+3
View File
@@ -507,6 +507,9 @@ void vm_vec_dist_squared_to_line(vec3d *p, vec3d *l0, vec3d *l1, vec3d *nearest,
void vm_vert2vec(vertex *vert, vec3d *vec);
void vm_vec2vert(vec3d *vec, vertex *vert);
//SUSHI: 2D vector "box" scaling
void vm_vec_boxscale(vec2d *vec, float scale);
#endif