]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/awacs.cpp
Initial revision
[taylor/freespace2.git] / src / ship / awacs.cpp
1 /*
2  * $Logfile: /Freespace2/code/Ship/AWACS.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * all sorts of cool stuff about ships
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 13    8/25/99 10:50a Dave
15  * Added music to the mainhall.tbl
16  * 
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
20  * 
21  * 11    7/06/99 10:45a Andsager
22  * Modify engine wash to work on any ship that is not small.  Add AWACS
23  * ask for help.
24  * 
25  * 10    6/17/99 1:41p Andsager
26  * Added comments to team_visibility_update
27  * 
28  * 9     6/15/99 4:01p Jamesa
29  * Don't count navbuoys and cargo containers towards team visibilitiy.
30  * 
31  * 8     6/02/99 12:52p Andsager
32  * Added team-wide ship visibility.  Implemented for player.
33  * 
34  * 7     5/28/99 3:14p Andsager
35  * Modify nebula scan range by species.
36  * 
37  * 6     5/28/99 9:39a Andsager
38  * Modify awacs to take account for huge ships.
39  * 
40  * 5     5/12/99 2:55p Andsager
41  * Implemented level 2 tag as priority in turret object selection
42  * 
43  * 4     1/25/99 2:49p Anoop
44  * Put in more sanity checks when checking for AWACS sources.
45  * 
46  * 3     1/25/99 5:03a Dave
47  * First run of stealth, AWACS and TAG missile support. New mission type
48  * :)
49  * 
50  * 2     1/08/99 2:08p Dave
51  * Fixed software rendering for pofview. Super early support for AWACS and
52  * beam weapons.
53  * 
54  * 
55  * $NoKeywords: $
56  */
57
58 #include "timer.h"
59 #include "ship.h"
60 #include "linklist.h"
61 #include "neb.h"
62 #include "awacs.h"
63 #include "missionparse.h"
64 #include "multi.h"
65
66 // ----------------------------------------------------------------------------------------------------
67 // AWACS DEFINES/VARS
68 //
69
70 // timestamp for updating AWACS stuff
71 #define AWACS_STAMP_TIME                        1000
72 int Awacs_stamp = -1;
73
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
77
78 // list of all AWACS sources
79 #define MAX_AWACS                                       30
80 typedef struct awacs_entry {
81         int team;
82         ship_subsys *subsys;
83         object *objp;
84 } awacs_entry;
85 awacs_entry Awacs[MAX_AWACS];
86 int Awacs_count = 0;
87
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
92
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
100
101 // ----------------------------------------------------------------------------------------------------
102 // AWACS FORWARD DECLARATIONS
103 //
104
105 // update the total awacs levels
106 void awacs_update_all_levels();
107
108 // update team visibility info
109 void team_visibility_update();
110
111
112 // ----------------------------------------------------------------------------------------------------
113 // AWACS FUNCTIONS
114 //
115
116 // call when initializing level, before parsing mission
117 void awacs_level_init()
118 {
119         // set the update timestamp to -1 
120         Awacs_stamp = -1;
121 }
122
123 // call every frame to process AWACS details
124 void awacs_process()
125 {
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);
130
131                 // recalculate everything
132                 awacs_update_all_levels();
133
134                 // update team visibility
135                 team_visibility_update();
136         }
137 }
138
139
140 // ----------------------------------------------------------------------------------------------------
141 // AWACS FORWARD DEFINITIONS
142 //
143
144 // update the total awacs levels
145 void awacs_update_all_levels()
146 {
147         ship_obj *moveup;       
148         ship *shipp;
149         ship_subsys *ship_system;
150         int idx;
151
152         // zero all levels
153         Awacs_level = 0.0f;
154         for(idx=0; idx<MAX_TEAMS; idx++){
155                 Awacs_team[idx] = 0.0f;
156         }
157
158         Awacs_count = 0;
159
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)){
164                         continue;
165                 }       
166                 
167                 // get a handle to the ship
168                 shipp = &Ships[Objects[moveup->objnum].instance];
169
170                 // ignore dying, departing, or arriving ships
171                 if((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)){
172                         continue;
173                 }
174
175                 // only look at ships that have awacs subsystems
176                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS) ) {
177                         continue;
178                 }
179
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);
186
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];                             
192                                         Awacs_count++;
193                                 }
194                         }
195                 }
196         }
197
198         // awacs level
199         Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
200
201         // spew all the info
202 #ifndef NDEBUG 
203         /*
204         for(idx=0; idx<MAX_TEAMS; idx++){
205                 nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
206         }
207         nprintf(("General", "AWACS level == %f\n", Awacs_level));
208         */
209 #endif
210 }
211
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)
217 {               
218         vector dist_vec, subsys_pos;
219         float closest = 0.0f;
220         float test;
221         int closest_index = -1;
222         int idx;
223         ship *shipp;    
224
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])){
227                 return 1.5f;
228         }
229
230         // ships on the same teamare always viewable
231         if((target->type == OBJ_SHIP) && (Ships[target->instance].team == viewer->team)){
232                 return 1.5f;
233         }
234
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);
238
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){
245                                 continue;
246                         }
247
248                         // if this awacs source has somehow become invalid
249                         if(Awacs[idx].objp->type != OBJ_SHIP){
250                                 continue;
251                         }
252
253                         // get the subsystem position
254                         if(!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys)){
255                                 continue;
256                         }
257
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)) {
263                                         closest_index = idx;
264                                         break;
265                                 }
266
267                         // not a huge ship
268                         } else {
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);
272
273                                 if (test > Awacs[idx].subsys->awacs_radius) {
274                                         continue;
275                                 }
276                                 if ((closest_index == -1) || (test < closest)) {
277                                         closest = test;
278                                         closest_index = idx;
279                                         break;
280                                 }
281                         }
282                 }
283         }
284
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){
289                         return 1.5f;
290                 }
291         }
292         
293         // if this is a stealth ship
294         if( 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 ) {
299                                 return 0.5f;
300                         }
301
302                         // otherwise its targetable
303                         return 1.5f;
304                 } 
305                 // otherwise its completely hidden
306                 else {
307                         return -1.0f;
308                 }
309         }
310         // all other ships
311         else {
312                 // if this is not a nebula mission, its always targetable
313                 if( !nebula_mission ){
314                         return 1.5f;
315                 }
316
317                 // if the ship is within range of an awacs, its fully targetable
318                 if(closest_index != -1){
319                         return 1.5f;
320                 }
321
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) {
326                 case SPECIES_SHIVAN:
327                         scan_nebula_range *= float(AWACS_SHIVAN_MULT);
328                         break;
329
330                 case SPECIES_VASUDAN:
331                         scan_nebula_range *= float(AWACS_VASUDAN_MULT);
332                         break;
333
334                 case SPECIES_TERRAN:
335                         scan_nebula_range *= float(AWACS_TERRAN_MULT);
336                         break;
337
338                 default:
339                         Int3();
340                 }
341
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)) {
346                                         return 1.5f;
347                                 }
348                                 return 0.5f;
349                         }
350                 } 
351                 // otherwise check straight up nebula numbers
352                 else {
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)){
356                                 return 1.5f;
357                         } else if(test < scan_nebula_range){
358                                 return 0.5f;
359                         }
360                 }
361
362                 // untargetable at longer range
363                 return -1.0f;   
364         }               
365
366         Int3();
367         return 1.5f;
368 }
369
370
371 // update team visibility
372 #define NUM_TEAMS       5
373 void team_visibility_update()
374 {
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];
381         ship_obj *moveup;
382         ship *shipp;
383
384         friendly_count = hostile_count = neutral_count = unknown_count = traitor_count = num_stealth = 0;
385
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));
390
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)) {
395                         continue;
396                 }       
397                 
398                 // get a handle to the ship
399                 shipp = &Ships[Objects[moveup->objnum].instance];
400
401                 // ignore dying, departing, or arriving ships
402                 if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING)) {
403                         continue;
404                 }
405
406                 // check if ship if flagged as invisible
407                 if (shipp->flags & SF_HIDDEN_FROM_SENSORS) {
408                         continue;
409                 }
410
411                 int ship_num = shipp - Ships;
412                 Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
413
414                 switch (shipp->team) {
415                 case TEAM_FRIENDLY:     
416                         Team_friendly_visibility[ship_num] = 1;
417                         friendly_ships[friendly_count++] = ship_num;
418                         break;
419
420                 case TEAM_HOSTILE:      
421                         Team_hostile_visibility[ship_num] = 1;
422                         hostile_ships[hostile_count++] = ship_num;
423                         break;
424
425                 case TEAM_NEUTRAL:      
426                         Team_neutral_visibility[ship_num] = 1;
427                         neutral_ships[neutral_count++] = ship_num;
428                         break;
429
430                 case TEAM_UNKNOWN:
431                         unknown_ships[unknown_count++] = ship_num;
432                         break;
433
434                 case TEAM_TRAITOR:
435                         traitor_ships[traitor_count++] = ship_num;
436                         break;
437
438                 default:
439                         Int3();
440                 }
441         }
442
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;
447
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;
455
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;
462
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;
469
470         int idx, en_idx, cur_count, en_count, en_team;
471
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];
477
478                 // short circuit if team has no presence
479                 if (cur_count == 0) {
480                         break;
481                 }
482
483                 // check agains both enemy teams
484                 for (int en_team_inc=1; en_team_inc<NUM_TEAMS; en_team_inc++) {
485                         // set up enemy team
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];
489
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)){
496                                                 continue;
497                                         }
498
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;
502                                                 break;
503                                         }
504                                 } //end cur_count (ship on current team looking at en_team)
505                         } // end en_count
506                 } // end en_team_inc
507         } // end cur_team
508 }
509
510
511 // Determine is ship is visible by team
512 int ship_is_visible_by_team(int ship_num, int team)
513 {
514         Assert((ship_num >= 0) && (ship_num < MAX_SHIPS));
515
516         switch (team) {
517         case TEAM_FRIENDLY:
518                 return Team_friendly_visibility[ship_num];
519                 break;
520
521         case TEAM_HOSTILE:
522                 return Team_hostile_visibility[ship_num];
523                 break;
524
525         case TEAM_NEUTRAL:
526                 return Team_neutral_visibility[ship_num];
527                 break;
528
529         case TEAM_UNKNOWN:
530                 return 0;
531                 break;
532
533         case TEAM_TRAITOR:
534                 return 0;
535                 break;
536
537         default:
538                 Int3();
539                 return 0;
540         }
541 }
542
543
544
545