mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
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:
1 parent
915a9270c9
commit
9246a2f74d
11 files changed
+419
-33
No files matched your search
+8
-1
@@ -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.
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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\
|
||||
";
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user