mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
1564 lines
40 KiB
C++
1564 lines
40 KiB
C++
// Autopilot.cpp
|
|
// Derek Meek
|
|
// 4-30-2004
|
|
|
|
|
|
|
|
|
|
#include "autopilot/autopilot.h"
|
|
#include "ai/ai.h"
|
|
#include "ai/aigoals.h"
|
|
#include "ship/ship.h"
|
|
#include "object/object.h"
|
|
#include "object/objcollide.h"
|
|
#include "object/waypoint.h"
|
|
#include "parse/sexp.h"
|
|
#include "freespace2/freespace.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "iff_defs/iff_defs.h"
|
|
#include "sound/audiostr.h"
|
|
#include "mission/missiontraining.h"
|
|
#include "mission/missionmessage.h"
|
|
#include "io/timer.h"
|
|
#include "gamesnd/eventmusic.h"
|
|
#include "cfile/cfile.h"
|
|
#include "parse/parselo.h"
|
|
#include "globalincs/def_files.h"
|
|
#include "localization/localize.h"
|
|
#include "camera/camera.h"
|
|
#include "asteroid/asteroid.h"
|
|
#include <map>
|
|
|
|
// Extern functions/variables
|
|
extern int Player_use_ai;
|
|
extern int get_wing_index(object *objp, int wingnum);
|
|
extern object * get_wing_leader(int wingnum);
|
|
extern int Cmdline_autopilot_interruptable;
|
|
|
|
|
|
// Module variables
|
|
bool AutoPilotEngaged;
|
|
bool UseCutsceneBars;
|
|
int CurrentNav;
|
|
float ramp_bias;
|
|
NavPoint Navs[MAX_NAVPOINTS];
|
|
NavMessage NavMsgs[NP_NUM_MESSAGES];
|
|
int audio_handle;
|
|
int NavLinkDistance;
|
|
// time offsets for autonav events
|
|
int LockAPConv;
|
|
int EndAPCinematic;
|
|
int MoveCamera;
|
|
int camMovingTime;
|
|
//float CameraSpeed;
|
|
bool CinematicStarted, CameraMoving;
|
|
vec3d cameraPos, cameraTarget;
|
|
std::map<int,int> autopilot_wings;
|
|
|
|
// used for ramping time compression;
|
|
int start_dist;
|
|
|
|
// ********************************************************************************************
|
|
|
|
void autopilot_ai_waypoint_goal_fixup(ai_goal* aigp)
|
|
{
|
|
// this function sets wp_index properly;
|
|
for (int i = 0; i < Num_waypoint_lists; i++)
|
|
{
|
|
if (!stricmp(aigp->ship_name, Waypoint_lists[i].name))
|
|
{
|
|
aigp->wp_index = i;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ********************************************************************************************
|
|
bool Sel_NextNav()
|
|
{
|
|
if (AutoPilotEngaged)
|
|
return false;
|
|
|
|
int i;
|
|
if (CurrentNav == -1)
|
|
{
|
|
for (i = 0; i < MAX_NAVPOINTS; i++)
|
|
{
|
|
if (Navs[i].flags & NP_VALIDTYPE && !(Navs[i].flags & NP_NOSELECT))
|
|
{
|
|
CurrentNav=i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = CurrentNav+1; i < MAX_NAVPOINTS+CurrentNav; i++)
|
|
{
|
|
if (Navs[i%MAX_NAVPOINTS].flags & NP_VALIDTYPE && !(Navs[i%MAX_NAVPOINTS].flags & NP_NOSELECT))
|
|
{
|
|
if (i != CurrentNav)
|
|
{
|
|
CurrentNav=i%MAX_NAVPOINTS;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// ********************************************************************************************
|
|
vec3d *NavPoint::GetPosition()
|
|
{
|
|
if (flags & NP_WAYPOINT)
|
|
{
|
|
return &((waypoint_list*) target_obj)->waypoints[waypoint_num-1];
|
|
}
|
|
else
|
|
{
|
|
return &Objects[((ship*) target_obj)->objnum].pos;
|
|
}
|
|
}
|
|
|
|
char* NavPoint::GetInteralName()
|
|
{
|
|
char *NavName;
|
|
char strtmp[33];
|
|
|
|
if (flags & NP_WAYPOINT)
|
|
{
|
|
NavName = new char[strlen(((waypoint_list*)target_obj)->name)+5];
|
|
memset(NavName, 0, strlen(((waypoint_list*)target_obj)->name)+5);
|
|
strcpy(NavName, ((waypoint_list*)target_obj)->name);
|
|
|
|
strcat(NavName, ":");
|
|
sprintf(strtmp, "%d", waypoint_num);
|
|
strcat(NavName, strtmp);
|
|
}
|
|
else
|
|
{
|
|
NavName = new char[strlen(((ship*)target_obj)->ship_name)+1];
|
|
memset(NavName, 0, strlen(((ship*)target_obj)->ship_name)+1);
|
|
strcpy(NavName, ((ship*)target_obj)->ship_name);
|
|
}
|
|
|
|
return NavName;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Tell us if autopilot is allowed
|
|
// This needs:
|
|
// * Nav point selected
|
|
// * No enemies within X distance
|
|
// * Destination > 1,000 meters away
|
|
bool CanAutopilot(bool send_msg)
|
|
{
|
|
if (CurrentNav == -1)
|
|
{
|
|
if (send_msg)
|
|
send_autopilot_msgID(NP_MSG_FAIL_NOSEL);
|
|
return false;
|
|
}
|
|
|
|
if (object_get_gliding(Player_obj))
|
|
{
|
|
if (send_msg)
|
|
send_autopilot_msgID(NP_MSG_FAIL_GLIDING);
|
|
return false;
|
|
}
|
|
// You cannot autopilot if you're within 1000 meters of your destination nav point
|
|
if (vm_vec_dist_quick(&Player_obj->pos, Navs[CurrentNav].GetPosition()) < 1000)
|
|
{
|
|
if (send_msg)
|
|
send_autopilot_msgID(NP_MSG_FAIL_TOCLOSE);
|
|
return false;
|
|
}
|
|
|
|
// see if any hostiles are nearby
|
|
for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
|
|
{
|
|
object *other_objp = &Objects[so->objnum];
|
|
|
|
// attacks player?
|
|
if (iff_x_attacks_y(obj_team(other_objp), obj_team(Player_obj))
|
|
&& !(Ship_info[Ships[other_objp->instance].ship_info_index].flags & SIF_CARGO))
|
|
{
|
|
// Cannot autopilot if enemy within 5,000 meters
|
|
if (vm_vec_dist_quick(&Player_obj->pos, &other_objp->pos) < 5000)
|
|
{
|
|
if (send_msg)
|
|
send_autopilot_msgID(NP_MSG_FAIL_HOSTILES);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//check for asteroids
|
|
for (int n=0; n<MAX_ASTEROIDS; n++)
|
|
{
|
|
// asteroid
|
|
if (Asteroids[n].flags & AF_USED)
|
|
{
|
|
// Cannot autopilot if asteroid within 1,000 meters
|
|
if (vm_vec_dist_quick(&Player_obj->pos, &Objects[Asteroids[n].objnum].pos) < 1000)
|
|
{
|
|
if (send_msg)
|
|
send_autopilot_msgID(NP_MSG_FAIL_HAZARD);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Tell us if autopilot is allowed at a certain position (only performs checks based on that position)
|
|
// This needs:
|
|
// * Nav point selected
|
|
// * No enemies within X distance
|
|
// * Destination > 1,000 meters away
|
|
bool CanAutopilotPos(vec3d targetPos)
|
|
{
|
|
// You cannot autopilot if you're within 1000 meters of your destination nav point
|
|
if (vm_vec_dist_quick(&targetPos, Navs[CurrentNav].GetPosition()) < 1000)
|
|
return false;
|
|
// see if any hostiles are nearby
|
|
for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
|
|
{
|
|
object *other_objp = &Objects[so->objnum];
|
|
// attacks player?
|
|
if (iff_x_attacks_y(obj_team(other_objp), obj_team(Player_obj))
|
|
&& !(Ship_info[Ships[other_objp->instance].ship_info_index].flags & SIF_CARGO)) // ignore cargo
|
|
{
|
|
// Cannot autopilot if enemy within 5,000 meters
|
|
if (vm_vec_dist_quick(&targetPos, &other_objp->pos) < 5000)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//check for asteroids
|
|
for (int n=0; n<MAX_ASTEROIDS; n++)
|
|
{
|
|
// asteroid
|
|
if (Asteroids[n].flags & AF_USED)
|
|
{
|
|
// Cannot autopilot if asteroid within 1,000 meters
|
|
if (vm_vec_dist_quick(&targetPos, &Objects[Asteroids[n].objnum].pos) < 1000)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Engages autopilot
|
|
// This does:
|
|
// * Control switched from player to AI
|
|
// * Time compression to 32x
|
|
// * Lock time compression -WMC
|
|
// * Tell AI to fly to targetted Nav Point (for all nav-status wings/ships)
|
|
// * Sets max waypoint speed to the best-speed of the slowest ship tagged
|
|
void StartAutopilot()
|
|
{
|
|
if (!CanAutopilot())
|
|
return;
|
|
|
|
AutoPilotEngaged = true;
|
|
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
LockAPConv = timestamp(); // lock convergence instantly
|
|
else
|
|
LockAPConv = timestamp(3000); // 3 seconds before we lock convergence
|
|
Player_use_ai = 1;
|
|
set_time_compression(1);
|
|
lock_time_compression(true);
|
|
|
|
// determine speed cap
|
|
int i,j, wcount=1, tc_factor=1;
|
|
float speed_cap = 1000000.0; // 1m is a safe starting point
|
|
float radius = Player_obj->radius, distance = 0.0f, ftemp;
|
|
bool capshipPresent = false;
|
|
int capship_counts[3]; // three size classes
|
|
capship_counts[0] = 0;
|
|
capship_counts[1] = 0;
|
|
capship_counts[2] = 0;
|
|
|
|
int capship_placed[3]; // three size classes
|
|
capship_placed[0] = 0;
|
|
capship_placed[1] = 0;
|
|
capship_placed[2] = 0;
|
|
|
|
float capship_spreads[3];
|
|
capship_spreads[0] = 0.0f;
|
|
capship_spreads[1] = 0.0f;
|
|
capship_spreads[2] = 0.0f;
|
|
|
|
SCP_vector<int> capIndexes;
|
|
|
|
// empty the autopilot wings map
|
|
autopilot_wings.clear();
|
|
|
|
// vars for usage w/ cinematic
|
|
vec3d pos, norm1, perp, tpos, rpos = Player_obj->pos, zero;
|
|
memset(&zero, 0, sizeof(vec3d));
|
|
|
|
|
|
// instantly turn player toward tpos
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
{
|
|
vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos);
|
|
vm_vector_2_matrix(&Player_obj->orient, &norm1, NULL, NULL);
|
|
}
|
|
|
|
for (i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1 &&
|
|
(Ships[i].flags2 & SF2_NAVPOINT_CARRY ||
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)
|
|
)
|
|
)
|
|
{
|
|
if (speed_cap > vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel))
|
|
speed_cap = vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel);
|
|
}
|
|
}
|
|
|
|
|
|
// damp speed_cap to 90% of actual -- to make sure ships stay in formation
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
speed_cap = 0.90f * speed_cap;
|
|
ramp_bias = speed_cap/50.0f;
|
|
|
|
// assign ship goals
|
|
// when assigning goals to individual ships only do so if Ships[shipnum].wingnum != -1
|
|
// we will assign wing goals below
|
|
|
|
for (i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1 &&
|
|
(Ships[i].flags2 & SF2_NAVPOINT_CARRY ||
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)
|
|
)
|
|
)
|
|
{
|
|
// do we have capital ships in the area?
|
|
if (Ship_info[Ships[i].ship_info_index].flags
|
|
& ( SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER | SIF_FREIGHTER | SIF_TRANSPORT))
|
|
{
|
|
capshipPresent = true;
|
|
|
|
capIndexes.resize(capIndexes.size()+1);
|
|
capIndexes[capIndexes.size()-1] = i;
|
|
// ok.. what size class
|
|
|
|
if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP))
|
|
{
|
|
capship_counts[0]++;
|
|
if (capship_spreads[0] < Objects[Ships[i].objnum].radius)
|
|
capship_spreads[0] = Objects[Ships[i].objnum].radius;
|
|
}
|
|
else if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CORVETTE))
|
|
{
|
|
capship_counts[1]++;
|
|
if (capship_spreads[1] < Objects[Ships[i].objnum].radius)
|
|
capship_spreads[1] = Objects[Ships[i].objnum].radius;
|
|
}
|
|
else
|
|
{
|
|
capship_counts[2]++;
|
|
if (capship_spreads[2] < Objects[Ships[i].objnum].radius)
|
|
capship_spreads[2] = Objects[Ships[i].objnum].radius;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// check for bigger radius for usage later
|
|
/*if (!vm_vec_cmp(&rpos, &Player_obj->pos))
|
|
// want to make sure rpos isn't player pos - we can worry about it being largest object's later
|
|
{
|
|
rpos = Objects[Ships[i].objnum].pos;
|
|
}*/
|
|
|
|
if (Objects[Ships[i].objnum].radius > radius)
|
|
{
|
|
rpos = Objects[Ships[i].objnum].pos;
|
|
radius = Objects[Ships[i].objnum].radius;
|
|
}
|
|
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
{// instantly turn the ship to match the direction player is looking
|
|
//vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos);
|
|
vm_vector_2_matrix(&Objects[Ships[i].objnum].orient, &norm1, NULL, NULL);
|
|
}
|
|
|
|
// snap wings into formation them into formation
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS && // only if using cinematics
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) // only if in a wing
|
|
&& Player_obj != &Objects[Ships[i].objnum]) //only if not player object
|
|
{
|
|
ai_info *aip = &Ai_info[Ships[i].ai_index];
|
|
int wingnum = aip->wing, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum);
|
|
vec3d goal_point;
|
|
object *leader_objp = get_wing_leader(wingnum);
|
|
|
|
if (leader_objp != &Objects[Ships[i].objnum])
|
|
{
|
|
// not leader.. get our position relative to leader
|
|
get_absolute_wing_pos_autopilot(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
|
|
}
|
|
else
|
|
{
|
|
ai_clear_wing_goals(wingnum);
|
|
j = 1+int( (float)floor(double(wcount-1)/2.0) );
|
|
switch (wcount % 2)
|
|
{
|
|
case 1: // back-left
|
|
vm_vec_add(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
//vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec);
|
|
vm_vec_normalize(&perp);
|
|
vm_vec_scale(&perp, -166.0f*j); // 166m is supposedly the optimal range according to tolwyn
|
|
vm_vec_add(&goal_point, &Player_obj->pos, &perp);
|
|
break;
|
|
|
|
default: //back-right
|
|
case 0:
|
|
vm_vec_add(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
//vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec);
|
|
vm_vec_normalize(&perp);
|
|
vm_vec_scale(&perp, 166.0f*j);
|
|
vm_vec_add(&goal_point, &Player_obj->pos, &perp);
|
|
break;
|
|
}
|
|
autopilot_wings[wingnum] = wcount;
|
|
wcount++;
|
|
}
|
|
Objects[Ships[i].objnum].pos = goal_point;
|
|
if (vm_vec_dist_quick(&Player_obj->pos, &Objects[Ships[i].objnum].pos) > distance)
|
|
{
|
|
distance = vm_vec_dist_quick(&Player_obj->pos, &Objects[Ships[i].objnum].pos);
|
|
}
|
|
}
|
|
// lock primary and secondary weapons
|
|
//Ships[i].flags2 |= (SF2_PRIMARIES_LOCKED | SF2_SECONDARIES_LOCKED);
|
|
|
|
// clear the ship goals and cap the waypoint speed
|
|
ai_clear_ship_goals(&Ai_info[Ships[i].ai_index]);
|
|
Ai_info[Ships[i].ai_index].waypoint_speed_cap = (int)speed_cap;
|
|
|
|
|
|
// if they're not part of a wing set their goal
|
|
if (Ships[i].wingnum == -1 || The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
{
|
|
if (Navs[CurrentNav].flags & NP_WAYPOINT)
|
|
{
|
|
ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->name, &Ai_info[Ships[i].ai_index] );
|
|
//fixup has to wait until after wing goals
|
|
}
|
|
else
|
|
{
|
|
ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, &Ai_info[Ships[i].ai_index] );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// assign wing goals
|
|
if (!(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS))
|
|
{
|
|
for (i = 0; i < MAX_WINGS; i++)
|
|
{
|
|
if (Wings[i].flags & WF_NAV_CARRY )
|
|
{
|
|
//ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip );
|
|
|
|
//ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum );
|
|
//ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum );
|
|
//ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
|
|
|
|
ai_clear_wing_goals( i );
|
|
if (Navs[CurrentNav].flags & NP_WAYPOINT)
|
|
{
|
|
|
|
ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->name, i );
|
|
|
|
// "fix up" the goal
|
|
for (j = 0; j < MAX_AI_GOALS; j++)
|
|
{
|
|
if (Wings[i].ai_goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE ||
|
|
Wings[i].ai_goals[j].ai_mode == AIM_WAYPOINTS )
|
|
{
|
|
autopilot_ai_waypoint_goal_fixup(&(Wings[i].ai_goals[j]));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, i );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fixup has to go down here because ships are assigned goals during wing goals as well
|
|
for (i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1)
|
|
{
|
|
if (Ships[i].flags2 & SF2_NAVPOINT_CARRY ||
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY))
|
|
for (j = 0; j < MAX_AI_GOALS; j++)
|
|
{
|
|
if (Ai_info[Ships[i].ai_index].goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE ||
|
|
Ai_info[Ships[i].ai_index].goals[j].ai_mode == AIM_WAYPOINTS)
|
|
{
|
|
autopilot_ai_waypoint_goal_fixup( &(Ai_info[Ships[i].ai_index].goals[j]) );
|
|
|
|
|
|
// formation fixup
|
|
//ai_form_on_wing(&Objects[Ships[i].objnum], &Objects[Player_ship->objnum]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
start_dist = DistanceTo(CurrentNav);
|
|
|
|
// ----------------------------- setup cinematic -----------------------------
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
{
|
|
if (capshipPresent)
|
|
{
|
|
// position capships
|
|
|
|
vec3d right, front, up, offset;
|
|
for (i = 0; i < (int)capIndexes.size(); i++)
|
|
{
|
|
vm_vec_add(&right, &Player_obj->orient.vec.rvec, &zero);
|
|
vm_vec_add(&front, &Player_obj->orient.vec.fvec, &zero);
|
|
vm_vec_add(&up, &Player_obj->orient.vec.uvec, &zero);
|
|
vm_vec_add(&offset, &zero, &zero);
|
|
if (Ship_info[Ships[capIndexes[i]].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP))
|
|
{
|
|
//0 - below - three lines of position
|
|
|
|
// front/back to zero
|
|
vm_vec_add(&front, &zero, &zero);
|
|
|
|
// position below
|
|
vm_vec_scale(&up, capship_spreads[0]); // scale the up vector by the radius of the largest ship in this formation part
|
|
|
|
|
|
switch (capship_placed[0] % 3)
|
|
{
|
|
case 1: // right
|
|
vm_vec_scale(&right, capship_spreads[0]);
|
|
break;
|
|
|
|
case 2: // left
|
|
vm_vec_scale(&right, -capship_spreads[0]);
|
|
break;
|
|
|
|
default: // straight
|
|
case 0:
|
|
vm_vec_add(&right, &zero, &zero);
|
|
vm_vec_scale(&up, 1.5); // add an extra half-radius
|
|
break;
|
|
}
|
|
|
|
// scale by row
|
|
vm_vec_scale(&right, (1+((float)floor((float)capship_placed[0]/3))));
|
|
vm_vec_scale(&up, -(1+((float)floor((float)capship_placed[0]/3))));
|
|
|
|
capship_placed[0]++;
|
|
}
|
|
else if (Ship_info[Ships[capIndexes[i]].ship_info_index].flags & SIF_CORVETTE)
|
|
{
|
|
//1 above - 3 lines of position
|
|
// front/back to zero
|
|
vm_vec_add(&front, &zero, &zero);
|
|
|
|
// position below
|
|
vm_vec_scale(&up, capship_spreads[1]); // scale the up vector by the radius of the largest ship in this formation part
|
|
|
|
|
|
switch (capship_placed[1] % 3)
|
|
{
|
|
case 1: // right
|
|
vm_vec_scale(&right, capship_spreads[1]);
|
|
break;
|
|
|
|
case 2: // left
|
|
vm_vec_scale(&right, -capship_spreads[1]);
|
|
break;
|
|
|
|
default: // straight
|
|
case 0:
|
|
vm_vec_add(&right, &zero, &zero);
|
|
vm_vec_scale(&up, 1.5); // add an extra half-radius
|
|
break;
|
|
}
|
|
|
|
// scale by row
|
|
vm_vec_scale(&right, (1+((float)floor((float)capship_placed[1]/3))));
|
|
vm_vec_scale(&up, (1+((float)floor((float)capship_placed[1]/3))));
|
|
|
|
// move ourselves up and out of the way of the smaller ships
|
|
vm_vec_add(&perp, &Player_obj->orient.vec.uvec, &zero);
|
|
vm_vec_scale(&perp, capship_spreads[2]);
|
|
vm_vec_add(&up, &up, &perp);
|
|
|
|
capship_placed[1]++;
|
|
}
|
|
else
|
|
{
|
|
//2 either side - 6 lines of position (right (dir, front, back), left (dir, front, back) )
|
|
// placing pattern: right, left, front right, front left, rear right, rear left
|
|
|
|
// up/down to zero
|
|
vm_vec_add(&up, &zero, &zero);
|
|
|
|
|
|
switch (capship_placed[2] % 6)
|
|
{
|
|
case 5: // rear left
|
|
vm_vec_scale(&right, -capship_spreads[2]);
|
|
vm_vec_scale(&front, -capship_spreads[2]);
|
|
break;
|
|
|
|
case 4: // rear right
|
|
vm_vec_scale(&right, capship_spreads[2]);
|
|
vm_vec_scale(&front, -capship_spreads[2]);
|
|
break;
|
|
|
|
case 3: // front left
|
|
vm_vec_scale(&right, -capship_spreads[2]);
|
|
vm_vec_scale(&front, capship_spreads[2]);
|
|
break;
|
|
|
|
case 2: // front right
|
|
vm_vec_scale(&right, capship_spreads[2]);
|
|
vm_vec_scale(&front, capship_spreads[2]);
|
|
break;
|
|
|
|
case 1: // straight left
|
|
vm_vec_scale(&right, 1.5);
|
|
vm_vec_scale(&right, -capship_spreads[2]);
|
|
vm_vec_add(&front, &zero, &zero);
|
|
break;
|
|
|
|
default: // straight right
|
|
case 0:
|
|
vm_vec_scale(&right, 1.5);
|
|
vm_vec_scale(&right, capship_spreads[2]);
|
|
vm_vec_add(&front, &zero, &zero);
|
|
break;
|
|
}
|
|
// these ships seem to pack a little too tightly
|
|
vm_vec_scale(&right, 2*(1+((float)floor((float)capship_placed[2]/3))));
|
|
vm_vec_scale(&front, 2*(1+((float)floor((float)capship_placed[2]/3))));
|
|
|
|
// move "out" by 166*(wcount-1) so we don't bump into fighters
|
|
vm_vec_add(&perp, &Player_obj->orient.vec.rvec, &zero);
|
|
vm_vec_scale(&perp, 166.0f*float(wcount-1));
|
|
if ( (capship_placed[2] % 2) == 0)
|
|
vm_vec_add(&right, &right, &perp);
|
|
else
|
|
vm_vec_sub(&right, &right, &perp);
|
|
|
|
capship_placed[2]++;
|
|
}
|
|
|
|
// integrate the up/down componant
|
|
vm_vec_add(&offset, &offset, &up);
|
|
|
|
//integrate the left/right componant
|
|
vm_vec_add(&offset, &offset, &right);
|
|
|
|
//integrate the left/right componant
|
|
vm_vec_add(&offset, &offset, &front);
|
|
|
|
// global scale the position by 50%
|
|
//vm_vec_scale(&offset, 1.5);
|
|
|
|
vm_vec_add(&Objects[Ships[capIndexes[i]].objnum].pos, &Player_obj->pos, &offset);
|
|
|
|
if (vm_vec_dist_quick(&Player_obj->pos, &Objects[Ships[i].objnum].pos) > distance)
|
|
{
|
|
distance = vm_vec_dist_quick(&Player_obj->pos, &Objects[Ships[i].objnum].pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
ftemp = floor(Player_obj->phys_info.max_vel.xyz.z/speed_cap);
|
|
if (ftemp >= 2.0f && ftemp < 4.0f)
|
|
tc_factor = 2;
|
|
else if (ftemp >= 4.0f && ftemp < 8.0f)
|
|
tc_factor = 4;
|
|
else if (ftemp >= 8.0f)
|
|
tc_factor = 8;
|
|
|
|
|
|
|
|
tpos = *Navs[CurrentNav].GetPosition();
|
|
// determine distance toward nav at which camera will be
|
|
vm_vec_sub(&pos, &tpos, &Player_obj->pos);
|
|
vm_vec_normalize(&pos); // pos is now a unit vector in the direction we will be moving the camera
|
|
//norm1 = pos;
|
|
vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead)
|
|
vm_vec_add(&pos, &pos, &Player_obj->pos); // pos is now 5*speed cap in front of player ship
|
|
|
|
switch (myrand()%24)
|
|
// 8 different ways of getting perp points
|
|
// 4 of which will not be used when capships are present (anything below, or straight above)
|
|
{
|
|
|
|
case 1: // down
|
|
case 9:
|
|
case 16:
|
|
if (capship_placed[0] == 0)
|
|
vm_vec_sub(&perp, &zero, &Player_obj->orient.vec.uvec);
|
|
else
|
|
{ // become up-left
|
|
vm_vec_add(&perp, &zero, &Player_obj->orient.vec.uvec);
|
|
vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.rvec);
|
|
}
|
|
break;
|
|
|
|
case 2: // up
|
|
case 10:
|
|
case 23:
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
if (capshipPresent) // become up-right
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.rvec);
|
|
break;
|
|
|
|
case 3: // left
|
|
case 11:
|
|
case 22:
|
|
vm_vec_sub(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
break;
|
|
|
|
case 4: // up-left
|
|
case 12:
|
|
case 21:
|
|
vm_vec_sub(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
break;
|
|
|
|
case 5: // up-right
|
|
case 13:
|
|
case 20:
|
|
vm_vec_add(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
break;
|
|
|
|
case 6: // down-left
|
|
case 14:
|
|
case 19:
|
|
vm_vec_sub(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
if (capship_placed[0] < 2)
|
|
vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
else
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
break;
|
|
|
|
case 7: // down-right
|
|
case 15:
|
|
case 18:
|
|
vm_vec_add(&perp, &zero, &Player_obj->orient.vec.rvec);
|
|
if (capship_placed[0] < 1)
|
|
vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
else
|
|
vm_vec_add(&perp, &perp, &Player_obj->orient.vec.uvec);
|
|
break;
|
|
|
|
default:
|
|
case 0: // right
|
|
case 8:
|
|
case 17:
|
|
perp = Player_obj->orient.vec.rvec;
|
|
break;
|
|
|
|
}
|
|
vm_vec_normalize(&perp);
|
|
//vm_vec_scale(&perp, 2*radius+distance);
|
|
|
|
vm_vec_scale(&perp, Player_obj->radius+radius);
|
|
|
|
// randomly scale up/down by up to 20%
|
|
j = 20-myrand()%40; // [-20,20]
|
|
|
|
vm_vec_scale(&perp, 1.0f+(float(j)/100.0f));
|
|
vm_vec_add(&cameraPos, &pos, &perp);
|
|
|
|
if (capshipPresent)
|
|
{
|
|
vm_vec_normalize(&perp);
|
|
|
|
// place it behind
|
|
//vm_vec_copy_scale(&norm1, &Player_obj->orient.vec.fvec, -2*(Player_obj->radius+radius*(1.0f+(float(j)/100.0f))));
|
|
//vm_vec_add(&cameraTarget, &cameraTarget, &norm1);
|
|
|
|
vm_vec_copy_scale(&cameraTarget,&perp, radius/5.0f);
|
|
|
|
//vm_vec_scale(&cameraTarget, Player_obj->radius+radius*(1.0f+(float(j)/100.0f)));
|
|
|
|
//vm_vec_add(&cameraTarget, &pos, &cameraTarget);
|
|
//CameraSpeed = (radius+distance)/25;
|
|
|
|
//vm_vec_add(&cameraTarget, &zero, &perp);
|
|
//vm_vec_scale(&CameraVelocity, (radius+distance/100.f));
|
|
//vm_vec_scale(&CameraVelocity, 1.0f/float(NPS_TICKRATE*tc_factor));
|
|
}
|
|
else
|
|
{
|
|
vm_vec_add(&cameraTarget, &zero, &zero);
|
|
//CameraSpeed = 0;
|
|
}
|
|
//CameraMoving = false;
|
|
|
|
|
|
EndAPCinematic = timestamp((10000*tc_factor)+NPS_TICKRATE); // 10 objective seconds before end of cinematic
|
|
MoveCamera = timestamp((5500*tc_factor)+NPS_TICKRATE);
|
|
camMovingTime = int(4.5*float(tc_factor));
|
|
set_time_compression((float)tc_factor);
|
|
}
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Checks if autopilot should automatically die
|
|
// Returns true if:
|
|
// * Targetted waypoint < 10,000 meters away
|
|
// * Enemy < 10,000 meters
|
|
bool Autopilot_AutoDiable()
|
|
{
|
|
return !CanAutopilot();
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Disengages autopilot
|
|
// this does:
|
|
// * Time compression to 1x
|
|
// * Delete AI nav goal
|
|
// * Control switched from AI to player
|
|
void EndAutoPilot()
|
|
{
|
|
AutoPilotEngaged = false;
|
|
|
|
set_time_compression(1);
|
|
lock_time_compression(false);
|
|
Player_use_ai = 0;
|
|
//Clear AI Goals
|
|
|
|
if (CinematicStarted) // clear cinematic if we need to
|
|
{
|
|
if (UseCutsceneBars)
|
|
{
|
|
Cutscene_bar_flags &= ~CUB_CUTSCENE;
|
|
}
|
|
cam_reset_camera();
|
|
CinematicStarted = false;
|
|
}
|
|
|
|
Assert( CurrentNav >= 0 );
|
|
|
|
int goal = 0;
|
|
char *goal_name = NULL;
|
|
|
|
if (Navs[CurrentNav].flags & NP_WAYPOINT)
|
|
{
|
|
goal = AI_GOAL_WAYPOINTS_ONCE;
|
|
goal_name = ((waypoint_list*)Navs[CurrentNav].target_obj)->name;
|
|
}
|
|
else
|
|
{
|
|
goal = AI_GOAL_FLY_TO_SHIP;
|
|
goal_name = ((ship*)Navs[CurrentNav].target_obj)->ship_name;
|
|
}
|
|
|
|
// assign ship goals
|
|
// when assigning goals to individual ships only do so if Ships[shipnum].wingnum != -1
|
|
// we will assign wing goals below
|
|
int i, j;
|
|
|
|
for (i = 0; i < MAX_SHIPS; i++)
|
|
|
|
{
|
|
if (Ships[i].objnum != -1 &&
|
|
(
|
|
Ships[i].flags2 & SF2_NAVPOINT_CARRY ||
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY )
|
|
)
|
|
)
|
|
{
|
|
//unlock their weaponry
|
|
//Ships[i].flags2 &= ~(SF2_PRIMARIES_LOCKED | SF2_SECONDARIES_LOCKED);
|
|
Ai_info[Ships[i].ai_index].waypoint_speed_cap = -1; // uncap their speed
|
|
|
|
for (j = 0; j < MAX_AI_GOALS; j++)
|
|
{
|
|
ai_goal *aigp = &Ai_info[Ships[i].ai_index].goals[j];
|
|
|
|
if ( ((aigp->ship_name != NULL) && !stricmp(aigp->ship_name, goal_name))
|
|
&& (aigp->ai_mode == goal) )
|
|
{
|
|
ai_remove_ship_goal(&Ai_info[Ships[i].ai_index], j);
|
|
}
|
|
}
|
|
|
|
if (Ships[i].wingnum != -1)
|
|
{
|
|
for (j = 0; j < MAX_AI_GOALS; j++)
|
|
{
|
|
ai_goal *aigp = &Wings[Ships[i].wingnum].ai_goals[j];
|
|
|
|
if ( ((aigp->ship_name != NULL) && !stricmp(aigp->ship_name, goal_name))
|
|
&& (aigp->ai_mode == goal) )
|
|
{
|
|
aigp->ai_mode = AI_GOAL_NONE;
|
|
aigp->signature = -1;
|
|
aigp->priority = -1;
|
|
aigp->flags = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// un-assign wing goals
|
|
if ( !(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) )
|
|
{
|
|
for (i = 0; i < MAX_WINGS; i++)
|
|
{
|
|
if (Wings[i].flags & WF_NAV_CARRY )
|
|
{
|
|
for (j = 0; j < MAX_AI_GOALS; j++)
|
|
{
|
|
ai_goal *aigp = &Wings[i].ai_goals[j];
|
|
|
|
if ( ((aigp->ship_name != NULL) && !stricmp(aigp->ship_name, goal_name))
|
|
&& (aigp->ai_mode == goal) )
|
|
{
|
|
aigp->ai_mode = AI_GOAL_NONE;
|
|
aigp->signature = -1;
|
|
aigp->priority = -1;
|
|
aigp->flags = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ********************************************************************************************
|
|
|
|
camera* nav_get_set_camera()
|
|
{
|
|
static camid nav_camera;
|
|
if(!nav_camera.isValid())
|
|
{
|
|
nav_camera = cam_create("Nav camera");
|
|
}
|
|
|
|
cam_set_camera(nav_camera);
|
|
|
|
return nav_camera.getCamera();
|
|
}
|
|
|
|
void nav_warp(bool prewarp=false)
|
|
{
|
|
// ok... find our end distance - norm1 is still a unit vector in the direction from the player to the navpoint
|
|
vec3d targetPos, tpos=Player_obj->pos, pos;
|
|
|
|
vm_vec_sub(&pos, Navs[CurrentNav].GetPosition(), &Player_obj->pos);
|
|
vm_vec_normalize(&pos);
|
|
vm_vec_scale(&pos, 250.0f); // we move by increments of 250
|
|
|
|
while (CanAutopilotPos(tpos))
|
|
{
|
|
vm_vec_add(&tpos, &tpos, &pos);
|
|
}
|
|
vm_vec_sub(&targetPos, &tpos, &Player_obj->pos); //targetPos is actually a projection in a the direction toward the nav
|
|
|
|
if (prewarp)
|
|
{
|
|
vm_vec_scale(&targetPos, 0.5);
|
|
vm_vec_add(&cameraPos, &cameraPos, &targetPos);
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1 &&
|
|
(Ships[i].flags2 & SF2_NAVPOINT_CARRY ||
|
|
(Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)
|
|
)
|
|
)
|
|
{
|
|
vm_vec_add(&Objects[Ships[i].objnum].pos, &Objects[Ships[i].objnum].pos, &targetPos);
|
|
}
|
|
}
|
|
|
|
// retime all collision pairs
|
|
obj_all_collisions_retime();
|
|
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Checks for changes every NPS_TICKRATE milliseconds
|
|
// Checks:
|
|
// * if we've gotten close enough to a nav point for it to be counted as "Visited"
|
|
// * If we're current AutoNavigating it checks if we need to autodisengage
|
|
void NavSystem_Do()
|
|
{
|
|
static unsigned int last_update = 0;
|
|
if (clock () - last_update > NPS_TICKRATE)
|
|
{
|
|
if (AutoPilotEngaged)
|
|
{
|
|
if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)
|
|
{
|
|
camera *cam = nav_get_set_camera();
|
|
if (CinematicStarted)
|
|
{
|
|
// update our cinematic and possibly perform warp
|
|
//if (!CameraMoving)
|
|
if(cam != NULL)
|
|
cam->set_rotation_facing(&Player_obj->pos);
|
|
|
|
if (timestamp() >= MoveCamera && !CameraMoving && vm_vec_mag(&cameraTarget) > 0.0f)
|
|
{
|
|
//Free_camera->set_position(&cameraTarget, float(camMovingTime), float(camMovingTime)/2.0f);
|
|
//Free_camera->set_translation_velocity(&cameraTarget);
|
|
CameraMoving = true;
|
|
}
|
|
|
|
|
|
if (timestamp() >= EndAPCinematic || Autopilot_AutoDiable())
|
|
{
|
|
nav_warp();
|
|
EndAutoPilot();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// start cinematic
|
|
if (UseCutsceneBars)
|
|
{
|
|
Cutscene_bar_flags |= CUB_CUTSCENE;
|
|
Cutscene_bar_flags &= ~CUB_GRADUAL;
|
|
}
|
|
nav_warp(true);
|
|
|
|
if(cam != NULL)
|
|
{
|
|
cam->set_position(&cameraPos);
|
|
cam->set_rotation_facing(&Player_obj->pos);
|
|
}
|
|
|
|
CinematicStarted = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Autopilot_AutoDiable())
|
|
EndAutoPilot();
|
|
|
|
}
|
|
|
|
}
|
|
// check if a NavPoints target has left, delete it if so
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_NAVPOINTS; i++)
|
|
{
|
|
if ((Navs[i].flags & NP_SHIP) && (Navs[i].target_obj != NULL))
|
|
{
|
|
if (((ship*)Navs[i].target_obj)->objnum == -1)
|
|
{
|
|
if (CurrentNav == i)
|
|
CurrentNav = -1;
|
|
DelNavPoint(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if we're reached a Node
|
|
for (i = 0; i < MAX_NAVPOINTS; i++)
|
|
{
|
|
if (Navs[i].target_obj != NULL)
|
|
{
|
|
if (Navs[i].flags & NP_VALIDTYPE && DistanceTo(i) < 1000)
|
|
Navs[i].flags |= NP_VISITED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ramp time compression - only if not using cinematics
|
|
if (AutoPilotEngaged && !(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS))
|
|
{
|
|
int dstfrm_start = start_dist - DistanceTo(CurrentNav);
|
|
|
|
// Ramp UP time compression
|
|
if (dstfrm_start < (3500*ramp_bias))
|
|
{
|
|
|
|
if (dstfrm_start >= (3000*ramp_bias) && DistanceTo(CurrentNav) > 30000)
|
|
set_time_compression(64);
|
|
else if (dstfrm_start >= (2000*ramp_bias))
|
|
set_time_compression(32);
|
|
else if (dstfrm_start >= (1600*ramp_bias))
|
|
set_time_compression(16);
|
|
else if (dstfrm_start >= (1200*ramp_bias))
|
|
set_time_compression(8);
|
|
else if (dstfrm_start >= (800*ramp_bias))
|
|
set_time_compression(4);
|
|
else if (dstfrm_start >= (400*ramp_bias))
|
|
set_time_compression(2);
|
|
}
|
|
|
|
// Ramp DOWN time compression
|
|
if (DistanceTo(CurrentNav) <= (7000*ramp_bias))
|
|
{
|
|
int dist = DistanceTo(CurrentNav);
|
|
if (dist >= (5000*ramp_bias))
|
|
set_time_compression(32);
|
|
else if (dist >= (4000*ramp_bias))
|
|
set_time_compression(16);
|
|
else if (dist >= (3000*ramp_bias))
|
|
set_time_compression(8);
|
|
else if (dist >= (2000*ramp_bias))
|
|
set_time_compression(4);
|
|
else if (dist >= (1000*ramp_bias))
|
|
set_time_compression(2);
|
|
}
|
|
}
|
|
|
|
// autopilot linking
|
|
for (int i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1 && Ships[i].flags2 & SF2_NAVPOINT_NEEDSLINK)
|
|
{
|
|
object *other_objp = &Objects[Ships[i].objnum];
|
|
|
|
if (vm_vec_dist_quick(&Player_obj->pos, &other_objp->pos) < (NavLinkDistance + other_objp->radius))
|
|
{
|
|
Ships[i].flags2 &= ~SF2_NAVPOINT_NEEDSLINK;
|
|
Ships[i].flags2 |= SF2_NAVPOINT_CARRY;
|
|
|
|
send_autopilot_msgID(NP_MSG_MISC_LINKED);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
// ********************************************************************************************
|
|
|
|
void send_autopilot_msgID(int msgid)
|
|
{
|
|
if (msgid < 0 || msgid > NP_NUM_MESSAGES)
|
|
return;
|
|
|
|
send_autopilot_msg(NavMsgs[msgid].message, NavMsgs[msgid].filename);
|
|
}
|
|
// ********************************************************************************************
|
|
|
|
void send_autopilot_msg(char *msg, char *snd)
|
|
{
|
|
// setup
|
|
if (audio_handle != -1)
|
|
{
|
|
audiostream_close_file(audio_handle, 0);
|
|
audio_handle = -1;
|
|
}
|
|
|
|
if (strlen(msg) != 0 && strcmp(msg, "none"))
|
|
change_message("autopilot builtin message", msg, -1, 0);
|
|
|
|
// load sound
|
|
if (snd != NULL || strlen(snd) == 0 || !strcmp(snd, "none"))
|
|
{
|
|
audio_handle = audiostream_open(snd, ASF_MENUMUSIC );
|
|
}
|
|
|
|
// send/play
|
|
if (audio_handle != -1)
|
|
{
|
|
audiostream_play(audio_handle, Master_event_music_volume, 0);
|
|
}
|
|
|
|
if (strlen(msg) != 0 && strcmp(msg, "none"))
|
|
message_training_queue("autopilot builtin message", timestamp(0), 5); // display message for five seconds
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Inits the Nav System
|
|
void NavSystem_Init()
|
|
{
|
|
memset((char *)&Navs, 0, sizeof(Navs));
|
|
AutoPilotEngaged = false;
|
|
CurrentNav = -1;
|
|
audio_handle = -1;
|
|
CinematicStarted = false;
|
|
UseCutsceneBars = true;
|
|
|
|
// defaults... can be tabled or bound to mission later
|
|
if (cf_exists_full("autopilot.tbl", CF_TYPE_TABLES))
|
|
parse_autopilot_table("autopilot.tbl");
|
|
else
|
|
parse_autopilot_table(NULL);
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
|
|
void parse_autopilot_table(char *filename)
|
|
{
|
|
int rval;
|
|
SCP_vector<std::string> lines;
|
|
|
|
// open localization
|
|
lcl_ext_open();
|
|
|
|
if ((rval = setjmp(parse_abort)) != 0)
|
|
{
|
|
mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", (filename) ? filename : "<default autopilot.tbl>", rval));
|
|
lcl_ext_close();
|
|
return;
|
|
}
|
|
|
|
if (filename == NULL)
|
|
read_file_text_from_array(defaults_get_file("autopilot.tbl"));
|
|
else
|
|
read_file_text(filename, CF_TYPE_TABLES);
|
|
|
|
reset_parse();
|
|
|
|
|
|
required_string("#Autopilot");
|
|
|
|
// autopilot link distance
|
|
required_string("$Link Distance:");
|
|
stuff_int(&NavLinkDistance);
|
|
|
|
// optional no cutscene bars
|
|
if (optional_string("+No_Cutscene_Bars"))
|
|
UseCutsceneBars = false;
|
|
// optional no cutscene bars
|
|
if (optional_string("+No_Autopilot_Interrupt"))
|
|
Cmdline_autopilot_interruptable = 0;
|
|
|
|
// No Nav selected message
|
|
char *msg_tags[] = { "$No Nav Selected:", "$Gliding:", "$Too Close:", "$Hostiles:", "$Linked:", "$Hazard:" };
|
|
for (int i = 0; i < NP_NUM_MESSAGES; i++)
|
|
{
|
|
required_string(msg_tags[i]);
|
|
|
|
required_string("+Msg:");
|
|
stuff_string(NavMsgs[i].message, F_MESSAGE, 256);
|
|
|
|
required_string("+Snd File:");
|
|
stuff_string(NavMsgs[i].filename, F_NAME, 256);
|
|
}
|
|
|
|
|
|
required_string("#END");
|
|
|
|
// close localization
|
|
lcl_ext_close();
|
|
}
|
|
|
|
|
|
// ********************************************************************************************
|
|
// Finds a Nav point by name
|
|
int FindNav(char *Nav)
|
|
{
|
|
for (int i = 0; i < MAX_NAVPOINTS; i++)
|
|
{
|
|
if (!stricmp(Navs[i].NavName, Nav))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Set A Nav point to "ZERO"
|
|
void ZeroNav(int i)
|
|
{
|
|
memset((char *)&Navs[i], 0, sizeof(NavPoint));
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Removes a Nav
|
|
bool DelNavPoint(char *Nav)
|
|
{
|
|
int n = FindNav(Nav);
|
|
|
|
return DelNavPoint(n);
|
|
|
|
}
|
|
|
|
|
|
bool DelNavPoint(int nav)
|
|
{
|
|
if (nav != -1)
|
|
{
|
|
if (nav != MAX_NAVPOINTS-1)
|
|
{
|
|
for (int i = nav; i < MAX_NAVPOINTS-1; i++)
|
|
{
|
|
Navs[nav] = Navs[nav+1];
|
|
}
|
|
|
|
}
|
|
ZeroNav(MAX_NAVPOINTS-1);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// adds a Nav
|
|
bool AddNav_Ship(char *Nav, char *TargetName, int flags)
|
|
{
|
|
// find an empty nav - should be the end
|
|
|
|
int empty = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_NAVPOINTS && empty == -1; i++)
|
|
{
|
|
if (Navs[i].flags == 0)
|
|
empty = i;
|
|
}
|
|
|
|
if (empty == -1) // no empty navpoint slots
|
|
return false;
|
|
|
|
// Create the NavPoint struct
|
|
NavPoint tnav;
|
|
|
|
strncpy(tnav.NavName, Nav, 32);
|
|
tnav.flags = NP_SHIP | flags;
|
|
|
|
Assert(!(tnav.flags & NP_WAYPOINT));
|
|
|
|
|
|
for (i = 0; i < MAX_SHIPS; i++)
|
|
{
|
|
if (Ships[i].objnum != -1 && !stricmp(TargetName, Ships[i].ship_name))
|
|
{
|
|
tnav.target_obj = (void *)&Ships[i];
|
|
}
|
|
}
|
|
|
|
tnav.waypoint_num = 0; // unused for NP_SHIP
|
|
|
|
|
|
// copy it into it's location
|
|
Navs[empty] = tnav;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool AddNav_Waypoint(char *Nav, char *WP_Path, int node, int flags)
|
|
{
|
|
// find an empty nav - should be the end
|
|
|
|
int empty = -1;
|
|
int i;
|
|
|
|
if (node == 0)
|
|
node = 1;
|
|
|
|
for (i = 0; i < MAX_NAVPOINTS && empty == -1; i++)
|
|
{
|
|
if (Navs[i].flags == 0)
|
|
empty = i;
|
|
}
|
|
|
|
if (empty == -1) // no empty navpoint slots
|
|
return false;
|
|
|
|
// Create the NavPoint struct
|
|
NavPoint tnav;
|
|
|
|
strncpy(tnav.NavName, Nav, 32);
|
|
tnav.flags = NP_WAYPOINT | flags;
|
|
|
|
Assert(!(tnav.flags & NP_SHIP));
|
|
|
|
|
|
for (i = 0; i < Num_waypoint_lists; i++)
|
|
{
|
|
if (!stricmp(WP_Path, Waypoint_lists[i].name))
|
|
{
|
|
tnav.target_obj = (void *)&Waypoint_lists[i];
|
|
}
|
|
}
|
|
|
|
tnav.waypoint_num = node;
|
|
|
|
// copy it into it's location
|
|
Navs[empty] = tnav;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
//Change Flags
|
|
bool Nav_Alt_Flags(char *Nav, int flags)
|
|
{
|
|
flags &= ~NP_VALIDTYPE; //clear the NP_SHIP and NP_WAYPOINT bits, make sure they haven't been set
|
|
|
|
int nav = FindNav(Nav);
|
|
|
|
if (nav != -1)
|
|
{
|
|
Navs[nav].flags &= NP_VALIDTYPE; // Clear all bits BUT NP_SHIP or NO_WAYPOINT
|
|
Navs[nav].flags |= flags; // merge
|
|
}
|
|
|
|
return (nav != -1);
|
|
}
|
|
|
|
//Get Flags
|
|
int Nav_Get_Flags(char *Nav)
|
|
{
|
|
int flags = 0;
|
|
|
|
int nav = FindNav(Nav);
|
|
|
|
if (nav != -1)
|
|
flags = Navs[nav].flags;
|
|
|
|
return flags;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// Sexp Accessors
|
|
|
|
//Generic
|
|
bool Nav_Set_Flag(char *Nav, int flag)
|
|
{
|
|
Assert(!(flag & NP_VALIDTYPE));
|
|
int nav = FindNav(Nav);
|
|
|
|
if (nav != -1)
|
|
{
|
|
Navs[nav].flags |= flag;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_UnSet_Flag(char *Nav, int flag)
|
|
{
|
|
Assert(!(flag & NP_VALIDTYPE));
|
|
|
|
int nav = FindNav(Nav);
|
|
|
|
if (nav != -1)
|
|
{
|
|
Navs[nav].flags &= ~flag;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
//Named
|
|
bool Nav_Set_Hidden(char *Nav)
|
|
{
|
|
return Nav_Set_Flag(Nav, NP_HIDDEN);
|
|
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_Set_NoAccess(char *Nav)
|
|
{
|
|
return Nav_Set_Flag(Nav, NP_NOACCESS);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_Set_Visited(char *Nav)
|
|
{
|
|
|
|
return Nav_Set_Flag(Nav, NP_VISITED);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_UnSet_Hidden(char *Nav)
|
|
{
|
|
return Nav_UnSet_Flag(Nav, NP_HIDDEN);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_UnSet_NoAccess(char *Nav)
|
|
{
|
|
return Nav_UnSet_Flag(Nav, NP_NOACCESS);
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool Nav_UnSet_Visited(char *Nav)
|
|
{
|
|
return Nav_UnSet_Flag(Nav, NP_VISITED);
|
|
}
|
|
|
|
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
unsigned int DistanceTo(char *nav)
|
|
{
|
|
int n = FindNav(nav);
|
|
|
|
return DistanceTo(n);
|
|
}
|
|
|
|
unsigned int DistanceTo(int nav)
|
|
{
|
|
if (nav >= MAX_NAVPOINTS && nav < 0)
|
|
return 0xFFFFFFFF;
|
|
|
|
return (uint)vm_vec_dist_quick(&Player_obj->pos, Navs[nav].GetPosition());
|
|
}
|
|
|
|
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
bool IsVisited(char *nav)
|
|
{
|
|
int n = FindNav(nav);
|
|
|
|
return IsVisited(n);
|
|
}
|
|
|
|
bool IsVisited(int nav)
|
|
{
|
|
if (nav >= MAX_NAVPOINTS && nav < 0)
|
|
return 0;
|
|
|
|
if (Navs[nav].flags & NP_VISITED)
|
|
return 1;
|
|
return 0;
|
|
}
|