]> icculus.org git repositories - taylor/freespace2.git/blob - src/object/objcollide.cpp
ryan's struct patch for gcc 2.95
[taylor/freespace2.git] / src / object / objcollide.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/Object/ObjCollide.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Helper routines for all the collision detection functions
16  * Also keeps track of all the object pairs.
17  *
18  * $Log$
19  * Revision 1.4  2002/06/17 06:33:10  relnev
20  * ryan's struct patch for gcc 2.95
21  *
22  * Revision 1.3  2002/06/09 04:41:24  relnev
23  * added copyright header
24  *
25  * Revision 1.2  2002/05/07 03:16:48  theoddone33
26  * The Great Newline Fix
27  *
28  * Revision 1.1.1.1  2002/05/03 03:28:10  root
29  * Initial import.
30  *
31  * 
32  * 18    7/15/99 9:20a Andsager
33  * FS2_DEMO initial checkin
34  * 
35  * 17    6/14/99 3:21p Andsager
36  * Allow collisions between ship and its debris.  Fix up collision pairs
37  * when large ship is warping out.
38  * 
39  * 16    5/08/99 8:25p Dave
40  * Upped object pairs. First run of nebula lightning.
41  * 
42  * 15    4/23/99 12:01p Johnson
43  * Added SIF_HUGE_SHIP
44  * 
45  * 14    4/21/99 6:15p Dave
46  * Did some serious housecleaning in the beam code. Made it ready to go
47  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
48  * a handy macro for recalculating collision pairs for a given object.
49  * 
50  * 13    4/20/99 3:41p Andsager
51  * Modify objects_will_collide to use endpoints
52  * 
53  * 12    3/10/99 6:50p Dave
54  * Changed the way we buffer packets for all clients. Optimized turret
55  * fired packets. Did some weapon firing optimizations.
56  * 
57  * 11    3/05/99 3:32p Dan
58  * Make out of collision pars message npirntf with "collision"
59  * 
60  * 10    1/21/99 2:06p Dave
61  * Final checkin for multiplayer testing.
62  * 
63  * 9     1/21/99 10:44a Dave
64  * More beam weapon stuff. Put in warmdown time.
65  * 
66  * 8     1/15/99 5:01p Dave
67  * Fix for object collision problem.
68  * 
69  * 7     1/12/99 5:45p Dave
70  * Moved weapon pipeline in multiplayer to almost exclusively client side.
71  * Very good results. Bandwidth goes down, playability goes up for crappy
72  * connections. Fixed object update problem for ship subsystems.
73  * 
74  * 6     1/12/99 12:53a Dave
75  * More work on beam weapons - made collision detection very efficient -
76  * collide against all object types properly - made 3 movement types
77  * smooth. Put in test code to check for possible non-darkening pixels on
78  * object textures.
79  * 
80  * 5     11/19/98 11:08p Andsager
81  * Check in of physics and collision detection of rotating submodels
82  * 
83  * 4     11/05/98 5:55p Dave
84  * Big pass at reducing #includes
85  * 
86  * 3     10/26/98 9:42a Dave
87  * Early flak gun support.
88  * 
89  * 2     10/07/98 10:53a Dave
90  * Initial checkin.
91  * 
92  * 1     10/07/98 10:50a Dave
93  * 
94  * 38    7/29/98 3:24p Allender
95  * don't call collision code when either of the object pairs is NULL.
96  * 
97  * 37    4/08/98 8:38a Mike
98  * Make ships avoid player when attacking or guarding a faraway object.
99  * 
100  * 36    4/02/98 11:40a Lawrance
101  * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE
102  * 
103  * 35    4/01/98 1:48p Allender
104  * major changes to ship collision in multiplayer.  Clients now do own
105  * ship/ship collisions (with their own ship only)  Modifed the hull
106  * update packet to be sent quicker when object is target of player.
107  * 
108  * 34    4/01/98 10:57a Mike
109  * Reduce array sizes to save memory.
110  * 
111  * 33    3/31/98 5:18p John
112  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
113  * bunch of debug stuff out of player file.  Made model code be able to
114  * unload models and malloc out only however many models are needed.
115  *  
116  * 
117  * 32    3/06/98 12:45a Lawrance
118  * Rmove a bogus assert in the object pair save/restore
119  * 
120  * 31    3/05/98 3:12p Mike
121  * Fix bugs in asteroid collisions.  Throw asteroids at Galataea (needs to
122  * be made general).  Add skill level support for asteroid throwing.
123  * 
124  * 30    3/05/98 12:12a Mike
125  * Make an asteroid in the way prevent player warpout.
126  * 
127  * 29    2/27/98 4:48p John
128  * Made objects keep track of number of pairs they have associated with
129  * them.  Then, I can early out of the obj_remove_all which was 2.5% of
130  * frametime at beginning of sm2-2 which then is 0% after this.
131  * 
132  * 28    2/26/98 10:07p Hoffoss
133  * Rewrote state saving and restoring to fix bugs and simplify the code.
134  * 
135  * 27    2/23/98 9:00a Andsager
136  * improved back face culling of object:laser pairs to check relative
137  * velocity dot laser forward < 0 vs. v_other dot v_laser < 0
138  * 
139  * 26    2/19/98 4:34p Lawrance
140  * Allow weapon-asteroid collisions
141  * 
142  * 25    2/19/98 10:52a Sandeep
143  * AL: Have asteroids collide with all ships
144  * 
145  * 24    2/12/98 11:45a Andsager
146  * Try to cull debris:weapon collisions before they collisoin pair is
147  * created.  Look at movement in lifetime and whether debris is behind
148  * laser.
149  * 
150  * 23    2/07/98 3:54p John
151  * Added FVI to show_game_resources
152  * 
153  * 22    2/06/98 7:29p John
154  * Made only players ships and player ship weapons do collisions with
155  * asteroids.
156  * 
157  * 21    2/05/98 11:49p Andsager
158  * Don't check laser:ship collisions on same team for small ships if laser
159  * source not player
160  * 
161  * 20    2/05/98 9:21p John
162  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
163  * game.
164  * 
165  * 19    2/05/98 12:51a Mike
166  * Early asteroid stuff.
167  * 
168  * 18    2/03/98 9:40a Andsager
169  * Modify collision object pair creation code so that pairs between debris
170  * and weapons (other than player) are not created.
171  * 
172  * 17    1/28/98 11:06a Andsager
173  * Remove some collision pairs.  Add debug code for displaying collision
174  * pair info.  
175  * 
176  * 16    1/24/98 11:16a Andsager
177  * Remove debug  info.
178  * 
179  * 15    1/24/98 11:09a Andsager
180  * Fixed bug that was removing collision pairs when weapon within the
181  * radius of object.
182  * 
183  * 14    1/23/98 5:07p Andsager
184  * Added tests for object pairs.  (1)  for weapons that do not turn, find
185  * the soonest hit time.  (2)  Check if soonest hit time if after end of
186  * weapon lifetime.
187  * 
188  * 13    1/22/98 6:43p John
189  * Took out debug code.
190  * 
191  * 12    1/22/98 6:42p John
192  * Move game_flush out of debug console into freespace.     Made object
193  * pair code throw out some weapons that turn.   Added stats for how many
194  * object pair are actually checked.
195  * 
196  * 11    1/17/98 10:06p Lawrance
197  * For missiles, only collide if WIF_BOMB flag set.
198  * 
199  * 10    1/14/98 5:21p Allender
200  * system to delete object when buffer is nearly full.  System in place to
201  * delete weapons when nearly out of weapons slots
202  * 
203  * 9     1/13/98 8:09p John
204  * Removed the old collision system that checked all pairs.   Added code
205  * to disable collisions and particles.
206  * 
207  * 8     1/13/98 3:11p Allender
208  * new code to remove old weapons when no more weapon slots available.
209  * Currently not called anywhere pending further testing
210  * 
211  * 7     1/10/98 3:22p Allender
212  * temporary check in to get new code
213  * 
214  * 6     1/10/98 1:14p John
215  * Added explanation to debug console commands
216  * 
217  * 5     1/09/98 9:29a Mike
218  * Enable docked ships to warp out.  Make damage done to a ship not
219  * proportional to its own mass.
220  * 
221  * 4     1/08/98 12:12a Mike
222  * Make ships turn before warping out, if necessary, to avoid a collision.
223  * Warn player if his warpout will collide.  Abort if in stage1.
224  * 
225  * 3     12/21/97 4:33p John
226  * Made debug console functions a class that registers itself
227  * automatically, so you don't need to add the function to
228  * debugfunctions.cpp.  
229  * 
230  * 2     12/16/97 5:23p Allender
231  * don't deal with object collision paris on multiplayer clients
232  * 
233  * 1     9/17/97 2:14p John
234  * Initial revision
235  *
236  * $NoKeywords: $
237  */
238
239 #include "objcollide.h"
240 #include "linklist.h"
241 #include "systemvars.h"
242 #include "timer.h"
243 #include "multi.h"
244 #include "beam.h"
245
246 #ifdef FS2_DEMO
247         #define MAX_PAIRS 3000
248 #else
249         #define MAX_PAIRS 8000  //      Reduced from 10,000 to 6,000 by MK on 4/1/98.
250                                                                         //      Most I saw was 3400 in sm1-06a, the asteriod mission.  No other mission came close.
251 #endif
252
253 // the next 3 variables are used for pair statistics
254 // also in weapon.cpp there is Weapons_created.
255 int Pairs_created = 0;
256 int Num_pairs = 0;
257 int Num_pairs_checked = 0;
258 int pairs_not_created = 0;
259
260 int Num_pairs_hwm = 0;
261
262 obj_pair Obj_pairs[MAX_PAIRS];
263
264 obj_pair pair_used_list;
265 obj_pair pair_free_list;
266
267 void obj_reset_pairs()
268 {
269         int i;
270         
271 //      mprintf(( "Resetting object pairs...\n" ));
272
273         pair_used_list.a = pair_used_list.b = NULL;             
274         pair_used_list.next = NULL;
275         pair_free_list.a = pair_free_list.b = NULL;             
276         pair_free_list.next = &Obj_pairs[0];
277         
278         Num_pairs = 0;
279         for (i=0; i<MAX_PAIRS; i++ )    {
280                 Obj_pairs[i].a = Obj_pairs[i].b = NULL;
281                 Obj_pairs[i].next = &Obj_pairs[i+1];
282         }
283         Obj_pairs[MAX_PAIRS-1].next = NULL;
284 }
285
286 // returns true if we should reject object pair if one is child of other.
287 int reject_obj_pair_on_parent(object *A, object *B)
288 {
289         if (A->type == OBJ_SHIP) {
290                 if (B->type == OBJ_DEBRIS) {
291                         if (B->parent_sig == A->signature) {
292                                 return 0;
293                         }
294                 }
295         }
296
297         if (B->type == OBJ_SHIP) {
298                 if (A->type == OBJ_DEBRIS) {
299                         if (A->parent_sig == B->signature) {
300                                 return 0;
301                         }
302                 }
303         }
304
305         if (A->parent_sig == B->signature) {
306                 return 1;
307         }
308
309         if (B->parent_sig == A->signature) {
310                 return 1;
311         }
312
313         return 0;
314 }
315
316
317 // Adds the pair to the pair list
318 void obj_add_pair( object *A, object *B, int check_time, int add_to_end )
319 {
320         uint ctype;
321         int (*check_collision)( obj_pair *pair );
322         int swapped = 0;        
323         
324         check_collision = NULL;
325
326         if ( A==B ) return;             // Don't check collisions with yourself
327
328         if ( !(A->flags&OF_COLLIDES) ) return;          // This object doesn't collide with anything
329         if ( !(B->flags&OF_COLLIDES) ) return;          // This object doesn't collide with anything
330         
331         // Make sure you're not checking a parent with it's kid or vicy-versy
332 //      if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return;
333 //      if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return;
334         if ( reject_obj_pair_on_parent(A,B) ) {
335                 return;
336         }
337
338         Assert( A->type < 127 );
339         Assert( B->type < 127 );
340
341         ctype = COLLISION_OF(A->type,B->type);
342         switch( ctype ) {
343         case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP):
344                 swapped = 1;
345                 check_collision = collide_ship_weapon;
346                 break;
347         case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON):
348                 check_collision = collide_ship_weapon;
349                 break;
350         case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON):
351                 check_collision = collide_debris_weapon;
352                 break;
353         case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS):
354                 swapped = 1;
355                 check_collision = collide_debris_weapon;
356                 break;
357         case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP):
358                 check_collision = collide_debris_ship;          
359                 break;
360         case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS):
361                 check_collision = collide_debris_ship;
362                 swapped = 1;
363                 break;
364         case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON):
365                 // Only check collision's with player weapons
366 //              if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) {
367                         check_collision = collide_asteroid_weapon;
368 //              }
369                 break;
370         case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID):
371                 swapped = 1;
372                 // Only check collision's with player weapons
373 //              if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) {
374                         check_collision = collide_asteroid_weapon;
375 //              }
376                 break;
377         case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP):
378                 // Only check collisions with player ships
379 //              if ( B->flags & OF_PLAYER_SHIP )        {
380                         check_collision = collide_asteroid_ship;
381 //              }
382                 break;
383         case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID):
384                 // Only check collisions with player ships
385 //              if ( A->flags & OF_PLAYER_SHIP )        {
386                         check_collision = collide_asteroid_ship;
387 //              }
388                 swapped = 1;
389                 break;
390         case COLLISION_OF(OBJ_SHIP,OBJ_SHIP):
391                 check_collision = collide_ship_ship;
392                 break;  
393         
394         case COLLISION_OF(OBJ_BEAM, OBJ_SHIP):
395                 if(beam_collide_early_out(A, B)){
396                         return;
397                 }
398                 check_collision = beam_collide_ship;
399                 break;
400
401         case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID):
402                 if(beam_collide_early_out(A, B)){
403                         return;
404                 }
405                 check_collision = beam_collide_asteroid;
406                 break;
407
408         case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS):
409                 if(beam_collide_early_out(A, B)){
410                         return;
411                 }
412                 check_collision = beam_collide_debris;
413                 break;
414
415         case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON):
416                 if(beam_collide_early_out(A, B)){
417                         return;
418                 }               
419                 check_collision = beam_collide_missile;
420                 break;
421
422         case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): {
423                 weapon_info *awip, *bwip;
424                 awip = &Weapon_info[Weapons[A->instance].weapon_info_index];
425                 bwip = &Weapon_info[Weapons[B->instance].weapon_info_index];
426                 
427                 if (awip->subtype != WP_LASER || bwip->subtype != WP_LASER) {
428                         if (awip->subtype == WP_LASER) {
429                                 if ( bwip->wi_flags & WIF_BOMB ) {
430                                         check_collision = collide_weapon_weapon;
431                                 }
432                         } else if (bwip->subtype == WP_LASER) {
433                                 if ( awip->wi_flags & WIF_BOMB ) {
434                                         check_collision = collide_weapon_weapon;
435                                         swapped=1;                      
436                                 }
437                         } else {
438                                 if ( (awip->wi_flags&WIF_BOMB) || (bwip->wi_flags&WIF_BOMB) ) {
439                                         check_collision = collide_weapon_weapon;
440                                 }
441                         }
442                 }
443 /*
444                 int     atype, btype;
445
446                 atype = Weapon_info[Weapons[A->instance].weapon_info_index].subtype;
447                 btype = Weapon_info[Weapons[B->instance].weapon_info_index].subtype;
448
449                 if ((atype == WP_LASER) && (btype == WP_MISSILE))
450                         check_collision = collide_weapon_weapon;
451                 else if ((atype == WP_MISSILE) && (btype == WP_LASER)) {
452                         check_collision = collide_weapon_weapon;
453                         swapped = 1;
454                 } else if ((atype == WP_MISSILE) && (btype == WP_MISSILE))
455                         check_collision = collide_weapon_weapon;
456 */
457
458                 break;
459         }
460
461         default:
462                 return;
463         }
464
465         // Swap them if needed
466         if ( swapped )  {
467                 object *tmp = A;
468                 A = B;
469                 B = tmp;
470         }
471
472         // if there are any more obj_pair checks
473         // we should then add function int maybe_not_add_obj_pair()
474         // MWA -- 4/1/98 -- I'd do it, but I don't want to bust anything, so I'm doing my stuff here instead :-)
475         //if ( MULTIPLAYER_CLIENT && !(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE)){
476                 // multiplayer clients will only do ship/ship collisions, and their own ship to boot
477         //      if ( check_collision != collide_ship_ship ){
478         //              return;
479         //      }
480
481         //      if ( (A != Player_obj) && (B != Player_obj) ){
482         //              return;
483         //      }
484         //}     
485
486         // only check debris:weapon collisions for player
487         if (check_collision == collide_debris_weapon) {
488                 // weapon is B
489                 if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
490                 // check for dumbfire weapon
491                         // check if debris is behind laser
492                         float vdot;
493                         if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) {
494                                 vector velocity_rel_weapon;
495                                 vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel);
496                                 vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.v.fvec);
497                         } else {
498                                 vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel);
499                         }
500                         if ( vdot <= 0.0f )     {
501                                 // They're heading in opposite directions...
502                                 // check their positions
503                                 vector weapon2other;
504                                 vm_vec_sub( &weapon2other, &A->pos, &B->pos );
505                                 float pdot = vm_vec_dot( &B->orient.v.fvec, &weapon2other );
506                                 if ( pdot <= -A->radius )       {
507                                         // The other object is behind the weapon by more than
508                                         // its radius, so it will never hit...
509                                         return;
510                                 }
511                         }
512
513                         // check dist vs. dist moved during weapon lifetime
514                         vector delta_v;
515                         vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel);
516                         if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) {
517                                 return;
518                         }
519
520                         // for nonplayer ships, only create collision pair if close enough
521                         if ( !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) )
522                                 return;
523                 }
524         }
525
526         // don't check same team laser:ship collisions on small ships if not player
527         if (check_collision == collide_ship_weapon) {
528                 // weapon is B
529                 if ( !(Objects[B->parent].flags & OF_PLAYER_SHIP)
530                         && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) 
531                         && (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) 
532                         && (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) {
533                         pairs_not_created++;
534                         return;
535                 }
536         }
537
538         if ( !check_collision ) return;
539         Pairs_created++;
540
541         // At this point, we have determined that collisions between
542         // these two should be checked, so add the pair to the
543         // collision pair list.
544
545         if ( pair_free_list.next == NULL )      {
546                 nprintf(( "collision", "Out of object pairs!! Not all collisions will work!\n" ));
547                 return;
548         }
549
550         // get a new obj_pair from the free list
551         obj_pair * new_pair = pair_free_list.next;
552         pair_free_list.next = new_pair->next;
553
554         if ( add_to_end ) {
555                 obj_pair *last, *tmp;
556
557                 last = tmp = pair_used_list.next;
558                 while( tmp != NULL )    {
559                         if ( tmp->next == NULL )
560                                 last = tmp;
561
562                         tmp = tmp->next;
563                 }
564
565                 if ( last == NULL )
566                         last = &pair_used_list;
567                         
568                 last->next = new_pair;
569                 Assert(new_pair != NULL);
570                 new_pair->next = NULL;
571         }
572         else {
573                 new_pair->next = pair_used_list.next;
574                 pair_used_list.next = new_pair;
575         }
576         
577         Num_pairs++;
578 /*      if (Num_pairs > Num_pairs_hwm) {
579                 Num_pairs_hwm = Num_pairs;
580                 //nprintf(("AI", "Num_pairs high water mark = %i\n", Num_pairs_hwm));
581         }
582 */
583
584         A->num_pairs++;
585         B->num_pairs++;
586         
587         new_pair->a = A;
588         new_pair->b = B;
589         new_pair->check_collision = check_collision;
590
591         if ( check_time == -1 ){
592                 new_pair->next_check_time = timestamp(0);       // 0 means instantly time out
593         } else {
594                 new_pair->next_check_time = check_time;
595         }
596
597 }
598
599 MONITOR(NumPairs);
600 MONITOR(NumPairsChecked);
601
602 void obj_check_all_collisions()
603 {
604         if ( !(Game_detail_flags & DETAIL_FLAG_COLLISION) )     return;
605
606         obj_pair *parent, *tmp; 
607         // debug info
608         float avg_time_to_next_check = 0.0f;
609                                                         
610         parent = &pair_used_list;
611         tmp = parent->next;
612
613         Num_pairs_checked = 0;
614
615         while( tmp != NULL )    {
616
617                 int removed = 0;
618                 if ( (tmp->a != NULL) && (tmp->b != NULL) && timestamp_elapsed(tmp->next_check_time) )  {
619                         Num_pairs_checked++;
620
621                         if ((*tmp->check_collision)(tmp))       {
622                                 // We never need to check this pair again.
623                                 #if 0   //def DONT_REMOVE_PAIRS
624                                         // Never check it again, but keep the pair around
625                                         // (useful for debugging)
626                                         tmp->next_check_time = timestamp(-1);           
627                                 #else
628                                         // Never check it again, so remove the pair
629                                         removed = 1;
630                                         tmp->a->num_pairs--;    
631                                         Assert( tmp->a->num_pairs > -1 );
632                                         tmp->b->num_pairs--;
633                                         Assert( tmp->b->num_pairs > -1 );
634                                         Num_pairs--;
635                                         // Assert(Num_pairs >= 0);
636                                         parent->next = tmp->next;
637                                         tmp->a = tmp->b = NULL;
638                                         tmp->next = pair_free_list.next;
639                                         pair_free_list.next = tmp;
640                                         tmp = parent->next;
641                                 #endif
642                         }
643                 } 
644
645                 if (!removed)   {
646                         parent = tmp;
647                         tmp = tmp->next;
648                         // debug info
649                         if (tmp) {
650                                 int add_time = timestamp_until( tmp->next_check_time );
651                                 if (add_time > 0)
652                                         avg_time_to_next_check += (float) add_time;
653                         }
654                 }
655         }
656
657         MONITOR_INC(NumPairs,Num_pairs);
658         MONITOR_INC(NumPairsChecked,Num_pairs_checked);
659
660         // #define PAIR_STATS
661         #ifdef PAIR_STATS
662         avg_time_to_next_check = avg_time_to_next_check / Num_pairs;
663         extern int Num_hull_pieces;
664         extern int Weapons_created;
665         // mprintf(( "[pairs checked: %d, start_pairs: %d, num obj: %d, avg next time: %f]\n", n, org_pairs, num_objects, avg_time_to_next_check ));
666         // mprintf(( "[Num_hull_pieces: %3d, Num_weapons_created: %3d, pairs_not_created: %3d, pairs_created: %3d, percent new saved: %9.5f]\n", Num_hull_pieces, Weapons_created, pairs_not_created, Pairs_created, 100.0f*(float)pairs_not_created/(float)(pairs_not_created + Pairs_created) ));
667          mprintf(( "[pairs_created: %3d, pairs_not_created: %3d, percent saved %6.3f]\n", Pairs_created, pairs_not_created, 100.0f*pairs_not_created/(Pairs_created+pairs_not_created) ));
668         pairs_not_created = 0;
669         Weapons_created = 0;
670         Pairs_created = 0;
671         #endif
672
673         // What percent of the pairs did we check?
674         // FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects.
675
676 //      if ( org_pairs > 1 )    {
677 //              Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs);
678 //      } else {
679 //              Object_checked_percentage = 0.0f;
680 //      }
681
682 }
683
684 //      See if two lines intersect by doing recursive subdivision.
685 //      Bails out if larger distance traveled is less than sum of radii + 1.0f.
686 int collide_subdivide(vector *p0, vector *p1, float prad, vector *q0, vector *q1, float qrad)
687 {
688         float   a_dist, b_dist, ab_dist;
689
690         a_dist = vm_vec_dist(p0, p1);
691         b_dist = vm_vec_dist(q0, q1);
692
693         ab_dist = vm_vec_dist(p1, q1);
694
695         //      See if their spheres intersect
696         if (ab_dist < a_dist + b_dist + prad + qrad) {
697                 if (ab_dist  < prad + qrad)
698                         return 1;
699                 else if (vm_vec_dist(p0, q0) < prad + qrad)
700                         return 1;
701                 else if (max(a_dist, b_dist) < prad + qrad + 1.0f)
702                         return 0;
703                 else {
704                         int     r1, r2 = 0;
705                         vector  pa, qa;
706
707                         vm_vec_avg(&pa, p0, p1);
708                         vm_vec_avg(&qa, q0, q1);
709                         r1 = collide_subdivide(p0, &pa, prad, q0, &qa, qrad);
710                         if (!r1)
711                                 r2 = collide_subdivide(&pa, p1, prad, &qa, q1, qrad);
712
713                         return r1 | r2;
714                 }
715         } else
716                 return 0;
717 }
718
719
720
721 //      Return true if object A is expected to collide with object B within time duration
722 //      For purposes of this check, the first object moves from current location to predicted
723 //      location.  The second object is assumed to be where it will be at time duration, NOT
724 //      where it currently is.
725 //      radius_scale is used to control the precision of the check.
726 //              If 0.0, then use polygon models to perform check, slow and accurate
727 //              If !0.0, then use as a scale on the radius of the objects.  1.0 is Descent style
728 //                      collisions.  Larger values can be used to be sloppy about the collisions which
729 //                      is useful if a moving object wants to prevent a collision.
730 int objects_will_collide(object *A, object *B, float duration, float radius_scale)
731 {
732         object  A_future;
733         vector  hitpos;
734
735
736         A_future = *A;
737         vm_vec_scale_add2(&A_future.pos, &A->phys_info.vel, duration);
738
739         if (radius_scale == 0.0f) {
740                 return ship_check_collision_fast(B, &A_future, &hitpos );
741         } else {
742                 float           size_A, size_B, dist, r;
743                 vector  nearest_point;
744
745                 size_A = A->radius * radius_scale;
746                 size_B = B->radius * radius_scale;
747
748                 //      If A is moving, check along vector.
749                 if (A->phys_info.speed != 0.0f) {
750                         r = find_nearest_point_on_line(&nearest_point, &A->pos, &A_future.pos, &B->pos);
751                         if (r < 0) {
752                                 nearest_point = A->pos;
753                         } else if (r > 1) {
754                                 nearest_point = A_future.pos;
755                         }
756                         dist = vm_vec_dist_quick(&B->pos, &nearest_point);
757                         return (dist < size_A + size_B);
758                 } else {
759                         return vm_vec_dist_quick(&B->pos, &A->pos) < size_A + size_B;
760                 }
761         }
762 }
763
764 //      Return true if the vector from *start_pos to *end_pos is within objp->radius*radius_scale of *objp
765 int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale)
766 {
767         float           dist, r;
768         vector  nearest_point;
769
770         r = find_nearest_point_on_line(&nearest_point, start_pos, end_pos, &objp->pos);
771         if ((r >= 0.0f) && (r <= 1.0f)) {
772                 dist = vm_vec_dist_quick(&objp->pos, &nearest_point);
773
774                 return (dist < objp->radius * radius_scale);
775         } else
776                 return 0;
777 }
778
779 // Returns TRUE if the weapon will never hit the other object.
780 // If it can it predicts how long until these two objects need
781 // to be checked and fills the time in in current_pair.
782 int weapon_will_never_hit( object *weapon, object *other, obj_pair * current_pair )
783 {
784
785         Assert( weapon->type == OBJ_WEAPON );
786
787 //      mprintf(( "Frame: %d,  Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair ));
788         
789
790         // Do some checks for weapons that don't turn
791         if ( !(Weapon_info[Weapons[weapon->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
792
793                 // This first check is to see if a weapon is behind an object, and they
794                 // are heading in opposite directions.   If so, we don't need to ever check     
795                 // them again.   This is only valid for weapons that don't turn. 
796
797                 float vdot;
798                 if (Weapon_info[Weapons[weapon->instance].weapon_info_index].subtype == WP_LASER) {
799                         vector velocity_rel_weapon;
800                         vm_vec_sub(&velocity_rel_weapon, &weapon->phys_info.vel, &other->phys_info.vel);
801                         vdot = -vm_vec_dot(&velocity_rel_weapon, &weapon->orient.v.fvec);
802                 } else {
803                         vdot = vm_vec_dot( &other->phys_info.vel, &weapon->phys_info.vel);
804                 }
805                 if ( vdot <= 0.0f )     {
806                         // They're heading in opposite directions...
807                         // check their positions
808                         vector weapon2other;
809                         vm_vec_sub( &weapon2other, &other->pos, &weapon->pos );
810                         float pdot = vm_vec_dot( &weapon->orient.v.fvec, &weapon2other );
811                         if ( pdot <= -other->radius )   {
812                                 // The other object is behind the weapon by more than
813                                 // its radius, so it will never hit...
814                                 return 1;
815                         }
816                 }
817
818                 // FUTURE ENHANCEMENT IDEAS 
819
820                 // Given a laser does it hit a slow or not moving object
821                 // in its life or the next n seconds?  We'd actually need to check the 
822                 // model for this.
823                                 
824         }
825
826
827         // This check doesn't care about orient, only looks at the maximum speed
828         // of the two objects, so it knows that in the next n seconds, they can't
829         // go further than some distance, so don't bother checking collisions for 
830         // that time.   This is very rough, but is so general that it works for
831         // everything and immidiately gets rid of a lot of cases.
832         
833         if ( current_pair )     {
834                 // Find the time it will take before these get within each others distances.
835                 // tmp->next_check_time = timestamp(500);
836                 //vector        max_vel;                        //maximum foward velocity in x,y,z
837
838                 float max_vel_weapon, max_vel_other;
839                 max_vel_weapon = weapon->phys_info.max_vel.xyz.z;
840                 max_vel_other = other->phys_info.max_vel.xyz.z;
841                 if (max_vel_other < 10.0f) {
842                         if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) {
843                                 // bump up velocity from collision
844                                 max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f;
845                         } else {
846                                 max_vel_other = 10.0f;          // object may move from collision
847                         }
848                 }
849
850                 // check weapon that does not turn against sphere expanding at ship maxvel
851                 // compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time
852                 // look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t  Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship 
853                 // Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides
854                 if ( !(Weapon_info[Weapons[weapon->instance].weapon_info_index].wi_flags & WIF_TURNS) ) {
855                         vector delta_x, laser_vel;
856                         float a,b,c, delta_x_dot_vl, delta_t;
857                         float root1, root2, root, earliest_time;
858
859                         vm_vec_sub( &delta_x, &weapon->pos, &other->pos );
860                         vm_vec_copy_scale( &laser_vel, &weapon->orient.v.fvec, max_vel_weapon );
861                         delta_t = (other->radius + 10.0f) / max_vel_other;              // time to get from center to radius of other obj
862                         delta_x_dot_vl = vm_vec_dotprod( &delta_x, &laser_vel );
863
864                         a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other;
865                         b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t);
866                         c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t;
867
868                         float discriminant = b*b - 4.0f*a*c;
869                         if ( discriminant < 0) {
870                                 // never hit
871                                 return 1;
872                         } else {
873                                 root = fl_sqrt( discriminant );
874                                 root1 = (-b + root) / (2.0f * a) * 1000.0f;     // get time in ms
875                                 root2 = (-b - root) / (2.0f * a) * 1000.0f;     // get time in ms
876                         }
877
878                         // find earliest positive time
879                         if ( root1 > root2 ) {
880                                 float temp = root1;
881                                 root1 = root2;
882                                 root2 = temp;
883                         }
884
885                         if (root1 > 0) {
886                                 earliest_time = root1;
887                         } else if (root2 > 0) {
888                                 // root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame
889                                 current_pair->next_check_time = timestamp(0);   // check next time
890                                 return 0;
891                         } else {
892                                 // both times negative, so never collides
893                                 return 1;
894                         }
895
896
897
898                         // check if possible collision occurs after weapon expires
899                         if ( earliest_time > 1000*Weapons[weapon->instance].lifeleft )
900                                 return 1;
901
902                         // Allow one worst case frametime to elapse (~5 fps)
903                         earliest_time -= 200.0f;
904
905                         if (earliest_time > 100) {
906                                 current_pair->next_check_time = timestamp( fl2i(earliest_time) );
907                                 return 0;
908                         } else {
909                                 current_pair->next_check_time = timestamp(0);   // check next time
910                                 return 0;
911                         }
912
913                 } else {
914
915                         float dist, max_vel, time;
916
917                         max_vel = max_vel_weapon + max_vel_other;
918
919                         // suggest that fudge factor for other radius be changed to other_radius + const (~10)
920                         dist = vm_vec_dist( &other->pos, &weapon->pos ) - (other->radius + 10.0f);
921                         if ( dist > 0.0f )      {
922                                 time = (dist*1000.0f) / max_vel;
923                                 int time_ms = fl2i(time);
924
925                                 // check if possible collision occurs after weapon expires
926                                 if ( time_ms > 1000*Weapons[weapon->instance].lifeleft )
927                                         return 1;
928
929                                 time_ms -= 200; // Allow at least one worst case frametime to elapse (~5 fps)
930                                                 
931                                 if ( time_ms > 100 )    {               // If it takes longer than 1/10th of a second, then delay it
932                                         current_pair->next_check_time = timestamp(time_ms);
933                                         //mprintf(( "Delaying %d ms\n", time_ms ));
934                                         return 0;
935                                 }
936                         }
937                         current_pair->next_check_time = timestamp(0);   // check next time
938
939                 }
940         }
941
942         return 0;
943 }
944
945 //      Return true if vector from *curpos to *goalpos intersects with object *goalobjp
946 //      Else, return false.
947 //      radius is radius of object moving from curpos to goalpos.
948 int pp_collide(vector *curpos, vector *goalpos, object *goalobjp, float radius)
949 {
950         mc_info mc;
951
952         Assert(goalobjp->type == OBJ_SHIP);
953
954         ship_model_start(goalobjp);
955
956         mc.model_num = Ships[goalobjp->instance].modelnum;                      // Fill in the model to check
957         mc.orient = &goalobjp->orient;  // The object's orient
958         mc.pos = &goalobjp->pos;                        // The object's position
959         mc.p0 = curpos;                                 // Point 1 of ray to check
960         mc.p1 = goalpos;                                        // Point 2 of ray to check
961         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE;
962         mc.radius = radius;
963
964         model_collide(&mc);
965
966         ship_model_stop(goalobjp);
967
968         return mc.num_hits;
969 }
970
971 //      Setup and call pp_collide for collide_predict_large_ship
972 //      Returns true if objp will collide with objp2 before it reaches goal_pos.
973 int cpls_aux(vector *goal_pos, object *objp2, object *objp)
974 {
975         float   radius;
976         
977         radius = objp->radius;
978         if (1.5f * radius < 70.0f)
979                 radius *= 1.5f;
980         else
981                 radius = 70.0f;
982
983         if (pp_collide(&objp->pos, goal_pos, objp2, radius))
984                 return 1;
985         else
986                 return 0;
987 }
988
989 //      Return true if objp will collide with some large object.
990 //      Don't check for an object this ship is docked to.
991 int collide_predict_large_ship(object *objp, float distance)
992 {
993         object  *objp2;
994         vector  cur_pos, goal_pos;
995         object  *dock_objp;
996         ship_info       *sip;
997
998         sip = &Ship_info[Ships[objp->instance].ship_info_index];
999
1000         dock_objp = NULL;
1001         if (objp->type == OBJ_SHIP) {
1002                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
1003                 if (aip->dock_objnum != -1)
1004                         dock_objp = &Objects[aip->dock_objnum];
1005         }
1006
1007         cur_pos = objp->pos;
1008
1009         vm_vec_scale_add(&goal_pos, &cur_pos, &objp->orient.v.fvec, distance);
1010
1011         for ( objp2 = GET_FIRST(&obj_used_list); objp2 != END_OF_LIST(&obj_used_list); objp2 = GET_NEXT(objp2) ) {
1012                 if ((objp != objp2) && (objp2->type == OBJ_SHIP)) {
1013                         if (Ship_info[Ships[objp2->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
1014                                 if (objp2 == dock_objp)
1015                                         continue;
1016
1017                                 if (cpls_aux(&goal_pos, objp2, objp))
1018                                         return 1;
1019                         }
1020                 } else if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) && (objp2->type == OBJ_ASTEROID)) {
1021                         if (vm_vec_dist_quick(&objp2->pos, &objp->pos) < (distance + objp2->radius)*2.5f) {
1022                                 vector  pos, delvec;
1023                                 int             count;
1024                                 float           d1;
1025
1026                                 d1 = 2.5f * distance + objp2->radius;
1027                                 count = (int) (d1/(objp2->radius + objp->radius));      //      Scale up distance, else looks like there would be a collision.
1028                                 pos = cur_pos;
1029                                 vm_vec_normalized_dir(&delvec, &goal_pos, &cur_pos);
1030                                 vm_vec_scale(&delvec, d1/count);
1031
1032                                 for (; count>0; count--) {
1033                                         if (vm_vec_dist_quick(&pos, &objp2->pos) < objp->radius + objp2->radius)
1034                                                 return 1;
1035                                         vm_vec_add2(&pos, &delvec);
1036                                 }
1037                         }
1038                 }
1039         }
1040
1041         return 0;
1042 }
1043
1044 // function to iterate through all object collision pairs looking for weapons
1045 // which could be deleted since they are not going to hit anything.  Passed into this
1046 // function is a 'time' parameter used as watermark for which weapons to check.
1047
1048 #define CRW_NO_OBJECT           -1
1049 #define CRW_NO_PAIR                     0
1050 #define CRW_IN_PAIR                     1
1051 #define CRW_CAN_DELETE          2
1052
1053 #define CRW_MAX_TO_DELETE       4
1054
1055 char crw_status[MAX_WEAPONS];
1056
1057 void crw_check_weapon( int weapon_num, int collide_next_check )
1058 {
1059         float next_check_time;
1060         weapon *wp;
1061
1062         wp = &Weapons[weapon_num];
1063
1064         // if this weapons life left > time before next collision, then we cannot remove it
1065         crw_status[WEAPON_INDEX(wp)] = CRW_IN_PAIR;
1066         next_check_time = ((float)(timestamp_until(collide_next_check)) / 1000.0f);
1067         if ( wp->lifeleft < next_check_time )
1068                 crw_status[WEAPON_INDEX(wp)] = CRW_CAN_DELETE;
1069 }
1070
1071 int collide_remove_weapons( )
1072 {
1073         obj_pair *opp;
1074         int i, num_deleted, oldest_index, j, loop_count;
1075         float oldest_time;
1076
1077         // setup remove_weapon array.  assume we can remove it.
1078         for (i = 0; i < MAX_WEAPONS; i++ ) {
1079                 if ( Weapons[i].objnum == -1 )
1080                         crw_status[i] = CRW_NO_OBJECT;
1081                 else
1082                         crw_status[i] = CRW_NO_PAIR;
1083         }
1084
1085         // first pass is to see if any of the weapons don't have collision pairs.
1086
1087         opp = &pair_used_list;
1088         opp = opp->next;
1089         while( opp != NULL )    {
1090                 // for each collide pair, if the two objects can still collide, then set the remove_weapon
1091                 // parameter for the weapon to 0.  need to check both parameters
1092                 if ( opp->a->type == OBJ_WEAPON )
1093                         crw_check_weapon( opp->a->instance, opp->next_check_time );
1094
1095                 if ( opp->b->type == OBJ_WEAPON )
1096                         crw_check_weapon( opp->b->instance, opp->next_check_time );
1097
1098                 opp = opp->next;
1099         }
1100
1101         // for each weapon which could be removed, delete the object
1102         num_deleted = 0;
1103         for ( i = 0; i < MAX_WEAPONS; i++ ) {
1104                 if ( crw_status[i] == CRW_CAN_DELETE ) {
1105                         Assert( Weapons[i].objnum != -1 );
1106                         obj_delete( Weapons[i].objnum );
1107                         num_deleted++;
1108                 }
1109         }
1110
1111         if ( num_deleted )
1112                 return num_deleted;
1113
1114         // if we didn't remove any weapons, try to the N oldest weapons.  first checking for pairs, then
1115         // checking for oldest weapons in general.  We will go through the loop a max of 2 times.  first time
1116         // through, we check oldest weapons with pairs, next time through, for oldest weapons.
1117         loop_count = 0;
1118         do {
1119                 for ( j = 0; j < CRW_MAX_TO_DELETE; j++ ) {
1120                         oldest_time = 1000.0f;
1121                         oldest_index = -1;
1122                         for (i = 0; i < MAX_WEAPONS; i++ ) {
1123                                 if ( Weapons[i].objnum == -1 )                  // shouldn't happen, but this is the safe thing to do.
1124                                         continue;
1125                                 if ( ((loop_count || crw_status[i] == CRW_NO_PAIR)) && (Weapons[i].lifeleft < oldest_time) ) {
1126                                         oldest_time = Weapons[i].lifeleft;
1127                                         oldest_index = i;
1128                                 }
1129                         }
1130                         if ( oldest_index != -1 ) {
1131                                 obj_delete(Weapons[oldest_index].objnum);
1132                                 num_deleted++;
1133                         }
1134                 }
1135
1136                 // if we deleted some weapons, then we can break
1137                 if ( num_deleted )
1138                         break;
1139
1140                 loop_count++;
1141         } while ( loop_count < 2);
1142
1143         return num_deleted;
1144
1145 }
1146
1147 void set_hit_struct_info(collision_info_struct *hit, mc_info *mc, int submodel_rot_hit)
1148 {
1149         hit->edge_hit = mc->edge_hit;
1150         hit->hit_pos = mc->hit_point_world;
1151         hit->hit_time = mc->hit_dist;
1152         hit->submodel_num = mc->hit_submodel;
1153
1154         hit->submodel_rot_hit = submodel_rot_hit;
1155 }
1156