FS2_Open
Open source remastering of the Freespace 2 engine
awacs.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #include "globalincs/linklist.h"
14 #include "iff_defs/iff_defs.h"
15 #include "io/timer.h"
16 #include "mission/missionparse.h"
17 #include "nebula/neb.h"
18 #include "network/multi.h"
19 #include "ship/awacs.h"
20 #include "ship/ship.h"
22 
23 
24 // ----------------------------------------------------------------------------------------------------
25 // AWACS DEFINES/VARS
26 //
27 
28 // timestamp for updating AWACS stuff
29 #define AWACS_STAMP_TIME 1000
30 int Awacs_stamp = -1;
31 
32 // total awacs levels for all teams
33 float Awacs_team[MAX_IFFS]; // total AWACS capabilities for each team
34 float Awacs_level; // Awacs_friendly - Awacs_hostile
35 
36 // list of all AWACS sources
37 #define MAX_AWACS 30
38 typedef struct awacs_entry {
39  int team;
41  object *objp;
42 } awacs_entry;
44 int Awacs_count = 0;
45 
46 // TEAM SHIP VISIBILITY
47 // team-wide shared visibility info
48 // at start of each frame (maybe timestamp), compute visibility
50 
51 // ----------------------------------------------------------------------------------------------------
52 // AWACS FORWARD DECLARATIONS
53 //
54 
55 // update the total awacs levels
57 
58 // update team visibility info
60 
61 
62 // ----------------------------------------------------------------------------------------------------
63 // AWACS FUNCTIONS
64 //
65 
66 // call when initializing level, before parsing mission
68 {
69  // set the update timestamp to -1
70  Awacs_stamp = -1;
71 }
72 
73 // call every frame to process AWACS details
75 {
76  // if we need to update total AWACS levels, do so now
78  {
79  // reset the timestamp
81 
82  // recalculate everything
84 
85  // update team visibility
87  }
88 }
89 
90 
91 // ----------------------------------------------------------------------------------------------------
92 // AWACS FORWARD DEFINITIONS
93 //
94 
95 // update the total awacs levels
97 {
98  ship_obj *moveup;
99  ship *shipp;
100  ship_subsys *ship_system;
101  int idx;
102 
103  // zero all levels
104  Awacs_level = 0.0f;
105  for (idx=0; idx<MAX_IFFS; idx++)
106  Awacs_team[idx] = 0.0f;
107 
108  Awacs_count = 0;
109 
110  // we need to traverse all subsystems on all ships
111  for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup))
112  {
113  // make sure its a valid ship
114  if ((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0))
115  continue;
116 
117  // get a handle to the ship
118  shipp = &Ships[Objects[moveup->objnum].instance];
119 
120  // ignore dying, departing, or arriving ships
121  if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING))
122  continue;
123 
124  // only look at ships that have awacs subsystems
125  if (!(Ship_info[shipp->ship_info_index].flags & SIF_HAS_AWACS))
126  continue;
127 
128  // traverse all subsystems
129  for (ship_system = GET_FIRST(&shipp->subsys_list); ship_system != END_OF_LIST(&shipp->subsys_list); ship_system = GET_NEXT(ship_system))
130  {
131  // if this is an AWACS subsystem
132  if ((ship_system->system_info != NULL) && (ship_system->system_info->flags & MSS_FLAG_AWACS))
133  {
134  // add the intensity to the team total
135  Awacs_team[shipp->team] += ship_system->awacs_intensity * (ship_system->current_hits / ship_system->max_hits);
136 
137  // add an Awacs source
138  if (Awacs_count < MAX_AWACS)
139  {
140  Awacs[Awacs_count].subsys = ship_system;
141  Awacs[Awacs_count].team = shipp->team;
142  Awacs[Awacs_count].objp = &Objects[moveup->objnum];
143  Awacs_count++;
144  }
145  }
146  }
147  }
148 
149  // Goober5000 - Awacs_level isn't used anywhere that I can see
150  // awacs level
151  //Awacs_level = Awacs_team[TEAM_FRIENDLY] - Awacs_team[TEAM_HOSTILE];
152 
153  // spew all the info
154 #ifndef NDEBUG
155  /*
156  for (idx=0; idx<MAX_TVT_TEAMS; idx++){
157  nprintf(("General", "Team %d AWACS == %f\n", idx, Awacs_team[idx]));
158  }
159  nprintf(("General", "AWACS level == %f\n", Awacs_level));
160  */
161 #endif
162 }
163 
164 // get the total AWACS level for target to viewer
165 // < 0.0f : untargetable
166 // 0.0 - 1.0f : marginally targetable
167 // >= 1.0f : fully targetable as normal
168 float awacs_get_level(object *target, ship *viewer, int use_awacs)
169 {
170  Assert(target); // Goober5000
171  Assert(viewer); // Goober5000
172 
173  vec3d dist_vec, subsys_pos;
174  float closest = 0.0f;
175  float test;
176  int closest_index = -1;
177  int idx, stealth_ship = 0, check_huge_ship = 0, friendly_stealth_invisible = 0;
178  ship *shipp = NULL;
179  ship_info *sip = NULL;
180 
181  int viewer_has_primitive_sensors = (viewer->flags2 & SF2_PRIMITIVE_SENSORS);
182 
183  // calc distance from viewer to target
184  vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos);
185  int distance = (int) vm_vec_mag_quick(&dist_vec);
186 
187 // redone by Goober5000
188 #define ALWAYS_TARGETABLE 1.5f
189 #define MARGINALLY_TARGETABLE 0.5f
190 #define UNTARGETABLE -1.0f
191 #define FULLY_TARGETABLE (viewer_has_primitive_sensors ? ((distance < viewer->primitive_sensor_range) ? MARGINALLY_TARGETABLE : UNTARGETABLE) : ALWAYS_TARGETABLE)
192 
193  // if the viewer is me, and I'm a multiplayer observer, its always viewable
195  return ALWAYS_TARGETABLE;
196 
197  // check the targeting threshold
198  if ((Hud_max_targeting_range > 0) && (distance > Hud_max_targeting_range)) {
199  return UNTARGETABLE;
200  }
201 
202  if (target->type == OBJ_SHIP) {
203  // if no valid target then bail as never viewable
204  if (target->instance < 0)
205  return UNTARGETABLE;
206 
207  // Goober5000
208  shipp = &Ships[target->instance];
209  sip = &Ship_info[shipp->ship_info_index];
210  stealth_ship = (shipp->flags2 & SF2_STEALTH);
211  friendly_stealth_invisible = (shipp->flags2 & SF2_FRIENDLY_STEALTH_INVIS);
212 
213  check_huge_ship = (sip->flags & SIF_HUGE_SHIP);
214  }
215 
216  int nebula_enabled = (The_mission.flags & MISSION_FLAG_FULLNEB);
217 
218  // ships on the same team are always viewable
219  if ((target->type == OBJ_SHIP) && (shipp->team == viewer->team))
220  {
221  // not necessarily now! -- Goober5000
222  if ( !(stealth_ship && friendly_stealth_invisible) )
223  return FULLY_TARGETABLE;
224  }
225 
226  // check for a tagged ship. TAG'd ships are _always_ visible
227  if (target->type == OBJ_SHIP)
228  {
229  Assert( shipp != NULL );
230  if (shipp->tag_left > 0.0f || shipp->level2_tag_left > 0.0f)
231  return FULLY_TARGETABLE;
232  }
233 
234  // only check for Awacs if stealth ship or Nebula mission
235  // determine the closest awacs on our team
236  if ((stealth_ship || nebula_enabled) && use_awacs)
237  {
238  for (idx=0; idx<Awacs_count; idx++)
239  {
240  // if not on the same team as the viewer
241  if (Awacs[idx].team != viewer->team)
242  continue;
243 
244  // if this awacs source has somehow become invalid
245  if (Awacs[idx].objp->type != OBJ_SHIP)
246  continue;
247 
248  // get the subsystem position
249  if (!get_subsystem_pos(&subsys_pos, Awacs[idx].objp, Awacs[idx].subsys))
250  continue;
251 
252  // determine if its the closest
253  // special case for HUGE_SHIPS
254  if (check_huge_ship)
255  {
256  // check if inside bbox expanded by awacs_radius
257  if (check_world_pt_in_expanded_ship_bbox(&subsys_pos, target, Awacs[idx].subsys->awacs_radius))
258  {
259  closest_index = idx;
260  break;
261  }
262  }
263  // not a huge ship
264  else
265  {
266  // get distance from Subsys to target
267  vm_vec_sub(&dist_vec, &subsys_pos, &target->pos);
268  test = vm_vec_mag_quick(&dist_vec);
269 
270  if (test > Awacs[idx].subsys->awacs_radius)
271  continue;
272 
273  if ((closest_index == -1) || (test < closest))
274  {
275  closest = test;
276  closest_index = idx;
277  break;
278  }
279  }
280  }
281  }
282 
283  // if this is a stealth ship
284  if (stealth_ship)
285  {
286  // if the ship is within range of an awacs
287  if (closest_index >= 0)
288  {
289  // if the nebula effect is active, stealth ships are only partially targetable
290  if (nebula_enabled)
291  return MARGINALLY_TARGETABLE;
292 
293  // otherwise it's targetable
294  return FULLY_TARGETABLE;
295  }
296  // otherwise its completely hidden
297  else
298  {
299  return UNTARGETABLE;
300  }
301  }
302  // all other ships
303  else
304  {
305  // if this is not a nebula mission, its always targetable
306  if (!nebula_enabled)
307  return FULLY_TARGETABLE;
308 
309  // if the ship is within range of an awacs, its fully targetable
310  if (closest_index >= 0)
311  return FULLY_TARGETABLE;
312 
313 
314  // fully targetable at half the nebula value
315 
316  // modify distance by species
317  float scan_nebula_range = Neb2_awacs * Species_info[Ship_info[viewer->ship_info_index].species].awacs_multiplier;
318 
319  // special case for huge ship - check inside expanded bounding boxes
320  if (check_huge_ship)
321  {
322  if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, scan_nebula_range))
323  {
324  if (check_world_pt_in_expanded_ship_bbox(&Objects[viewer->objnum].pos, target, 0.5f * scan_nebula_range))
325  return FULLY_TARGETABLE;
326 
327  return MARGINALLY_TARGETABLE;
328  }
329  }
330  // otherwise check straight up nebula numbers
331  else
332  {
333  vm_vec_sub(&dist_vec, &target->pos, &Objects[viewer->objnum].pos);
334  test = vm_vec_mag_quick(&dist_vec);
335 
336  if (test < (0.5f * scan_nebula_range))
337  return FULLY_TARGETABLE;
338  else if (test < scan_nebula_range)
339  return MARGINALLY_TARGETABLE;
340  }
341 
342  // untargetable at longer range
343  return UNTARGETABLE;
344  }
345 }
346 
347 
348 // update team visibility
350 {
351  int team_count[MAX_IFFS];
352  int team_ships[MAX_IFFS][MAX_SHIPS];
353 
354  ship_obj *moveup;
355  ship *shipp;
356 
357  // zero out stuff for each team
358  memset(team_count, 0, MAX_IFFS * sizeof(int));
359  memset(Ship_visibility_by_team, 0, MAX_IFFS * MAX_SHIPS * sizeof(ubyte));
360 
361  // Go through list of ships and mark those visible for their own team
362  for (moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup))
363  {
364  // make sure its a valid ship
365  if ((Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0))
366  continue;
367 
368  // get a handle to the ship
369  int ship_num = Objects[moveup->objnum].instance;
370  shipp = &Ships[ship_num];
371 
372  // ignore dying, departing, or arriving ships
373  if ((shipp->flags & SF_DYING) || (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_ARRIVING))
374  continue;
375 
376  // check if ship is flagged as invisible
377  if (shipp->flags & SF_HIDDEN_FROM_SENSORS)
378  continue;
379 
380  // check if ship is stealthed and friendly-invisible
381  if ((shipp->flags2 & SF2_STEALTH) && (shipp->flags2 & SF2_FRIENDLY_STEALTH_INVIS))
382  continue;
383 
384  Ship_visibility_by_team[shipp->team][ship_num] = 1;
385  team_ships[shipp->team][team_count[shipp->team]] = ship_num;
386  team_count[shipp->team]++;
387  }
388 
389  int idx, en_idx, cur_count, en_count;
390  int *cur_team_ships, *en_team_ships;
391 
392  // Do for all teams that cooperate with visibility
393  for (int cur_team = 0; cur_team < MAX_IFFS; cur_team++)
394  {
395  // set up current team
396  cur_count = team_count[cur_team];
397  cur_team_ships = team_ships[cur_team];
398 
399  // short circuit if team has no presence
400  if (cur_count == 0)
401  continue; // Goober5000 10/06/2005 changed from break; probably a bug
402 
403 
404  // check against all enemy teams
405  for (int en_team = 0; en_team < MAX_IFFS; en_team++)
406  {
407  // NOTE: we no longer skip our own team because we must adjust visibility for friendly-stealth-invisible ships
408  // if (en_team == cur_team)
409  // continue;
410 
411  // set up enemy team
412  en_count = team_count[en_team];
413  en_team_ships = team_ships[en_team];
414 
415  // check if current team can see enemy team's ships
416  for (en_idx = 0; en_idx < en_count; en_idx++)
417  {
418  // for each ship on other team
419  for (idx = 0; idx < cur_count; idx++)
420  {
421  // ignore nav buoys and cargo containers
422  if (Ship_info[Ships[cur_team_ships[idx]].ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY))
423  {
424  continue;
425  }
426 
427  // check against each ship on my team (and AWACS only once)
428  if (awacs_get_level(&Objects[Ships[en_team_ships[en_idx]].objnum], &Ships[cur_team_ships[idx]], (idx == 0)) > 1.0f)
429  {
430  Ship_visibility_by_team[cur_team][en_team_ships[en_idx]] = 1;
431  break;
432  }
433  }
434  }
435  }
436  }
437 }
438 
439 
440 // Determine is ship is visible by team
441 // Goober5000 - now accounts for primitive sensors
442 int ship_is_visible_by_team(object *target, ship *viewer)
443 {
444  Assert(target);
445  Assert(viewer);
446  Assert(target->type == OBJ_SHIP);
447 
448  // not visible if viewer has primitive sensors
449  if (viewer->flags2 & SF2_PRIMITIVE_SENSORS)
450  return 0;
451 
452  // not visible if out of range
454  return 0;
455 
456  // now evaluate this the old way
457  int ship_num = target->instance;
458  int team = viewer->team;
459 
460  return (int)Ship_visibility_by_team[team][ship_num];
461 }
int timestamp(int delta_ms)
Definition: timer.cpp:226
#define MY_NET_PLAYER_NUM
Definition: multi.h:127
model_subsystem * system_info
Definition: ship.h:314
int team
Definition: ship.h:606
float vm_vec_mag_quick(const vec3d *v)
Definition: vecmat.cpp:371
float awacs_intensity
Definition: ship.h:359
int objnum
Definition: ship.h:537
int Game_mode
Definition: systemvars.cpp:24
#define MISSION_FLAG_FULLNEB
Definition: missionparse.h:70
ubyte Ship_visibility_by_team[MAX_IFFS][MAX_SHIPS]
Definition: awacs.cpp:49
net_player * Net_player
Definition: multi.cpp:94
#define SIF_HAS_AWACS
Definition: ship.h:906
#define MAX_SHIPS
Definition: globals.h:37
float max_hits
Definition: ship.h:320
Assert(pm!=NULL)
Definition: pstypes.h:88
float level2_tag_left
Definition: ship.h:728
int team
Definition: awacs.cpp:39
#define SIF_CARGO
Definition: ship.h:884
CButton * team
#define FULLY_TARGETABLE
GLclampf f
Definition: Glext.h:7097
#define SF2_FRIENDLY_STEALTH_INVIS
Definition: ship.h:484
#define SF_ARRIVING
Definition: ship.h:453
uint flags
Definition: ship.h:644
float Awacs_team[MAX_IFFS]
Definition: awacs.cpp:33
object * objp
Definition: lua.cpp:3105
ship * shipp
Definition: lua.cpp:9162
vec3d pos
Definition: object.h:152
awacs_entry Awacs[MAX_AWACS]
Definition: awacs.cpp:43
#define MSS_FLAG_AWACS
Definition: model.h:111
float Neb2_awacs
Definition: neb.cpp:156
int objnum
Definition: ship.h:1483
ship_subsys subsys_list
Definition: ship.h:630
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
int instance
Definition: object.h:150
void awacs_update_all_levels()
Definition: awacs.cpp:96
#define SIF_NAVBUOY
Definition: ship.h:891
uint flags
Definition: model.h:169
#define GM_MULTIPLAYER
Definition: systemvars.h:18
int flags
Definition: ship.h:1227
#define AWACS_STAMP_TIME
Definition: awacs.cpp:29
#define MARGINALLY_TARGETABLE
#define SF_HIDDEN_FROM_SENSORS
Definition: ship.h:464
#define MULTI_OBSERVER(np)
Definition: multi.h:140
struct awacs_entry awacs_entry
ship_subsys * subsys
Definition: awacs.cpp:40
float current_hits
Definition: ship.h:319
Definition: ship.h:534
int idx
Definition: multiui.cpp:761
void team_visibility_update()
Definition: awacs.cpp:349
int get_subsystem_pos(vec3d *pos, object *objp, ship_subsys *subsysp)
Definition: ship.cpp:12975
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
unsigned char ubyte
Definition: pstypes.h:62
#define UNTARGETABLE
int Awacs_stamp
Definition: awacs.cpp:30
ship * Player_ship
Definition: ship.cpp:124
#define ALWAYS_TARGETABLE
float Awacs_level
Definition: awacs.cpp:34
int Hud_max_targeting_range
Definition: hud.cpp:99
#define OBJ_SHIP
Definition: object.h:32
GLbitfield flags
Definition: Glext.h:6722
#define SIF_HUGE_SHIP
Definition: ship.h:945
float awacs_get_level(object *target, ship *viewer, int use_awacs)
Definition: awacs.cpp:168
void vm_vec_sub(vec3d *dest, const vec3d *src0, const vec3d *src1)
Definition: vecmat.cpp:168
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
#define SF_DEPARTING
Definition: ship.h:475
float tag_left
Definition: ship.h:725
float vm_vec_dist_quick(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:417
GLenum target
Definition: Glext.h:6872
ship_obj Ship_obj_list
Definition: ship.cpp:162
int ship_info_index
Definition: ship.h:539
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define timestamp_elapsed(stamp)
Definition: timer.h:102
SCP_vector< species_info > Species_info
object * objp
Definition: awacs.cpp:41
int ship_is_visible_by_team(object *target, ship *viewer)
Definition: awacs.cpp:442
void awacs_process()
Definition: awacs.cpp:74
uint flags2
Definition: ship.h:645
#define MAX_IFFS
Definition: globals.h:34
#define SF_DYING
Definition: ship.h:447
#define SF2_PRIMITIVE_SENSORS
Definition: ship.h:483
#define MAX_AWACS
Definition: awacs.cpp:37
#define SF2_STEALTH
Definition: ship.h:485
mission The_mission
int check_world_pt_in_expanded_ship_bbox(vec3d *world_pt, object *objp, float delta_box)
Definition: ship.cpp:17241
char type
Definition: object.h:146
net_player Net_players[MAX_PLAYERS]
Definition: multi.cpp:93
int Awacs_count
Definition: awacs.cpp:44
void awacs_level_init()
Definition: awacs.cpp:67
Definition: awacs.cpp:38