2 * $Logfile: /Freespace2/code/Ship/AWACS.cpp $
7 * all sorts of cool stuff about ships
10 * Revision 1.2 2002/05/07 03:16:52 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:10 root
17 * 13 8/25/99 10:50a Dave
18 * Added music to the mainhall.tbl
20 * 12 7/19/99 12:02p Andsager
21 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
22 * only blow up subsystem if its strength is > 0
24 * 11 7/06/99 10:45a Andsager
25 * Modify engine wash to work on any ship that is not small. Add AWACS
28 * 10 6/17/99 1:41p Andsager
29 * Added comments to team_visibility_update
31 * 9 6/15/99 4:01p Jamesa
32 * Don't count navbuoys and cargo containers towards team visibilitiy.
34 * 8 6/02/99 12:52p Andsager
35 * Added team-wide ship visibility. Implemented for player.
37 * 7 5/28/99 3:14p Andsager
38 * Modify nebula scan range by species.
40 * 6 5/28/99 9:39a Andsager
41 * Modify awacs to take account for huge ships.
43 * 5 5/12/99 2:55p Andsager
44 * Implemented level 2 tag as priority in turret object selection
46 * 4 1/25/99 2:49p Anoop
47 * Put in more sanity checks when checking for AWACS sources.
49 * 3 1/25/99 5:03a Dave
50 * First run of stealth, AWACS and TAG missile support. New mission type
53 * 2 1/08/99 2:08p Dave
54 * Fixed software rendering for pofview. Super early support for AWACS and
66 #include "missionparse.h"
69 // ----------------------------------------------------------------------------------------------------
73 // timestamp for updating AWACS stuff
74 #define AWACS_STAMP_TIME 1000
77 // total awacs levels for all teams
78 float Awacs_team[MAX_TEAMS]; // total AWACS capabilities for each team
79 float Awacs_level; // Awacs_friendly - Awacs_hostile
81 // list of all AWACS sources
83 typedef struct awacs_entry {
88 awacs_entry Awacs[MAX_AWACS];
91 // species dependent factor increasing scan range in nebula
92 #define AWACS_SHIVAN_MULT 1.50
93 #define AWACS_VASUDAN_MULT 1.25
94 #define AWACS_TERRAN_MULT 1.00
96 // TEAM SHIP VISIBILITY
97 // team-wide shared visibility info
98 // at start of each frame (maybe timestamp), compute visibility
99 ubyte Team_friendly_visibility[MAX_SHIPS];
100 ubyte Team_neutral_visibility[MAX_SHIPS];
101 ubyte Team_hostile_visibility[MAX_SHIPS];
102 // no shared team visibility info for TEAM_UNKNOWN or TEAM_TRAITOR
104 // ----------------------------------------------------------------------------------------------------
105 // AWACS FORWARD DECLARATIONS
108 // update the total awacs levels
109 void awacs_update_all_levels();
111 // update team visibility info
112 void team_visibility_update();
115 // ----------------------------------------------------------------------------------------------------
119 // call when initializing level, before parsing mission
120 void awacs_level_init()
122 // set the update timestamp to -1
126 // call every frame to process AWACS details
129 // if we need to update total AWACS levels, do so now
130 if((Awacs_stamp == -1) || timestamp_elapsed(Awacs_stamp)){
131 // reset the timestamp
132 Awacs_stamp = timestamp(AWACS_STAMP_TIME);
134 // recalculate everything
135 awacs_update_all_levels();
137 // update team visibility
138 team_visibility_update();
143 // ----------------------------------------------------------------------------------------------------
144 // AWACS FORWARD DEFINITIONS
147 // update the total awacs levels
148 void awacs_update_all_levels()
152 ship_subsys *ship_system;
157 for(idx=0; idx<MAX_TEAMS; idx++){
158 Awacs_team[idx] = 0.0f;
163 // we need to traverse all subsystems on all ships
164 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
165 // make sure its a valid ship
166 if((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)){
170 // get a handle to the ship
171 shipp = &Ships[Objects[moveup->objnum].instance];
173 // ignore dying, departing, or arriving ships
174 if((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)){
178 // only look at ships that have awacs subsystems
179 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS) ) {
183 // traverse all subsystems
184 for(ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system)){
185 // if this is an AWACS subsystem
186 if((ship_system->system_info != NULL) && (ship_system->system_info->flags & MSS_FLAG_AWACS)){
187 // add the intensity to the team total
188 Awacs_team[shipp->team] += ship_system->awacs_intensity * (ship_system->current_hits / ship_system->system_info->max_hits);
190 // add an Awacs source
191 if(Awacs_count < MAX_AWACS){
192 Awacs[Awacs_count].subsys = ship_system;
193 Awacs[Awacs_count].team = shipp->team;
194 Awacs[Awacs_count].objp = &Objects[moveup->objnum];
202 Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
207 for(idx=0; idx<MAX_TEAMS; idx++){
208 nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
210 nprintf(("General", "AWACS level == %f\n", Awacs_level));
215 // get the total AWACS level for target to viewer
216 // < 0.0f : untargetable
217 // 0.0 - 1.0f : marginally targetable
218 // >= 1.0f : fully targetable as normal
219 float awacs_get_level(object *target, ship *viewer, int use_awacs)
221 vector dist_vec, subsys_pos;
222 float closest = 0.0f;
224 int closest_index = -1;
228 // if the viewer is me, and I'm a multiplayer observer, its always viewable
229 if((viewer == Player_ship) && (Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
233 // ships on the same teamare always viewable
234 if((target->type == OBJ_SHIP) && (Ships[target->instance].team == viewer->team)){
238 int stealth_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
239 int nebula_mission = (The_mission.flags & MISSION_FLAG_FULLNEB);
240 int check_huge_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_HUGE_SHIP);
242 // only check for Awacs if stealth ship or Nebula mission
243 // determine the closest friendly awacs
244 if ((stealth_ship || nebula_mission) && use_awacs) {
245 for (idx=0; idx<Awacs_count; idx++) {
246 // if not on the same team as the viewer
247 if(Awacs[idx].team != viewer->team){
251 // if this awacs source has somehow become invalid
252 if(Awacs[idx].objp->type != OBJ_SHIP){
256 // get the subsystem position
257 if(!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)){
261 // determine if its the closest
262 // special case for HUGE_SHIPS
263 if ( check_huge_ship ) {
264 // check if inside bbox expanded by awacs_radius
265 if (check_world_pt_in_expanded_ship_bbox(&subsys_pos, target, Awacs[idx].subsys->awacs_radius)) {
272 // get distance from Subsys to target
273 vm_vec_sub(&dist_vec, &subsys_pos, &target->pos);
274 test = vm_vec_mag_quick(&dist_vec);
276 if (test > Awacs[idx].subsys->awacs_radius) {
279 if ((closest_index == -1) || (test < closest)) {
288 // check for a tagged ship. TAG'd ships are _always_ visible
289 if(target->type == OBJ_SHIP){
290 shipp = &Ships[target->instance];
291 if(shipp->tag_left > 0.0f || shipp->level2_tag_left > 0.0f){
296 // if this is a stealth ship
298 // if the ship is within range of an awacs
299 if(closest_index != -1){
300 // if the nebula effect is active, stealth ships are only partially targetable
301 if ( nebula_mission ) {
305 // otherwise its targetable
308 // otherwise its completely hidden
315 // if this is not a nebula mission, its always targetable
316 if( !nebula_mission ){
320 // if the ship is within range of an awacs, its fully targetable
321 if(closest_index != -1){
325 // fully targetable at half the nebula value
326 // modify distance by species
327 float scan_nebula_range = Neb2_awacs;
328 switch (Ship_info[viewer->ship_info_index].species) {
330 scan_nebula_range *= float(AWACS_SHIVAN_MULT);
333 case SPECIES_VASUDAN:
334 scan_nebula_range *= float(AWACS_VASUDAN_MULT);
338 scan_nebula_range *= float(AWACS_TERRAN_MULT);
345 // special case for huge ship - check inside expanded bounding boxes
346 if ( check_huge_ship ) {
347 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, scan_nebula_range)) {
348 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, 0.5f*scan_nebula_range)) {
354 // otherwise check straight up nebula numbers
356 vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos);
357 test = vm_vec_mag_quick(&dist_vec);
358 if(test < (0.5f * scan_nebula_range)){
360 } else if(test < scan_nebula_range){
365 // untargetable at longer range
374 // update team visibility
376 void team_visibility_update()
378 int friendly_count, hostile_count, neutral_count, unknown_count, traitor_count, num_stealth;
379 int friendly_ships[MAX_SHIPS];
380 int hostile_ships[MAX_SHIPS];
381 int neutral_ships[MAX_SHIPS];
382 int unknown_ships[MAX_SHIPS];
383 int traitor_ships[MAX_SHIPS];
387 friendly_count = hostile_count = neutral_count = unknown_count = traitor_count = num_stealth = 0;
389 // zero out visibility for each team
390 memset(Team_friendly_visibility, 0, sizeof(Team_friendly_visibility));
391 memset(Team_hostile_visibility, 0, sizeof(Team_hostile_visibility));
392 memset(Team_neutral_visibility, 0, sizeof(Team_neutral_visibility));
394 // Go through list of ships and mark those visible for given team
395 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
396 // make sure its a valid ship
397 if ((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)) {
401 // get a handle to the ship
402 shipp = &Ships[Objects[moveup->objnum].instance];
404 // ignore dying, departing, or arriving ships
405 if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)) {
409 // check if ship if flagged as invisible
410 if (shipp->flags & SF_HIDDEN_FROM_SENSORS) {
414 int ship_num = shipp - Ships;
415 Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
417 switch (shipp->team) {
419 Team_friendly_visibility[ship_num] = 1;
420 friendly_ships[friendly_count++] = ship_num;
424 Team_hostile_visibility[ship_num] = 1;
425 hostile_ships[hostile_count++] = ship_num;
429 Team_neutral_visibility[ship_num] = 1;
430 neutral_ships[neutral_count++] = ship_num;
434 unknown_ships[unknown_count++] = ship_num;
438 traitor_ships[traitor_count++] = ship_num;
446 int team_count[NUM_TEAMS];
447 int *team_ships[NUM_TEAMS];
448 ubyte *team_visibility[NUM_TEAMS];
449 int *cur_team_ships, *en_team_ships;
451 // set up variable for loop
452 // team count is number of ship of each team type
453 team_count[0] = friendly_count;
454 team_count[1] = hostile_count;
455 team_count[2] = neutral_count;
456 team_count[3] = unknown_count;
457 team_count[4] = traitor_count;
459 // team ships is array of ship_nums of ships from each team
460 team_ships[0] = friendly_ships;
461 team_ships[1] = hostile_ships;
462 team_ships[2] = neutral_ships;
463 team_ships[3] = unknown_ships;
464 team_ships[4] = traitor_ships;
466 // this is the result, visiblity for each team indexed by ship_num
467 team_visibility[0] = Team_friendly_visibility;
468 team_visibility[1] = Team_hostile_visibility;
469 team_visibility[2] = Team_neutral_visibility;
470 team_visibility[3] = NULL;
471 team_visibility[4] = NULL;
473 int idx, en_idx, cur_count, en_count, en_team;
475 // Do for friendly, neutral and hostile (only teams that cooperate with visibility)
476 for (int cur_team=0; cur_team<3; cur_team++) {
477 // set up current team
478 cur_count = team_count[cur_team];
479 cur_team_ships = team_ships[cur_team];
481 // short circuit if team has no presence
482 if (cur_count == 0) {
486 // check agains both enemy teams
487 for (int en_team_inc=1; en_team_inc<NUM_TEAMS; en_team_inc++) {
489 en_team = (cur_team + en_team_inc) % NUM_TEAMS; // enemy_team (index) is cur_team + (1-4), wrapped back to range 0-4
490 en_count = team_count[en_team];
491 en_team_ships = team_ships[en_team];
493 // check if current team can see enemy team's ships
494 for (en_idx=0; en_idx<en_count; en_idx++) {
495 // for each ship on other team
496 for (idx=0; idx<cur_count; idx++) {
497 // ignore nav buoys and cargo containers
498 if(Ship_info[Ships[cur_team_ships[idx]].ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY)){
502 // check against each ship on my team(and AWACS only once)
503 if ( awacs_get_level(&Objects[Ships[en_team_ships[en_idx]].objnum], &Ships[cur_team_ships[idx]], idx==0) > 1) {
504 team_visibility[cur_team][en_team_ships[en_idx]] = 1;
507 } //end cur_count (ship on current team looking at en_team)
514 // Determine is ship is visible by team
515 int ship_is_visible_by_team(int ship_num, int team)
517 Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
521 return Team_friendly_visibility[ship_num];
525 return Team_hostile_visibility[ship_num];
529 return Team_neutral_visibility[ship_num];