2 * $Logfile: /Freespace2/code/Ship/AWACS.cpp $
7 * all sorts of cool stuff about ships
10 * Revision 1.1 2002/05/03 03:28:10 root
14 * 13 8/25/99 10:50a Dave
15 * Added music to the mainhall.tbl
17 * 12 7/19/99 12:02p Andsager
18 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
19 * only blow up subsystem if its strength is > 0
21 * 11 7/06/99 10:45a Andsager
22 * Modify engine wash to work on any ship that is not small. Add AWACS
25 * 10 6/17/99 1:41p Andsager
26 * Added comments to team_visibility_update
28 * 9 6/15/99 4:01p Jamesa
29 * Don't count navbuoys and cargo containers towards team visibilitiy.
31 * 8 6/02/99 12:52p Andsager
32 * Added team-wide ship visibility. Implemented for player.
34 * 7 5/28/99 3:14p Andsager
35 * Modify nebula scan range by species.
37 * 6 5/28/99 9:39a Andsager
38 * Modify awacs to take account for huge ships.
40 * 5 5/12/99 2:55p Andsager
41 * Implemented level 2 tag as priority in turret object selection
43 * 4 1/25/99 2:49p Anoop
44 * Put in more sanity checks when checking for AWACS sources.
46 * 3 1/25/99 5:03a Dave
47 * First run of stealth, AWACS and TAG missile support. New mission type
50 * 2 1/08/99 2:08p Dave
51 * Fixed software rendering for pofview. Super early support for AWACS and
63 #include "missionparse.h"
66 // ----------------------------------------------------------------------------------------------------
70 // timestamp for updating AWACS stuff
71 #define AWACS_STAMP_TIME 1000
74 // total awacs levels for all teams
75 float Awacs_team[MAX_TEAMS]; // total AWACS capabilities for each team
76 float Awacs_level; // Awacs_friendly - Awacs_hostile
78 // list of all AWACS sources
80 typedef struct awacs_entry {
85 awacs_entry Awacs[MAX_AWACS];
88 // species dependent factor increasing scan range in nebula
89 #define AWACS_SHIVAN_MULT 1.50
90 #define AWACS_VASUDAN_MULT 1.25
91 #define AWACS_TERRAN_MULT 1.00
93 // TEAM SHIP VISIBILITY
94 // team-wide shared visibility info
95 // at start of each frame (maybe timestamp), compute visibility
96 ubyte Team_friendly_visibility[MAX_SHIPS];
97 ubyte Team_neutral_visibility[MAX_SHIPS];
98 ubyte Team_hostile_visibility[MAX_SHIPS];
99 // no shared team visibility info for TEAM_UNKNOWN or TEAM_TRAITOR
101 // ----------------------------------------------------------------------------------------------------
102 // AWACS FORWARD DECLARATIONS
105 // update the total awacs levels
106 void awacs_update_all_levels();
108 // update team visibility info
109 void team_visibility_update();
112 // ----------------------------------------------------------------------------------------------------
116 // call when initializing level, before parsing mission
117 void awacs_level_init()
119 // set the update timestamp to -1
123 // call every frame to process AWACS details
126 // if we need to update total AWACS levels, do so now
127 if((Awacs_stamp == -1) || timestamp_elapsed(Awacs_stamp)){
128 // reset the timestamp
129 Awacs_stamp = timestamp(AWACS_STAMP_TIME);
131 // recalculate everything
132 awacs_update_all_levels();
134 // update team visibility
135 team_visibility_update();
140 // ----------------------------------------------------------------------------------------------------
141 // AWACS FORWARD DEFINITIONS
144 // update the total awacs levels
145 void awacs_update_all_levels()
149 ship_subsys *ship_system;
154 for(idx=0; idx<MAX_TEAMS; idx++){
155 Awacs_team[idx] = 0.0f;
160 // we need to traverse all subsystems on all ships
161 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
162 // make sure its a valid ship
163 if((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)){
167 // get a handle to the ship
168 shipp = &Ships[Objects[moveup->objnum].instance];
170 // ignore dying, departing, or arriving ships
171 if((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)){
175 // only look at ships that have awacs subsystems
176 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS) ) {
180 // traverse all subsystems
181 for(ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system)){
182 // if this is an AWACS subsystem
183 if((ship_system->system_info != NULL) && (ship_system->system_info->flags & MSS_FLAG_AWACS)){
184 // add the intensity to the team total
185 Awacs_team[shipp->team] += ship_system->awacs_intensity * (ship_system->current_hits / ship_system->system_info->max_hits);
187 // add an Awacs source
188 if(Awacs_count < MAX_AWACS){
189 Awacs[Awacs_count].subsys = ship_system;
190 Awacs[Awacs_count].team = shipp->team;
191 Awacs[Awacs_count].objp = &Objects[moveup->objnum];
199 Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
204 for(idx=0; idx<MAX_TEAMS; idx++){
205 nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
207 nprintf(("General", "AWACS level == %f\n", Awacs_level));
212 // get the total AWACS level for target to viewer
213 // < 0.0f : untargetable
214 // 0.0 - 1.0f : marginally targetable
215 // >= 1.0f : fully targetable as normal
216 float awacs_get_level(object *target, ship *viewer, int use_awacs)
218 vector dist_vec, subsys_pos;
219 float closest = 0.0f;
221 int closest_index = -1;
225 // if the viewer is me, and I'm a multiplayer observer, its always viewable
226 if((viewer == Player_ship) && (Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
230 // ships on the same teamare always viewable
231 if((target->type == OBJ_SHIP) && (Ships[target->instance].team == viewer->team)){
235 int stealth_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
236 int nebula_mission = (The_mission.flags & MISSION_FLAG_FULLNEB);
237 int check_huge_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_HUGE_SHIP);
239 // only check for Awacs if stealth ship or Nebula mission
240 // determine the closest friendly awacs
241 if ((stealth_ship || nebula_mission) && use_awacs) {
242 for (idx=0; idx<Awacs_count; idx++) {
243 // if not on the same team as the viewer
244 if(Awacs[idx].team != viewer->team){
248 // if this awacs source has somehow become invalid
249 if(Awacs[idx].objp->type != OBJ_SHIP){
253 // get the subsystem position
254 if(!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)){
258 // determine if its the closest
259 // special case for HUGE_SHIPS
260 if ( check_huge_ship ) {
261 // check if inside bbox expanded by awacs_radius
262 if (check_world_pt_in_expanded_ship_bbox(&subsys_pos, target, Awacs[idx].subsys->awacs_radius)) {
269 // get distance from Subsys to target
270 vm_vec_sub(&dist_vec, &subsys_pos, &target->pos);
271 test = vm_vec_mag_quick(&dist_vec);
273 if (test > Awacs[idx].subsys->awacs_radius) {
276 if ((closest_index == -1) || (test < closest)) {
285 // check for a tagged ship. TAG'd ships are _always_ visible
286 if(target->type == OBJ_SHIP){
287 shipp = &Ships[target->instance];
288 if(shipp->tag_left > 0.0f || shipp->level2_tag_left > 0.0f){
293 // if this is a stealth ship
295 // if the ship is within range of an awacs
296 if(closest_index != -1){
297 // if the nebula effect is active, stealth ships are only partially targetable
298 if ( nebula_mission ) {
302 // otherwise its targetable
305 // otherwise its completely hidden
312 // if this is not a nebula mission, its always targetable
313 if( !nebula_mission ){
317 // if the ship is within range of an awacs, its fully targetable
318 if(closest_index != -1){
322 // fully targetable at half the nebula value
323 // modify distance by species
324 float scan_nebula_range = Neb2_awacs;
325 switch (Ship_info[viewer->ship_info_index].species) {
327 scan_nebula_range *= float(AWACS_SHIVAN_MULT);
330 case SPECIES_VASUDAN:
331 scan_nebula_range *= float(AWACS_VASUDAN_MULT);
335 scan_nebula_range *= float(AWACS_TERRAN_MULT);
342 // special case for huge ship - check inside expanded bounding boxes
343 if ( check_huge_ship ) {
344 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, scan_nebula_range)) {
345 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, 0.5f*scan_nebula_range)) {
351 // otherwise check straight up nebula numbers
353 vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos);
354 test = vm_vec_mag_quick(&dist_vec);
355 if(test < (0.5f * scan_nebula_range)){
357 } else if(test < scan_nebula_range){
362 // untargetable at longer range
371 // update team visibility
373 void team_visibility_update()
375 int friendly_count, hostile_count, neutral_count, unknown_count, traitor_count, num_stealth;
376 int friendly_ships[MAX_SHIPS];
377 int hostile_ships[MAX_SHIPS];
378 int neutral_ships[MAX_SHIPS];
379 int unknown_ships[MAX_SHIPS];
380 int traitor_ships[MAX_SHIPS];
384 friendly_count = hostile_count = neutral_count = unknown_count = traitor_count = num_stealth = 0;
386 // zero out visibility for each team
387 memset(Team_friendly_visibility, 0, sizeof(Team_friendly_visibility));
388 memset(Team_hostile_visibility, 0, sizeof(Team_hostile_visibility));
389 memset(Team_neutral_visibility, 0, sizeof(Team_neutral_visibility));
391 // Go through list of ships and mark those visible for given team
392 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
393 // make sure its a valid ship
394 if ((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)) {
398 // get a handle to the ship
399 shipp = &Ships[Objects[moveup->objnum].instance];
401 // ignore dying, departing, or arriving ships
402 if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)) {
406 // check if ship if flagged as invisible
407 if (shipp->flags & SF_HIDDEN_FROM_SENSORS) {
411 int ship_num = shipp - Ships;
412 Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
414 switch (shipp->team) {
416 Team_friendly_visibility[ship_num] = 1;
417 friendly_ships[friendly_count++] = ship_num;
421 Team_hostile_visibility[ship_num] = 1;
422 hostile_ships[hostile_count++] = ship_num;
426 Team_neutral_visibility[ship_num] = 1;
427 neutral_ships[neutral_count++] = ship_num;
431 unknown_ships[unknown_count++] = ship_num;
435 traitor_ships[traitor_count++] = ship_num;
443 int team_count[NUM_TEAMS];
444 int *team_ships[NUM_TEAMS];
445 ubyte *team_visibility[NUM_TEAMS];
446 int *cur_team_ships, *en_team_ships;
448 // set up variable for loop
449 // team count is number of ship of each team type
450 team_count[0] = friendly_count;
451 team_count[1] = hostile_count;
452 team_count[2] = neutral_count;
453 team_count[3] = unknown_count;
454 team_count[4] = traitor_count;
456 // team ships is array of ship_nums of ships from each team
457 team_ships[0] = friendly_ships;
458 team_ships[1] = hostile_ships;
459 team_ships[2] = neutral_ships;
460 team_ships[3] = unknown_ships;
461 team_ships[4] = traitor_ships;
463 // this is the result, visiblity for each team indexed by ship_num
464 team_visibility[0] = Team_friendly_visibility;
465 team_visibility[1] = Team_hostile_visibility;
466 team_visibility[2] = Team_neutral_visibility;
467 team_visibility[3] = NULL;
468 team_visibility[4] = NULL;
470 int idx, en_idx, cur_count, en_count, en_team;
472 // Do for friendly, neutral and hostile (only teams that cooperate with visibility)
473 for (int cur_team=0; cur_team<3; cur_team++) {
474 // set up current team
475 cur_count = team_count[cur_team];
476 cur_team_ships = team_ships[cur_team];
478 // short circuit if team has no presence
479 if (cur_count == 0) {
483 // check agains both enemy teams
484 for (int en_team_inc=1; en_team_inc<NUM_TEAMS; en_team_inc++) {
486 en_team = (cur_team + en_team_inc) % NUM_TEAMS; // enemy_team (index) is cur_team + (1-4), wrapped back to range 0-4
487 en_count = team_count[en_team];
488 en_team_ships = team_ships[en_team];
490 // check if current team can see enemy team's ships
491 for (en_idx=0; en_idx<en_count; en_idx++) {
492 // for each ship on other team
493 for (idx=0; idx<cur_count; idx++) {
494 // ignore nav buoys and cargo containers
495 if(Ship_info[Ships[cur_team_ships[idx]].ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY)){
499 // check against each ship on my team(and AWACS only once)
500 if ( awacs_get_level(&Objects[Ships[en_team_ships[en_idx]].objnum], &Ships[cur_team_ships[idx]], idx==0) > 1) {
501 team_visibility[cur_team][en_team_ships[en_idx]] = 1;
504 } //end cur_count (ship on current team looking at en_team)
511 // Determine is ship is visible by team
512 int ship_is_visible_by_team(int ship_num, int team)
514 Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
518 return Team_friendly_visibility[ship_num];
522 return Team_hostile_visibility[ship_num];
526 return Team_neutral_visibility[ship_num];