]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/awacs.cpp
deal with static analyzer warnings (part 2)
[taylor/freespace2.git] / src / ship / awacs.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AWACS.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * all sorts of cool stuff about ships
16  *
17  * $Log$
18  * Revision 1.3  2002/06/09 04:41:26  relnev
19  * added copyright header
20  *
21  * Revision 1.2  2002/05/07 03:16:52  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:10  root
25  * Initial import.
26  *
27  * 
28  * 13    8/25/99 10:50a Dave
29  * Added music to the mainhall.tbl
30  * 
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
34  * 
35  * 11    7/06/99 10:45a Andsager
36  * Modify engine wash to work on any ship that is not small.  Add AWACS
37  * ask for help.
38  * 
39  * 10    6/17/99 1:41p Andsager
40  * Added comments to team_visibility_update
41  * 
42  * 9     6/15/99 4:01p Jamesa
43  * Don't count navbuoys and cargo containers towards team visibilitiy.
44  * 
45  * 8     6/02/99 12:52p Andsager
46  * Added team-wide ship visibility.  Implemented for player.
47  * 
48  * 7     5/28/99 3:14p Andsager
49  * Modify nebula scan range by species.
50  * 
51  * 6     5/28/99 9:39a Andsager
52  * Modify awacs to take account for huge ships.
53  * 
54  * 5     5/12/99 2:55p Andsager
55  * Implemented level 2 tag as priority in turret object selection
56  * 
57  * 4     1/25/99 2:49p Anoop
58  * Put in more sanity checks when checking for AWACS sources.
59  * 
60  * 3     1/25/99 5:03a Dave
61  * First run of stealth, AWACS and TAG missile support. New mission type
62  * :)
63  * 
64  * 2     1/08/99 2:08p Dave
65  * Fixed software rendering for pofview. Super early support for AWACS and
66  * beam weapons.
67  * 
68  * 
69  * $NoKeywords: $
70  */
71
72 #include "timer.h"
73 #include "ship.h"
74 #include "linklist.h"
75 #include "neb.h"
76 #include "awacs.h"
77 #include "missionparse.h"
78 #include "multi.h"
79
80 // ----------------------------------------------------------------------------------------------------
81 // AWACS DEFINES/VARS
82 //
83
84 // timestamp for updating AWACS stuff
85 #define AWACS_STAMP_TIME                        1000
86 int Awacs_stamp = -1;
87
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
91
92 // list of all AWACS sources
93 #define MAX_AWACS                                       30
94 typedef struct awacs_entry {
95         int team;
96         ship_subsys *subsys;
97         object *objp;
98 } awacs_entry;
99 awacs_entry Awacs[MAX_AWACS];
100 int Awacs_count = 0;
101
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
106
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
114
115 // ----------------------------------------------------------------------------------------------------
116 // AWACS FORWARD DECLARATIONS
117 //
118
119 // update the total awacs levels
120 void awacs_update_all_levels();
121
122 // update team visibility info
123 void team_visibility_update();
124
125
126 // ----------------------------------------------------------------------------------------------------
127 // AWACS FUNCTIONS
128 //
129
130 // call when initializing level, before parsing mission
131 void awacs_level_init()
132 {
133         // set the update timestamp to -1 
134         Awacs_stamp = -1;
135 }
136
137 // call every frame to process AWACS details
138 void awacs_process()
139 {
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);
144
145                 // recalculate everything
146                 awacs_update_all_levels();
147
148                 // update team visibility
149                 team_visibility_update();
150         }
151 }
152
153
154 // ----------------------------------------------------------------------------------------------------
155 // AWACS FORWARD DEFINITIONS
156 //
157
158 // update the total awacs levels
159 void awacs_update_all_levels()
160 {
161         ship_obj *moveup;       
162         ship *shipp;
163         ship_subsys *ship_system;
164         int idx;
165
166         // zero all levels
167         Awacs_level = 0.0f;
168         for(idx=0; idx<MAX_TEAMS; idx++){
169                 Awacs_team[idx] = 0.0f;
170         }
171
172         Awacs_count = 0;
173
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)){
178                         continue;
179                 }       
180                 
181                 // get a handle to the ship
182                 shipp = &Ships[Objects[moveup->objnum].instance];
183
184                 // ignore dying, departing, or arriving ships
185                 if((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)){
186                         continue;
187                 }
188
189                 // only look at ships that have awacs subsystems
190                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS) ) {
191                         continue;
192                 }
193
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);
200
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];                             
206                                         Awacs_count++;
207                                 }
208                         }
209                 }
210         }
211
212         // awacs level
213         Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
214
215         // spew all the info
216 #ifndef NDEBUG 
217         /*
218         for(idx=0; idx<MAX_TEAMS; idx++){
219                 nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
220         }
221         nprintf(("General", "AWACS level == %f\n", Awacs_level));
222         */
223 #endif
224 }
225
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)
231 {               
232         vector dist_vec, subsys_pos;
233 //      float closest = 0.0f;
234         float test;
235         int closest_index = -1;
236         int idx;
237         ship *shipp;    
238
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])){
241                 return 1.5f;
242         }
243
244         // ships on the same teamare always viewable
245         if((target->type == OBJ_SHIP) && (Ships[target->instance].team == viewer->team)){
246                 return 1.5f;
247         }
248
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);
252
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){
259                                 continue;
260                         }
261
262                         // if this awacs source has somehow become invalid
263                         if(Awacs[idx].objp->type != OBJ_SHIP){
264                                 continue;
265                         }
266
267                         // get the subsystem position
268                         if(!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)){
269                                 continue;
270                         }
271
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)) {
277                                         closest_index = idx;
278                                         break;
279                                 }
280
281                         // not a huge ship
282                         } else {
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);
286
287                                 if (test > Awacs[idx].subsys->awacs_radius) {
288                                         continue;
289                                 }
290                         //      if ((closest_index == -1) || (test < closest)) {
291                         //              closest = test;
292                                         closest_index = idx;
293                                         break;
294                         //      }
295                         }
296                 }
297         }
298
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){
303                         return 1.5f;
304                 }
305         }
306         
307         // if this is a stealth ship
308         if( 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 ) {
313                                 return 0.5f;
314                         }
315
316                         // otherwise its targetable
317                         return 1.5f;
318                 } 
319                 // otherwise its completely hidden
320                 else {
321                         return -1.0f;
322                 }
323         }
324         // all other ships
325         else {
326                 // if this is not a nebula mission, its always targetable
327                 if( !nebula_mission ){
328                         return 1.5f;
329                 }
330
331                 // if the ship is within range of an awacs, its fully targetable
332                 if(closest_index != -1){
333                         return 1.5f;
334                 }
335
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) {
340                 case SPECIES_SHIVAN:
341                         scan_nebula_range *= float(AWACS_SHIVAN_MULT);
342                         break;
343
344                 case SPECIES_VASUDAN:
345                         scan_nebula_range *= float(AWACS_VASUDAN_MULT);
346                         break;
347
348                 case SPECIES_TERRAN:
349                         scan_nebula_range *= float(AWACS_TERRAN_MULT);
350                         break;
351
352                 default:
353                         Int3();
354                 }
355
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)) {
360                                         return 1.5f;
361                                 }
362                                 return 0.5f;
363                         }
364                 } 
365                 // otherwise check straight up nebula numbers
366                 else {
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)){
370                                 return 1.5f;
371                         } else if(test < scan_nebula_range){
372                                 return 0.5f;
373                         }
374                 }
375
376                 // untargetable at longer range
377                 return -1.0f;   
378         }               
379 }
380
381
382 // update team visibility
383 #define NUM_TEAMS       5
384 void team_visibility_update()
385 {
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];
392         ship_obj *moveup;
393         ship *shipp;
394
395         friendly_count = hostile_count = neutral_count = unknown_count = traitor_count = num_stealth = 0;
396
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));
401
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)) {
406                         continue;
407                 }       
408                 
409                 // get a handle to the ship
410                 shipp = &Ships[Objects[moveup->objnum].instance];
411
412                 // ignore dying, departing, or arriving ships
413                 if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)) {
414                         continue;
415                 }
416
417                 // check if ship if flagged as invisible
418                 if (shipp->flags & SF_HIDDEN_FROM_SENSORS) {
419                         continue;
420                 }
421
422                 int ship_num = shipp - Ships;
423                 SDL_assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
424
425                 switch (shipp->team) {
426                 case TEAM_FRIENDLY:     
427                         Team_friendly_visibility[ship_num] = 1;
428                         friendly_ships[friendly_count++] = ship_num;
429                         break;
430
431                 case TEAM_HOSTILE:      
432                         Team_hostile_visibility[ship_num] = 1;
433                         hostile_ships[hostile_count++] = ship_num;
434                         break;
435
436                 case TEAM_NEUTRAL:      
437                         Team_neutral_visibility[ship_num] = 1;
438                         neutral_ships[neutral_count++] = ship_num;
439                         break;
440
441                 case TEAM_UNKNOWN:
442                         unknown_ships[unknown_count++] = ship_num;
443                         break;
444
445                 case TEAM_TRAITOR:
446                         traitor_ships[traitor_count++] = ship_num;
447                         break;
448
449                 default:
450                         Int3();
451                 }
452         }
453
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;
458
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;
466
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;
473
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;
480
481         int idx, en_idx, cur_count, en_count, en_team;
482
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];
488
489                 // short circuit if team has no presence
490                 if (cur_count == 0) {
491                         break;
492                 }
493
494                 // check agains both enemy teams
495                 for (int en_team_inc=1; en_team_inc<NUM_TEAMS; en_team_inc++) {
496                         // set up enemy team
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];
500
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)){
507                                                 continue;
508                                         }
509
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;
513                                                 break;
514                                         }
515                                 } //end cur_count (ship on current team looking at en_team)
516                         } // end en_count
517                 } // end en_team_inc
518         } // end cur_team
519 }
520
521
522 // Determine is ship is visible by team
523 int ship_is_visible_by_team(int ship_num, int team)
524 {
525         SDL_assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
526
527         switch (team) {
528         case TEAM_FRIENDLY:
529                 return Team_friendly_visibility[ship_num];
530                 break;
531
532         case TEAM_HOSTILE:
533                 return Team_hostile_visibility[ship_num];
534                 break;
535
536         case TEAM_NEUTRAL:
537                 return Team_neutral_visibility[ship_num];
538                 break;
539
540         case TEAM_UNKNOWN:
541                 return 0;
542                 break;
543
544         case TEAM_TRAITOR:
545                 return 0;
546                 break;
547
548         default:
549                 Int3();
550                 return 0;
551         }
552 }
553
554
555
556                 
557