Fix crash if Num_walls=0
[btb/d2x.git] / main / physics.c
1 /* $Id: physics.c,v 1.8 2004-12-20 06:28:10 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Code for flying through the mines
18  *
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <conf.h>
24 #endif
25
26 #ifdef RCS
27 static char rcsid[] = "$Id: physics.c,v 1.8 2004-12-20 06:28:10 btb Exp $";
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "joy.h"
34 #include "mono.h"
35 #include "error.h"
36
37 #include "inferno.h"
38 #include "segment.h"
39 #include "object.h"
40 #include "physics.h"
41 #include "key.h"
42 #include "game.h"
43 #include "collide.h"
44 #include "fvi.h"
45 #include "newdemo.h"
46 #include "timer.h"
47 #include "ai.h"
48 #include "wall.h"
49 #include "laser.h"
50 #include "bm.h"
51 #include "player.h"
52
53 #ifdef TACTILE
54 #include "tactile.h"
55 #endif
56
57 //Global variables for physics system
58
59 #define ROLL_RATE               0x2000
60 #define DAMP_ANG                        0x400                  //min angle to bank
61
62 #define TURNROLL_SCALE  (0x4ec4/2)
63
64 #define MAX_OBJECT_VEL i2f(100)
65
66 #define BUMP_HACK       1               //if defined, bump player when he gets stuck
67
68 //--unused-- int mike_mode=0;
69
70 //check point against each side of segment. return bitmask, where bit
71 //set means behind that side
72
73 int Physics_cheat_flag = 0;
74 extern char BounceCheat;
75
76 //##//returns the distance of a point (checkp) from a plane (defined by norm & planep)
77 //##fix dist_to_plane(vms_vector *checkp,vms_vector *norm,vms_vector *planep)
78 //##{
79 //##    vms_vector deltap;
80 //##
81 //##    vm_vec_sub(&deltap,checkp,planep);
82 //##
83 //##    return vm_vec_dot(&deltap,norm);
84 //##}
85
86 //--unused-- int dpjm_old_joy_x, dpjm_old_joy_y;
87
88 int floor_levelling=0;
89
90 //--unused-- level_with_floor()
91 //--unused-- {
92 //--unused--    floor_levelling=1;
93 //--unused-- }
94
95 //make sure matrix is orthogonal
96 void check_and_fix_matrix(vms_matrix *m)
97 {
98         vms_matrix tempm;
99
100         vm_vector_2_matrix(&tempm,&m->fvec,&m->uvec,NULL);
101         *m  = tempm;
102 }
103
104
105 void do_physics_align_object( object * obj )
106 {
107         vms_vector desired_upvec;
108         fixang delta_ang,roll_ang;
109         //vms_vector forvec = {0,0,f1_0};
110         vms_matrix temp_matrix;
111         fix d,largest_d=-f1_0;
112         int i,best_side;
113
114         best_side=0;
115         // bank player according to segment orientation
116
117         //find side of segment that player is most alligned with
118
119         for (i=0;i<6;i++) {
120                 #ifdef COMPACT_SEGS
121                         vms_vector _tv1;
122                         get_side_normal( &Segments[obj->segnum], i, 0, &_tv1 );
123                         d = vm_vec_dot(&_tv1,&obj->orient.uvec);
124                 #else                                   
125                         d = vm_vec_dot(&Segments[obj->segnum].sides[i].normals[0],&obj->orient.uvec);
126                 #endif
127
128                 if (d > largest_d) {largest_d = d; best_side=i;}
129         }
130
131         if (floor_levelling) {
132
133                 // old way: used floor's normal as upvec
134                 #ifdef COMPACT_SEGS
135                         get_side_normal(&Segments[obj->segnum], 3, 0, &desired_upvec );                 
136                 #else
137                         desired_upvec = Segments[obj->segnum].sides[3].normals[0];
138                 #endif
139
140         }
141         else  // new player leveling code: use normal of side closest to our up vec
142                 if (get_num_faces(&Segments[obj->segnum].sides[best_side])==2) {
143                         #ifdef COMPACT_SEGS
144                                 vms_vector normals[2];
145                                 get_side_normals(&Segments[obj->segnum], best_side, &normals[0], &normals[1] );                 
146
147                                 desired_upvec.x = (normals[0].x + normals[1].x) / 2;
148                                 desired_upvec.y = (normals[0].y + normals[1].y) / 2;
149                                 desired_upvec.z = (normals[0].z + normals[1].z) / 2;
150
151                                 vm_vec_normalize(&desired_upvec);
152                         #else
153                                 side *s = &Segments[obj->segnum].sides[best_side];
154                                 desired_upvec.x = (s->normals[0].x + s->normals[1].x) / 2;
155                                 desired_upvec.y = (s->normals[0].y + s->normals[1].y) / 2;
156                                 desired_upvec.z = (s->normals[0].z + s->normals[1].z) / 2;
157                 
158                                 vm_vec_normalize(&desired_upvec);
159                         #endif
160                 }
161                 else
162                         #ifdef COMPACT_SEGS
163                                 get_side_normal(&Segments[obj->segnum], best_side, 0, &desired_upvec );                 
164                         #else
165                                 desired_upvec = Segments[obj->segnum].sides[best_side].normals[0];
166                         #endif
167
168         if (labs(vm_vec_dot(&desired_upvec,&obj->orient.fvec)) < f1_0/2) {
169                 fixang save_delta_ang;
170                 vms_angvec tangles;
171                 
172                 vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,&desired_upvec,NULL);
173
174                 save_delta_ang = delta_ang = vm_vec_delta_ang(&obj->orient.uvec,&temp_matrix.uvec,&obj->orient.fvec);
175
176                 delta_ang += obj->mtype.phys_info.turnroll;
177
178                 if (abs(delta_ang) > DAMP_ANG) {
179                         vms_matrix rotmat, new_pm;
180
181                         roll_ang = fixmul(FrameTime,ROLL_RATE);
182
183                         if (abs(delta_ang) < roll_ang) roll_ang = delta_ang;
184                         else if (delta_ang<0) roll_ang = -roll_ang;
185
186                         tangles.p = tangles.h = 0;  tangles.b = roll_ang;
187                         vm_angles_2_matrix(&rotmat,&tangles);
188
189                         vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
190                         obj->orient = new_pm;
191                 }
192                 else floor_levelling=0;
193         }
194
195 }
196
197 void set_object_turnroll(object *obj)
198 {
199         fixang desired_bank;
200
201         desired_bank = -fixmul(obj->mtype.phys_info.rotvel.y,TURNROLL_SCALE);
202
203         if (obj->mtype.phys_info.turnroll != desired_bank) {
204                 fixang delta_ang,max_roll;
205
206                 max_roll = fixmul(ROLL_RATE,FrameTime);
207
208                 delta_ang = desired_bank - obj->mtype.phys_info.turnroll;
209
210                 if (labs(delta_ang) < max_roll)
211                         max_roll = delta_ang;
212                 else
213                         if (delta_ang < 0)
214                                 max_roll = -max_roll;
215
216                 obj->mtype.phys_info.turnroll += max_roll;
217         }
218
219 }
220
221 //list of segments went through
222 int phys_seglist[MAX_FVI_SEGS],n_phys_segs;
223
224
225 #define MAX_IGNORE_OBJS 100
226
227 #ifndef NDEBUG
228 #define EXTRA_DEBUG 1           //no extra debug when NDEBUG is on
229 #endif
230
231 #ifdef EXTRA_DEBUG
232 object *debug_obj=NULL;
233 #endif
234
235 #define XYZ(v) (v)->x,(v)->y,(v)->z
236
237
238 #ifndef NDEBUG
239 int     Total_retries=0, Total_sims=0;
240 int     Dont_move_ai_objects=0;
241 #endif
242
243 #define FT (f1_0/64)
244
245 extern int disable_new_fvi_stuff;
246 //      -----------------------------------------------------------------------------------------------------------
247 // add rotational velocity & acceleration
248 void do_physics_sim_rot(object *obj)
249 {
250         vms_angvec      tangles;
251         vms_matrix      rotmat,new_orient;
252         //fix                   rotdrag_scale;
253         physics_info *pi;
254
255         Assert(FrameTime > 0);  //Get MATT if hit this!
256
257         pi = &obj->mtype.phys_info;
258
259         if (!(pi->rotvel.x || pi->rotvel.y || pi->rotvel.z || pi->rotthrust.x || pi->rotthrust.y || pi->rotthrust.z))
260                 return;
261
262         if (obj->mtype.phys_info.drag) {
263                 int count;
264                 vms_vector accel;
265                 fix drag,r,k;
266
267                 count = FrameTime / FT;
268                 r = FrameTime % FT;
269                 k = fixdiv(r,FT);
270
271                 drag = (obj->mtype.phys_info.drag*5)/2;
272
273                 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
274
275                         vm_vec_copy_scale(&accel,&obj->mtype.phys_info.rotthrust,fixdiv(f1_0,obj->mtype.phys_info.mass));
276
277                         while (count--) {
278
279                                 vm_vec_add2(&obj->mtype.phys_info.rotvel,&accel);
280
281                                 vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-drag);
282                         }
283
284                         //do linear scale on remaining bit of time
285
286                         vm_vec_scale_add2(&obj->mtype.phys_info.rotvel,&accel,k);
287                         vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-fixmul(k,drag));
288                 }
289                 else if (! (obj->mtype.phys_info.flags & PF_FREE_SPINNING)) {
290                         fix total_drag=f1_0;
291
292                         while (count--)
293                                 total_drag = fixmul(total_drag,f1_0-drag);
294
295                         //do linear scale on remaining bit of time
296
297                         total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
298
299                         vm_vec_scale(&obj->mtype.phys_info.rotvel,total_drag);
300                 }
301
302         }
303
304         //mprintf( (0, "Rot vel = %.3f,%.3f,%.3f\n", f2fl(obj->mtype.phys_info.rotvel.x),f2fl(obj->mtype.phys_info.rotvel.y), f2fl(obj->mtype.phys_info.rotvel.z) ));
305
306         //now rotate object
307
308         //unrotate object for bank caused by turn
309         if (obj->mtype.phys_info.turnroll) {
310                 vms_matrix new_pm;
311
312                 tangles.p = tangles.h = 0;
313                 tangles.b = -obj->mtype.phys_info.turnroll;
314                 vm_angles_2_matrix(&rotmat,&tangles);
315                 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
316                 obj->orient = new_pm;
317         }
318
319         tangles.p = fixmul(obj->mtype.phys_info.rotvel.x,FrameTime);
320         tangles.h = fixmul(obj->mtype.phys_info.rotvel.y,FrameTime);
321         tangles.b  = fixmul(obj->mtype.phys_info.rotvel.z,FrameTime);
322
323         vm_angles_2_matrix(&rotmat,&tangles);
324         vm_matrix_x_matrix(&new_orient,&obj->orient,&rotmat);
325         obj->orient = new_orient;
326
327         if (obj->mtype.phys_info.flags & PF_TURNROLL)
328                 set_object_turnroll(obj);
329
330         //re-rotate object for bank caused by turn
331         if (obj->mtype.phys_info.turnroll) {
332                 vms_matrix new_pm;
333
334                 tangles.p = tangles.h = 0;
335                 tangles.b = obj->mtype.phys_info.turnroll;
336                 vm_angles_2_matrix(&rotmat,&tangles);
337                 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
338                 obj->orient = new_pm;
339         }
340
341         check_and_fix_matrix(&obj->orient);
342 }
343
344 //      -----------------------------------------------------------------------------------------------------------
345 //Simulate a physics object for this frame
346 void do_physics_sim(object *obj)
347 {
348         int ignore_obj_list[MAX_IGNORE_OBJS],n_ignore_objs;
349         int iseg;
350         int try_again;
351         int fate;
352         vms_vector frame_vec;                   //movement in this frame
353         vms_vector new_pos,ipos;                //position after this frame
354         int count=0;
355         int objnum;
356         int WallHitSeg, WallHitSide;
357         fvi_info hit_info;
358         fvi_query fq;
359         vms_vector save_pos;
360         int save_seg;
361         fix drag;
362         fix sim_time,old_sim_time;
363         vms_vector start_pos;
364         int obj_stopped=0;
365         fix moved_time;                 //how long objected moved before hit something
366         vms_vector save_p0,save_p1;
367         physics_info *pi;
368         int orig_segnum = obj->segnum;
369         int bounced=0;
370
371         Assert(obj->type != OBJ_NONE);
372         Assert(obj->movement_type == MT_PHYSICS);
373
374 #ifndef NDEBUG
375 if (Dont_move_ai_objects)
376         if (obj->control_type == CT_AI)
377                 return;
378 #endif
379
380         pi = &obj->mtype.phys_info;
381
382         do_physics_sim_rot(obj);
383
384         if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z))
385                 return;
386
387         objnum = obj-Objects;
388
389         n_phys_segs = 0;
390
391         disable_new_fvi_stuff = (obj->type != OBJ_PLAYER);
392
393         sim_time = FrameTime;
394
395 //debug_obj = obj;
396
397         #ifdef EXTRA_DEBUG
398         if (obj == debug_obj) {
399                 printf("object %d:\n  start pos = %x %x %x\n",objnum,XYZ(&obj->pos));
400                 printf("  thrust = %x %x %x\n",XYZ(&obj->mtype.phys_info.thrust));
401                 printf("  sim_time = %x\n",sim_time);
402         }
403
404         //check for correct object segment
405         if(!get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask == 0)
406         {
407                 #ifndef NDEBUG
408                 mprintf((0,"Warning: object %d not in given seg!\n",objnum));
409                 #endif
410                 //Int3();  Removed by Rob 10/5/94
411                 if (!update_object_seg(obj)) {
412                         #ifndef NDEBUG
413                         mprintf((0,"Warning: can't find seg for object %d - moving\n",objnum));
414                         #endif
415                         if (!(Game_mode & GM_MULTI))
416                                 Int3();
417                         compute_segment_center(&obj->pos,&Segments[obj->segnum]);
418                         obj->pos.x += objnum;
419                 }
420         }
421         #endif
422
423         start_pos = obj->pos;
424
425         n_ignore_objs = 0;
426
427         Assert(obj->mtype.phys_info.brakes==0);         //brakes not used anymore?
428
429                 //if uses thrust, cannot have zero drag
430         Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0);
431
432 //mprintf((0,"thrust=%x  speed=%x\n",vm_vec_mag(&obj->mtype.phys_info.thrust),vm_vec_mag(&obj->mtype.phys_info.velocity)));
433
434         //do thrust & drag
435         
436         if ((drag = obj->mtype.phys_info.drag) != 0) {
437
438                 int count;
439                 vms_vector accel;
440                 fix r,k;
441
442                 count = sim_time / FT;
443                 r = sim_time % FT;
444                 k = fixdiv(r,FT);
445
446                 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
447
448                         vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass));
449
450                         while (count--) {
451
452                                 vm_vec_add2(&obj->mtype.phys_info.velocity,&accel);
453
454                                 vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag);
455                         }
456
457                         //do linear scale on remaining bit of time
458
459                         vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k);
460
461                         vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag));
462                 }
463                 else {
464                         fix total_drag=f1_0;
465
466                         while (count--)
467                                 total_drag = fixmul(total_drag,f1_0-drag);
468
469                         //do linear scale on remaining bit of time
470
471                         total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
472
473                         vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag);
474                 }
475         }
476
477         #ifdef EXTRA_DEBUG
478         if (obj == debug_obj)
479                 printf("  velocity = %x %x %x\n",XYZ(&obj->mtype.phys_info.velocity));
480         #endif
481
482         do {
483                 try_again = 0;
484
485                 //Move the object
486                 vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time);
487
488                 #ifdef EXTRA_DEBUG
489                 if (obj == debug_obj)
490                         printf("  pass %d, frame_vec = %x %x %x\n",count,XYZ(&frame_vec));
491                 #endif
492
493                 if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) ) 
494                         break;
495
496                 count++;
497
498                 //      If retry count is getting large, then we are trying to do something stupid.
499                 if ( count > 3)         {
500                         if (obj->type == OBJ_PLAYER) {
501                                 if (count > 8)
502                                         break;
503                         } else
504                                 break;
505                 }
506
507                 vm_vec_add(&new_pos,&obj->pos,&frame_vec);
508
509                 #ifdef EXTRA_DEBUG
510                 if (obj == debug_obj)
511                         printf("   desired_pos  = %x %x %x\n",XYZ(&new_pos));
512                 #endif
513
514                 ignore_obj_list[n_ignore_objs] = -1;
515
516                 #ifdef EXTRA_DEBUG
517                 if (obj == debug_obj) {
518                         printf("   FVI parms: p0 = %8x %8x %8x, segnum=%x, size=%x\n",XYZ(&obj->pos),obj->segnum,obj->size);
519                         printf("              p1 = %8x %8x %8x\n",XYZ(&new_pos));
520                 }
521                 #endif
522
523                 fq.p0                                           = &obj->pos;
524                 fq.startseg                             = obj->segnum;
525                 fq.p1                                           = &new_pos;
526                 fq.rad                                  = obj->size;
527                 fq.thisobjnum                   = objnum;
528                 fq.ignore_obj_list      = ignore_obj_list;
529                 fq.flags                                        = FQ_CHECK_OBJS;
530
531                 if (obj->type == OBJ_WEAPON)
532                         fq.flags |= FQ_TRANSPOINT;
533
534                 if (obj->type == OBJ_PLAYER)
535                         fq.flags |= FQ_GET_SEGLIST;
536
537 //@@                    if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask != 0)
538 //@@                            Int3();
539
540 save_p0 = *fq.p0;
541 save_p1 = *fq.p1;
542
543
544                 fate = find_vector_intersection(&fq,&hit_info);
545                 //      Matt: Mike's hack.
546                 if (fate == HIT_OBJECT) {
547                         object  *objp = &Objects[hit_info.hit_object];
548
549                         if ((objp->type == OBJ_WEAPON) && ((objp->id == PROXIMITY_ID) || (objp->id == SUPERPROX_ID)))
550                                 count--;
551                 }
552
553                 #ifndef NDEBUG
554                 if (fate == HIT_BAD_P0) {
555                         mprintf((0,"Warning: Bad p0 in physics!  Object = %i, type = %i [%s]\n", obj-Objects, obj->type, Object_type_names[obj->type]));
556                         Int3();
557                 }
558                 #endif
559
560                 if (obj->type == OBJ_PLAYER) {
561                         int i;
562
563                         if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0])
564                                 n_phys_segs--;
565
566                         for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1);  )
567                                 phys_seglist[n_phys_segs++] = hit_info.seglist[i++];
568                 }
569
570                 #ifdef EXTRA_DEBUG
571                 if (obj == debug_obj)
572                         printf("   fate  = %d, hit_pnt = %8x %8x %8x\n",fate,XYZ(&hit_info.hit_pnt));;
573                 #endif
574
575                 ipos = hit_info.hit_pnt;
576                 iseg = hit_info.hit_seg;
577                 WallHitSide = hit_info.hit_side;
578                 WallHitSeg = hit_info.hit_side_seg;
579
580                 if (iseg==-1) {         //some sort of horrible error
581                         #ifndef NDEBUG
582                         mprintf((1,"iseg==-1 in physics!  Object = %i, type = %i (%s)\n", obj-Objects, obj->type, Object_type_names[obj->type]));
583                         #endif
584                         //Int3();
585                         //compute_segment_center(&ipos,&Segments[obj->segnum]);
586                         //ipos.x += objnum;
587                         //iseg = obj->segnum;
588                         //fate = HIT_NONE;
589                         if (obj->type == OBJ_WEAPON)
590                                 obj->flags |= OF_SHOULD_BE_DEAD;
591                         break;
592                 }
593
594                 Assert(!((fate==HIT_WALL) && ((WallHitSeg == -1) || (WallHitSeg > Highest_segment_index))));
595
596                 //if(!get_seg_masks(&hit_info.hit_pnt, hit_info.hit_seg, 0, __FILE__, __LINE__).centermask == 0)
597                 //      Int3();
598
599                 save_pos = obj->pos;                    //save the object's position
600                 save_seg = obj->segnum;
601
602                 // update object's position and segment number
603                 obj->pos = ipos;
604
605                 #ifdef EXTRA_DEBUG
606                 if (obj == debug_obj)
607                         printf("   new pos = %x %x %x\n",XYZ(&obj->pos));
608                 #endif
609
610                 if ( iseg != obj->segnum )
611                         obj_relink(objnum, iseg );
612
613                 //if start point not in segment, move object to center of segment
614                 if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask !=0 )
615                 {
616                         int n;
617
618                         if ((n=find_object_seg(obj))==-1) {
619                                 //Int3();
620                                 if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) {
621                                         obj->pos = obj->last_pos;
622                                         obj_relink(objnum, n );
623                                 }
624                                 else {
625                                         compute_segment_center(&obj->pos,&Segments[obj->segnum]);
626                                         obj->pos.x += objnum;
627                                 }
628                                 if (obj->type == OBJ_WEAPON)
629                                         obj->flags |= OF_SHOULD_BE_DEAD;
630                         }
631                         return;
632                 }
633
634                 //calulate new sim time
635                 {
636                         //vms_vector moved_vec;
637                         vms_vector moved_vec_n;
638                         fix attempted_dist,actual_dist;
639
640                         old_sim_time = sim_time;
641
642                         actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos);
643
644                         if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) {                //moved backwards
645
646                                 //don't change position or sim_time
647
648 //*******                                       mprintf((0,"Obj %d moved backwards\n",obj-Objects));
649
650                                 #ifdef EXTRA_DEBUG
651                                 if (obj == debug_obj)
652                                         printf("   Warning: moved backwards!\n");
653                                 #endif
654
655                                 obj->pos = save_pos;
656                 
657                                 //iseg = obj->segnum;           //don't change segment
658
659                                 obj_relink(objnum, save_seg );
660
661                                 moved_time = 0;
662                         }
663                         else {
664
665                                 //if (obj == debug_obj)
666                                 //      printf("   moved_vec = %x %x %x\n",XYZ(&moved_vec));
667                         
668                                 attempted_dist = vm_vec_mag(&frame_vec);
669
670                                 sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist);
671
672                                 moved_time = old_sim_time - sim_time;
673
674                                 if (sim_time < 0 || sim_time>old_sim_time) {
675                                         #ifndef NDEBUG
676                                         mprintf((0,"Bogus sim_time = %x, old = %x\n",sim_time,old_sim_time));
677                                         if (obj == debug_obj)
678                                                 printf("   Bogus sim_time = %x, old = %x, attempted_dist = %x, actual_dist = %x\n",sim_time,old_sim_time,attempted_dist,actual_dist);
679                                         //Int3(); Removed by Rob
680                                         #endif
681                                         sim_time = old_sim_time;
682                                         //WHY DOES THIS HAPPEN??
683
684                                         moved_time = 0;
685                                 }
686                         }
687
688                         #ifdef EXTRA_DEBUG
689                         if (obj == debug_obj)
690                                 printf("   new sim_time = %x\n",sim_time);
691                         #endif
692
693                 }
694
695
696                 switch( fate )          {
697
698                         case HIT_WALL:          {
699                                 vms_vector moved_v;
700                                 //@@fix total_d,moved_d;
701                                 fix hit_speed,wall_part;
702         
703                                 // Find hit speed       
704
705                                 vm_vec_sub(&moved_v,&obj->pos,&save_pos);
706
707                                 wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm);
708
709                                 if (wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0)
710                                         collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt );
711                                 else
712                                         scrape_object_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt );
713
714                                 Assert( WallHitSeg > -1 );
715                                 Assert( WallHitSide > -1 );
716
717                                 if ( !(obj->flags&OF_SHOULD_BE_DEAD) )  {
718                                         int forcefield_bounce;          //bounce off a forcefield
719
720                                         Assert(BounceCheat || !(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE));      //can't be bounce and stick
721
722                                         forcefield_bounce = (TmapInfo[Segments[WallHitSeg].sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD);
723
724                                         if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) {            //stop moving
725
726                                                 // mprintf((0, "Object %i stuck at %i:%i\n", obj-Objects, WallHitSeg, WallHitSide));
727                                                 add_stuck_object(obj, WallHitSeg, WallHitSide);
728
729                                                 vm_vec_zero(&obj->mtype.phys_info.velocity);
730                                                 obj_stopped = 1;
731                                                 try_again = 0;
732                                         }
733                                         else {                                  // Slide object along wall
734                                                 int check_vel=0;
735
736                                                 //We're constrained by wall, so subtract wall part from
737                                                 //velocity vector
738
739                                                 wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity);
740
741 //                                              mprintf((0, "%d", f2i(vm_vec_mag(&hit_info.hit_wallnorm)) ));
742
743                                                 if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) {            //bounce off wall
744                                                         wall_part *= 2; //Subtract out wall part twice to achieve bounce
745
746                                                         if (forcefield_bounce) {
747                                                                 check_vel = 1;                          //check for max velocity
748                                                                 if (obj->type == OBJ_PLAYER)
749                                                                         wall_part *= 2;         //player bounce twice as much
750                                                         }
751
752                                                         if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) {
753                                                                 Assert(obj->mtype.phys_info.flags & PF_BOUNCE);
754                                                                 if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE)
755                                                                         obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE);
756                                                                 else
757                                                                         obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE;
758                                                         }
759
760                                                         bounced = 1;            //this object bounced
761                                                 }
762
763                                                 vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part);
764
765 //                                              mprintf((0, "Velocity at bounce time = %d\n", f2i(vm_vec_mag(&obj->mtype.phys_info.velocity))));
766
767 //if (obj==ConsoleObject)
768 //      mprintf((0,"player vel = %x\n",vm_vec_mag_quick(&obj->mtype.phys_info.velocity)));
769
770                                                 if (check_vel) {
771                                                         fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
772
773                                                         if (vel > MAX_OBJECT_VEL)
774                                                                 vm_vec_scale(&obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel));
775                                                 }
776
777                                                 if (bounced && obj->type == OBJ_WEAPON)
778                                                         vm_vector_2_matrix(&obj->orient,&obj->mtype.phys_info.velocity,&obj->orient.uvec,NULL);
779
780                                                 #ifdef EXTRA_DEBUG
781                                                 if (obj == debug_obj) {
782                                                         printf("   sliding - wall_norm %x %x %x %x\n",wall_part,XYZ(&hit_info.hit_wallnorm));
783                                                         printf("   wall_part %x, new velocity = %x %x %x\n",wall_part,XYZ(&obj->mtype.phys_info.velocity));
784                                                 }
785                                                 #endif
786
787                                                 try_again = 1;
788                                         }
789                                 }
790                                 break;
791                         }
792
793                         case HIT_OBJECT:                {
794                                 vms_vector old_vel;
795
796                                 // Mark the hit object so that on a retry the fvi code
797                                 // ignores this object.
798
799                                 Assert(hit_info.hit_object != -1);
800
801                                 //      Calculcate the hit point between the two objects.
802                                 {       vms_vector      *ppos0, *ppos1, pos_hit;
803                                         fix                     size0, size1;
804                                         ppos0 = &Objects[hit_info.hit_object].pos;
805                                         ppos1 = &obj->pos;
806                                         size0 = Objects[hit_info.hit_object].size;
807                                         size1 = obj->size;
808                                         Assert(size0+size1 != 0);       // Error, both sizes are 0, so how did they collide, anyway?!?
809                                         //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1));
810                                         //vm_vec_add2(&pos_hit, ppos0);
811                                         vm_vec_sub(&pos_hit, ppos1, ppos0);
812                                         vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1));
813
814                                         old_vel = obj->mtype.phys_info.velocity;
815
816                                         collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit);
817
818                                 }
819
820                                 // Let object continue its movement
821                                 if ( !(obj->flags&OF_SHOULD_BE_DEAD)  ) {
822                                         //obj->pos = save_pos;
823
824                                         if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) {
825                                                 //if (Objects[hit_info.hit_object].type == OBJ_POWERUP)
826                                                         ignore_obj_list[n_ignore_objs++] = hit_info.hit_object;
827                                                 try_again = 1;
828                                         }
829                                 }
830
831                                 break;
832                         }       
833                         case HIT_NONE:          
834                         #ifdef TACTILE
835                                 if (TactileStick && obj==ConsoleObject && !(FrameCount & 15))
836                                  Tactile_Xvibrate_clear ();
837                         #endif
838                                 break;
839
840                         #ifndef NDEBUG
841                         case HIT_BAD_P0:
842                                 Int3();         // Unexpected collision type: start point not in specified segment.
843                                 mprintf((0,"Warning: Bad p0 in physics!!!\n"));
844                                 break;
845                         default:
846                                 // Unknown collision type returned from find_vector_intersection!!
847                                 Int3();
848                                 break;
849                         #endif
850                 }
851
852         } while ( try_again );
853
854         //      Pass retry count info to AI.
855         if (obj->control_type == CT_AI) {
856                 if (count > 0) {
857                         Ai_local_info[objnum].retry_count = count-1;
858                         #ifndef NDEBUG
859                         Total_retries += count-1;
860                         Total_sims++;
861                         #endif
862                 }
863         }
864
865         //I'm not sure why we do this.  I wish there were a comment that
866         //explained it.  I think maybe it only needs to be done if the object
867         //is sliding, but I don't know
868         if (!obj_stopped && !bounced)   {       //Set velocity from actual movement
869                 vms_vector moved_vec;
870
871                 vm_vec_sub(&moved_vec,&obj->pos,&start_pos);
872                 vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime));
873
874                 #ifdef BUMP_HACK
875                 if (obj==ConsoleObject && (obj->mtype.phys_info.velocity.x==0 && obj->mtype.phys_info.velocity.y==0 && obj->mtype.phys_info.velocity.z==0) &&
876                           !(obj->mtype.phys_info.thrust.x==0 && obj->mtype.phys_info.thrust.y==0 && obj->mtype.phys_info.thrust.z==0)) {
877                         vms_vector center,bump_vec;
878
879                         //bump player a little towards center of segment to unstick
880
881                         compute_segment_center(&center,&Segments[obj->segnum]);
882                         vm_vec_normalized_dir_quick(&bump_vec,&center,&obj->pos);
883
884                         //don't bump player toward center of reactor segment
885                         if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN)
886                                 vm_vec_negate(&bump_vec);
887
888                         vm_vec_scale_add2(&obj->pos,&bump_vec,obj->size/5);
889
890                         //if moving away from seg, might move out of seg, so update
891                         if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN)
892                                 update_object_seg(obj);
893                 }
894                 #endif
895         }
896
897         //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0);
898
899         //if (obj->control_type == CT_FLYING)
900         if (obj->mtype.phys_info.flags & PF_LEVELLING)
901                 do_physics_align_object( obj );
902
903
904         //hack to keep player from going through closed doors
905         if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (Physics_cheat_flag!=0xBADA55) ) {
906                 int sidenum;
907
908                 sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]);
909
910                 if (sidenum != -1) {
911
912                         if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) {
913                                 side *s;
914                                 int vertnum,num_faces,i;
915                                 fix dist;
916                                 int vertex_list[6];
917
918                                 //bump object back
919
920                                 s = &Segments[orig_segnum].sides[sidenum];
921
922                                 if (orig_segnum==-1)
923                                         Error("orig_segnum == -1 in physics");
924
925                                 create_abs_vertex_lists(&num_faces, vertex_list, orig_segnum, sidenum, __FILE__, __LINE__);
926
927                                 //let's pretend this wall is not triangulated
928                                 vertnum = vertex_list[0];
929                                 for (i=1;i<4;i++)
930                                         if (vertex_list[i] < vertnum)
931                                                 vertnum = vertex_list[i];
932
933                                 #ifdef COMPACT_SEGS
934                                         {
935                                         vms_vector _vn;
936                                         get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn );
937                                         dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]);
938                                         vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist);
939                                         }
940                                 #else
941                                         dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]);
942                                         vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist);
943                                 #endif
944                                 update_object_seg(obj);
945
946                         }
947                 }
948         }
949
950 //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL   #ifndef NDEBUG
951         //if end point not in segment, move object to last pos, or segment center
952         if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask != 0)
953         {
954                 if (find_object_seg(obj)==-1) {
955                         int n;
956
957                         //Int3();
958                         if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) {
959                                 obj->pos = obj->last_pos;
960                                 obj_relink(objnum, n );
961                         }
962                         else {
963                                 compute_segment_center(&obj->pos,&Segments[obj->segnum]);
964                                 obj->pos.x += objnum;
965                         }
966                         if (obj->type == OBJ_WEAPON)
967                                 obj->flags |= OF_SHOULD_BE_DEAD;
968                 }
969         }
970 //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL   #endif
971
972
973 }
974
975 //--unused-- //tell us what the given object will do (as far as hiting walls) in
976 //--unused-- //the given time (in seconds) t.  Igores acceleration (sorry)
977 //--unused-- //if check_objects is set, check with objects, else just with walls
978 //--unused-- //returns fate, fills in hit time.  If fate==HIT_NONE, hit_time undefined
979 //--unused-- int physics_lookahead(object *obj,fix t,int fvi_flags,fix *hit_time, fvi_info *hit_info)
980 //--unused-- {
981 //--unused--    vms_vector new_pos;
982 //--unused--    int objnum,fate;
983 //--unused--    fvi_query fq;
984 //--unused--
985 //--unused--    Assert(obj->movement_type == MT_PHYSICS);
986 //--unused--
987 //--unused--    objnum = obj-Objects;
988 //--unused--
989 //--unused--    vm_vec_scale_add(&new_pos, &obj->pos, &obj->mtype.phys_info.velocity, t);
990 //--unused--
991 //--unused--    fq.p0                                           = &obj->pos;
992 //--unused--    fq.startseg                             = obj->segnum;
993 //--unused--    fq.p1                                           = &new_pos;
994 //--unused--    fq.rad                                  = obj->size;
995 //--unused--    fq.thisobjnum                   = objnum;
996 //--unused--    fq.ignore_obj_list      = NULL;
997 //--unused--    fq.flags                                        = fvi_flags;
998 //--unused--
999 //--unused--    fate = find_vector_intersection(&fq,hit_info);
1000 //--unused--
1001 //--unused--    if (fate != HIT_NONE) {
1002 //--unused--            fix dist,speed;
1003 //--unused--
1004 //--unused--            dist = vm_vec_dist(&obj->pos, &hit_info->hit_pnt);
1005 //--unused--
1006 //--unused--            speed = vm_vec_mag(&obj->mtype.phys_info.velocity);
1007 //--unused--
1008 //--unused--            *hit_time = fixdiv(dist,speed);
1009 //--unused--
1010 //--unused--    }
1011 //--unused--
1012 //--unused--    return fate;
1013 //--unused--
1014 //--unused-- }
1015
1016 //Applies an instantaneous force on an object, resulting in an instantaneous
1017 //change in velocity.
1018 void phys_apply_force(object *obj,vms_vector *force_vec)
1019 {
1020
1021         //      Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass,
1022         //      in collision with crazy reactor robot thing on d2levf-s.
1023         if (obj->mtype.phys_info.mass == 0)
1024                 return;
1025
1026         if (obj->movement_type != MT_PHYSICS)
1027                 return;
1028
1029 #ifdef TACTILE
1030    if (TactileStick && obj==&Objects[Players[Player_num].objnum])
1031                 Tactile_apply_force (force_vec,&obj->orient);
1032 #endif
1033
1034         //Add in acceleration due to force
1035         vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass));
1036
1037
1038 }
1039
1040 //      ----------------------------------------------------------------
1041 //      Do *dest = *delta unless:
1042 //                              *delta is pretty small
1043 //              and     they are of different signs.
1044 void physics_set_rotvel_and_saturate(fix *dest, fix delta)
1045 {
1046         if ((delta ^ *dest) < 0) {
1047                 if (abs(delta) < F1_0/8) {
1048                         // mprintf((0, "D"));
1049                         *dest = delta/4;
1050                 } else
1051                         // mprintf((0, "d"));
1052                         *dest = delta;
1053         } else {
1054                 // mprintf((0, "!"));
1055                 *dest = delta;
1056         }
1057 }
1058
1059 //      ------------------------------------------------------------------------------------------------------
1060 //      Note: This is the old ai_turn_towards_vector code.
1061 //      phys_apply_rot used to call ai_turn_towards_vector until I fixed it, which broke phys_apply_rot.
1062 void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate)
1063 {
1064         vms_angvec      dest_angles, cur_angles;
1065         fix                     delta_p, delta_h;
1066         vms_vector      *rotvel_ptr = &obj->mtype.phys_info.rotvel;
1067
1068         // Make this object turn towards the goal_vector.  Changes orientation, doesn't change direction of movement.
1069         // If no one moves, will be facing goal_vector in 1 second.
1070
1071         //      Detect null vector.
1072         if ((goal_vector->x == 0) && (goal_vector->y == 0) && (goal_vector->z == 0))
1073                 return;
1074
1075         //      Make morph objects turn more slowly.
1076         if (obj->control_type == CT_MORPH)
1077                 rate *= 2;
1078
1079         vm_extract_angles_vector(&dest_angles, goal_vector);
1080         vm_extract_angles_vector(&cur_angles, &obj->orient.fvec);
1081
1082         delta_p = (dest_angles.p - cur_angles.p);
1083         delta_h = (dest_angles.h - cur_angles.h);
1084
1085         if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
1086         if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
1087         if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
1088         if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
1089
1090         delta_p = fixdiv(delta_p, rate);
1091         delta_h = fixdiv(delta_h, rate);
1092
1093         if (abs(delta_p) < F1_0/16) delta_p *= 4;
1094         if (abs(delta_h) < F1_0/16) delta_h *= 4;
1095
1096         physics_set_rotvel_and_saturate(&rotvel_ptr->x, delta_p);
1097         physics_set_rotvel_and_saturate(&rotvel_ptr->y, delta_h);
1098         rotvel_ptr->z = 0;
1099 }
1100
1101 //      -----------------------------------------------------------------------------
1102 //      Applies an instantaneous whack on an object, resulting in an instantaneous
1103 //      change in orientation.
1104 void phys_apply_rot(object *obj,vms_vector *force_vec)
1105 {
1106         fix     rate, vecmag;
1107
1108         if (obj->movement_type != MT_PHYSICS)
1109                 return;
1110
1111         vecmag = vm_vec_mag(force_vec)/8;
1112         if (vecmag < F1_0/256)
1113                 rate = 4*F1_0;
1114         else if (vecmag < obj->mtype.phys_info.mass >> 14)
1115                 rate = 4*F1_0;
1116         else {
1117                 rate = fixdiv(obj->mtype.phys_info.mass, vecmag);
1118                 if (obj->type == OBJ_ROBOT) {
1119                         if (rate < F1_0/4)
1120                                 rate = F1_0/4;
1121                         //      Changed by mk, 10/24/95, claw guys should not slow down when attacking!
1122                         if (!Robot_info[obj->id].thief && !Robot_info[obj->id].attack_type) {
1123                                 if (obj->ctype.ai_info.SKIP_AI_COUNT * FrameTime < 3*F1_0/4) {
1124                                         fix     tval = fixdiv(F1_0, 8*FrameTime);
1125                                         int     addval;
1126
1127                                         addval = f2i(tval);
1128
1129                                         if ( (d_rand() * 2) < (tval & 0xffff))
1130                                                 addval++;
1131                                         obj->ctype.ai_info.SKIP_AI_COUNT += addval;
1132                                         // -- mk: too much stuff making hard to see my debug messages...mprintf((0, "FrameTime = %7.3f, addval = %i\n", f2fl(FrameTime), addval));
1133                                 }
1134                         }
1135                 } else {
1136                         if (rate < F1_0/2)
1137                                 rate = F1_0/2;
1138                 }
1139         }
1140
1141         //      Turn amount inversely proportional to mass.  Third parameter is seconds to do 360 turn.
1142         physics_turn_towards_vector(force_vec, obj, rate);
1143
1144
1145 }
1146
1147
1148 //this routine will set the thrust for an object to a value that will
1149 //(hopefully) maintain the object's current velocity
1150 void set_thrust_from_velocity(object *obj)
1151 {
1152         fix k;
1153
1154         Assert(obj->movement_type == MT_PHYSICS);
1155
1156         k = fixmuldiv(obj->mtype.phys_info.mass,obj->mtype.phys_info.drag,(f1_0-obj->mtype.phys_info.drag));
1157
1158         vm_vec_copy_scale(&obj->mtype.phys_info.thrust,&obj->mtype.phys_info.velocity,k);
1159
1160 }
1161
1162