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