2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Ship/AWACS.cpp $
15 * all sorts of cool stuff about ships
18 * Revision 1.3 2002/06/09 04:41:26 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:52 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 13 8/25/99 10:50a Dave
29 * Added music to the mainhall.tbl
31 * 12 7/19/99 12:02p Andsager
32 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
33 * only blow up subsystem if its strength is > 0
35 * 11 7/06/99 10:45a Andsager
36 * Modify engine wash to work on any ship that is not small. Add AWACS
39 * 10 6/17/99 1:41p Andsager
40 * Added comments to team_visibility_update
42 * 9 6/15/99 4:01p Jamesa
43 * Don't count navbuoys and cargo containers towards team visibilitiy.
45 * 8 6/02/99 12:52p Andsager
46 * Added team-wide ship visibility. Implemented for player.
48 * 7 5/28/99 3:14p Andsager
49 * Modify nebula scan range by species.
51 * 6 5/28/99 9:39a Andsager
52 * Modify awacs to take account for huge ships.
54 * 5 5/12/99 2:55p Andsager
55 * Implemented level 2 tag as priority in turret object selection
57 * 4 1/25/99 2:49p Anoop
58 * Put in more sanity checks when checking for AWACS sources.
60 * 3 1/25/99 5:03a Dave
61 * First run of stealth, AWACS and TAG missile support. New mission type
64 * 2 1/08/99 2:08p Dave
65 * Fixed software rendering for pofview. Super early support for AWACS and
77 #include "missionparse.h"
80 // ----------------------------------------------------------------------------------------------------
84 // timestamp for updating AWACS stuff
85 #define AWACS_STAMP_TIME 1000
88 // total awacs levels for all teams
89 float Awacs_team[MAX_TEAMS]; // total AWACS capabilities for each team
90 float Awacs_level; // Awacs_friendly - Awacs_hostile
92 // list of all AWACS sources
94 typedef struct awacs_entry {
99 awacs_entry Awacs[MAX_AWACS];
102 // species dependent factor increasing scan range in nebula
103 #define AWACS_SHIVAN_MULT 1.50
104 #define AWACS_VASUDAN_MULT 1.25
105 #define AWACS_TERRAN_MULT 1.00
107 // TEAM SHIP VISIBILITY
108 // team-wide shared visibility info
109 // at start of each frame (maybe timestamp), compute visibility
110 ubyte Team_friendly_visibility[MAX_SHIPS];
111 ubyte Team_neutral_visibility[MAX_SHIPS];
112 ubyte Team_hostile_visibility[MAX_SHIPS];
113 // no shared team visibility info for TEAM_UNKNOWN or TEAM_TRAITOR
115 // ----------------------------------------------------------------------------------------------------
116 // AWACS FORWARD DECLARATIONS
119 // update the total awacs levels
120 void awacs_update_all_levels();
122 // update team visibility info
123 void team_visibility_update();
126 // ----------------------------------------------------------------------------------------------------
130 // call when initializing level, before parsing mission
131 void awacs_level_init()
133 // set the update timestamp to -1
137 // call every frame to process AWACS details
140 // if we need to update total AWACS levels, do so now
141 if((Awacs_stamp == -1) || timestamp_elapsed(Awacs_stamp)){
142 // reset the timestamp
143 Awacs_stamp = timestamp(AWACS_STAMP_TIME);
145 // recalculate everything
146 awacs_update_all_levels();
148 // update team visibility
149 team_visibility_update();
154 // ----------------------------------------------------------------------------------------------------
155 // AWACS FORWARD DEFINITIONS
158 // update the total awacs levels
159 void awacs_update_all_levels()
163 ship_subsys *ship_system;
168 for(idx=0; idx<MAX_TEAMS; idx++){
169 Awacs_team[idx] = 0.0f;
174 // we need to traverse all subsystems on all ships
175 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
176 // make sure its a valid ship
177 if((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)){
181 // get a handle to the ship
182 shipp = &Ships[Objects[moveup->objnum].instance];
184 // ignore dying, departing, or arriving ships
185 if((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)){
189 // only look at ships that have awacs subsystems
190 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS) ) {
194 // traverse all subsystems
195 for(ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system)){
196 // if this is an AWACS subsystem
197 if((ship_system->system_info != NULL) && (ship_system->system_info->flags & MSS_FLAG_AWACS)){
198 // add the intensity to the team total
199 Awacs_team[shipp->team] += ship_system->awacs_intensity * (ship_system->current_hits / ship_system->system_info->max_hits);
201 // add an Awacs source
202 if(Awacs_count < MAX_AWACS){
203 Awacs[Awacs_count].subsys = ship_system;
204 Awacs[Awacs_count].team = shipp->team;
205 Awacs[Awacs_count].objp = &Objects[moveup->objnum];
213 Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
218 for(idx=0; idx<MAX_TEAMS; idx++){
219 nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
221 nprintf(("General", "AWACS level == %f\n", Awacs_level));
226 // get the total AWACS level for target to viewer
227 // < 0.0f : untargetable
228 // 0.0 - 1.0f : marginally targetable
229 // >= 1.0f : fully targetable as normal
230 float awacs_get_level(object *target, ship *viewer, int use_awacs)
232 vector dist_vec, subsys_pos;
233 // float closest = 0.0f;
235 int closest_index = -1;
239 // if the viewer is me, and I'm a multiplayer observer, its always viewable
240 if((viewer == Player_ship) && (Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
244 // ships on the same teamare always viewable
245 if((target->type == OBJ_SHIP) && (Ships[target->instance].team == viewer->team)){
249 int stealth_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
250 int nebula_mission = (The_mission.flags & MISSION_FLAG_FULLNEB);
251 int check_huge_ship = (target->type == OBJ_SHIP) && (target->instance >= 0) && (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_HUGE_SHIP);
253 // only check for Awacs if stealth ship or Nebula mission
254 // determine the closest friendly awacs
255 if ((stealth_ship || nebula_mission) && use_awacs) {
256 for (idx=0; idx<Awacs_count; idx++) {
257 // if not on the same team as the viewer
258 if(Awacs[idx].team != viewer->team){
262 // if this awacs source has somehow become invalid
263 if(Awacs[idx].objp->type != OBJ_SHIP){
267 // get the subsystem position
268 if(!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)){
272 // determine if its the closest
273 // special case for HUGE_SHIPS
274 if ( check_huge_ship ) {
275 // check if inside bbox expanded by awacs_radius
276 if (check_world_pt_in_expanded_ship_bbox(&subsys_pos, target, Awacs[idx].subsys->awacs_radius)) {
283 // get distance from Subsys to target
284 vm_vec_sub(&dist_vec, &subsys_pos, &target->pos);
285 test = vm_vec_mag_quick(&dist_vec);
287 if (test > Awacs[idx].subsys->awacs_radius) {
290 // if ((closest_index == -1) || (test < closest)) {
299 // check for a tagged ship. TAG'd ships are _always_ visible
300 if(target->type == OBJ_SHIP){
301 shipp = &Ships[target->instance];
302 if(shipp->tag_left > 0.0f || shipp->level2_tag_left > 0.0f){
307 // if this is a stealth ship
309 // if the ship is within range of an awacs
310 if(closest_index != -1){
311 // if the nebula effect is active, stealth ships are only partially targetable
312 if ( nebula_mission ) {
316 // otherwise its targetable
319 // otherwise its completely hidden
326 // if this is not a nebula mission, its always targetable
327 if( !nebula_mission ){
331 // if the ship is within range of an awacs, its fully targetable
332 if(closest_index != -1){
336 // fully targetable at half the nebula value
337 // modify distance by species
338 float scan_nebula_range = Neb2_awacs;
339 switch (Ship_info[viewer->ship_info_index].species) {
341 scan_nebula_range *= float(AWACS_SHIVAN_MULT);
344 case SPECIES_VASUDAN:
345 scan_nebula_range *= float(AWACS_VASUDAN_MULT);
349 scan_nebula_range *= float(AWACS_TERRAN_MULT);
356 // special case for huge ship - check inside expanded bounding boxes
357 if ( check_huge_ship ) {
358 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, scan_nebula_range)) {
359 if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, 0.5f*scan_nebula_range)) {
365 // otherwise check straight up nebula numbers
367 vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos);
368 test = vm_vec_mag_quick(&dist_vec);
369 if(test < (0.5f * scan_nebula_range)){
371 } else if(test < scan_nebula_range){
376 // untargetable at longer range
382 // update team visibility
384 void team_visibility_update()
386 int friendly_count, hostile_count, neutral_count, unknown_count, traitor_count, num_stealth;
387 int friendly_ships[MAX_SHIPS];
388 int hostile_ships[MAX_SHIPS];
389 int neutral_ships[MAX_SHIPS];
390 int unknown_ships[MAX_SHIPS];
391 int traitor_ships[MAX_SHIPS];
395 friendly_count = hostile_count = neutral_count = unknown_count = traitor_count = num_stealth = 0;
397 // zero out visibility for each team
398 memset(Team_friendly_visibility, 0, sizeof(Team_friendly_visibility));
399 memset(Team_hostile_visibility, 0, sizeof(Team_hostile_visibility));
400 memset(Team_neutral_visibility, 0, sizeof(Team_neutral_visibility));
402 // Go through list of ships and mark those visible for given team
403 for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup)) {
404 // make sure its a valid ship
405 if ((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0)) {
409 // get a handle to the ship
410 shipp = &Ships[Objects[moveup->objnum].instance];
412 // ignore dying, departing, or arriving ships
413 if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)) {
417 // check if ship if flagged as invisible
418 if (shipp->flags & SF_HIDDEN_FROM_SENSORS) {
422 int ship_num = shipp - Ships;
423 SDL_assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
425 switch (shipp->team) {
427 Team_friendly_visibility[ship_num] = 1;
428 friendly_ships[friendly_count++] = ship_num;
432 Team_hostile_visibility[ship_num] = 1;
433 hostile_ships[hostile_count++] = ship_num;
437 Team_neutral_visibility[ship_num] = 1;
438 neutral_ships[neutral_count++] = ship_num;
442 unknown_ships[unknown_count++] = ship_num;
446 traitor_ships[traitor_count++] = ship_num;
454 int team_count[NUM_TEAMS];
455 int *team_ships[NUM_TEAMS];
456 ubyte *team_visibility[NUM_TEAMS];
457 int *cur_team_ships, *en_team_ships;
459 // set up variable for loop
460 // team count is number of ship of each team type
461 team_count[0] = friendly_count;
462 team_count[1] = hostile_count;
463 team_count[2] = neutral_count;
464 team_count[3] = unknown_count;
465 team_count[4] = traitor_count;
467 // team ships is array of ship_nums of ships from each team
468 team_ships[0] = friendly_ships;
469 team_ships[1] = hostile_ships;
470 team_ships[2] = neutral_ships;
471 team_ships[3] = unknown_ships;
472 team_ships[4] = traitor_ships;
474 // this is the result, visiblity for each team indexed by ship_num
475 team_visibility[0] = Team_friendly_visibility;
476 team_visibility[1] = Team_hostile_visibility;
477 team_visibility[2] = Team_neutral_visibility;
478 team_visibility[3] = NULL;
479 team_visibility[4] = NULL;
481 int idx, en_idx, cur_count, en_count, en_team;
483 // Do for friendly, neutral and hostile (only teams that cooperate with visibility)
484 for (int cur_team=0; cur_team<3; cur_team++) {
485 // set up current team
486 cur_count = team_count[cur_team];
487 cur_team_ships = team_ships[cur_team];
489 // short circuit if team has no presence
490 if (cur_count == 0) {
494 // check agains both enemy teams
495 for (int en_team_inc=1; en_team_inc<NUM_TEAMS; en_team_inc++) {
497 en_team = (cur_team + en_team_inc) % NUM_TEAMS; // enemy_team (index) is cur_team + (1-4), wrapped back to range 0-4
498 en_count = team_count[en_team];
499 en_team_ships = team_ships[en_team];
501 // check if current team can see enemy team's ships
502 for (en_idx=0; en_idx<en_count; en_idx++) {
503 // for each ship on other team
504 for (idx=0; idx<cur_count; idx++) {
505 // ignore nav buoys and cargo containers
506 if(Ship_info[Ships[cur_team_ships[idx]].ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY)){
510 // check against each ship on my team(and AWACS only once)
511 if ( awacs_get_level(&Objects[Ships[en_team_ships[en_idx]].objnum], &Ships[cur_team_ships[idx]], idx==0) > 1) {
512 team_visibility[cur_team][en_team_ships[en_idx]] = 1;
515 } //end cur_count (ship on current team looking at en_team)
522 // Determine is ship is visible by team
523 int ship_is_visible_by_team(int ship_num, int team)
525 SDL_assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
529 return Team_friendly_visibility[ship_num];
533 return Team_hostile_visibility[ship_num];
537 return Team_neutral_visibility[ship_num];