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