1 /* $Id: physics.c,v 1.6 2004-05-22 01:06:25 btb Exp $ */
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.
17 * Code for flying through the mines
20 * Revision 1.5 1995/10/12 17:28:08 allender
21 * put in code to move and object to center of segment in
22 * do_physics sim when fvi fails with bad point
24 * Revision 1.4 1995/08/23 21:32:44 allender
25 * fix mcc compiler warnings
27 * Revision 1.3 1995/07/28 15:38:56 allender
28 * removed isqrt thing -- not required here
30 * Revision 1.2 1995/07/28 15:13:29 allender
31 * fixed vector magnitude thing
33 * Revision 1.1 1995/05/16 15:29:42 allender
36 * Revision 2.2 1995/03/24 14:48:54 john
37 * Added cheat for player to go thru walls.
39 * Revision 2.1 1995/03/20 18:15:59 john
40 * Added code to not store the normals in the segment structure.
42 * Revision 2.0 1995/02/27 11:32:06 john
43 * New version 2.0, which has no anonymous unions, builds with
44 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
46 * Revision 1.213 1995/02/22 13:40:48 allender
47 * remove anonymous unions from object structure
49 * Revision 1.212 1995/02/22 13:24:42 john
50 * Removed the vecmat anonymous unions.
52 * Revision 1.211 1995/02/06 19:46:59 matt
53 * New function (untested), set_thrust_from_velocity()
55 * Revision 1.210 1995/02/02 16:26:12 matt
56 * Changed assert that was causing a problem
58 * Revision 1.209 1995/02/02 14:07:00 matt
59 * Fixed confusion about which segment you are touching when you're
60 * touching a wall. This manifested itself in spurious lava burns.
62 * Revision 1.208 1995/02/01 21:03:24 john
65 * Revision 1.207 1995/01/25 13:53:35 rob
66 * Removed an Int3 from multiplayer games.
68 * Revision 1.206 1995/01/23 17:30:47 rob
69 * Removed Int3 on bogus sim time.
71 * Revision 1.205 1995/01/17 11:08:56 matt
72 * Disable new-ish FVI edge checking for all objects except the player,
73 * since it was causing problems with the fusion cannon.
75 * Revision 1.204 1995/01/05 09:43:49 matt
76 * Took out int3s from new code
78 * Revision 1.203 1995/01/04 22:19:23 matt
79 * Added hack to keep player from squeezing through closed walls/doors
81 * Revision 1.202 1995/01/02 12:38:48 mike
82 * physics hack to crazy josh not get hung up on proximity bombs. Matt notified via email.
84 * Revision 1.201 1994/12/13 15:39:22 mike
85 * #ifndef NDEBUG some code.
87 * Revision 1.200 1994/12/13 13:28:34 yuan
90 * Revision 1.199 1994/12/13 13:25:00 matt
91 * Made bump hack compile out if so desired
93 * Revision 1.198 1994/12/13 12:02:39 matt
94 * Added hack to bump player a little if stuck
96 * Revision 1.197 1994/12/12 00:32:23 matt
97 * When objects other than player go out of mine, jerk to center of segment
99 * Revision 1.196 1994/12/10 22:52:42 mike
100 * make physics left-the-mine checking always be in.
102 * Revision 1.195 1994/12/08 00:53:01 mike
103 * oops...phys rot bug.
105 * Revision 1.194 1994/12/07 12:54:54 mike
106 * tweak rotvel applied from collisions.
108 * Revision 1.193 1994/12/07 00:36:08 mike
109 * fix phys_apply_rot for robots -- ai was bashing effect in next frame.
111 * Revision 1.192 1994/12/05 17:23:10 matt
112 * Made a bunch of debug code compile out
114 * Revision 1.191 1994/12/05 16:30:10 matt
115 * Was illegally changing an object's segment...shoot me.
117 * Revision 1.190 1994/12/05 11:58:51 mike
118 * fix stupid apply_force_rot() bug.
120 * Revision 1.189 1994/12/05 09:42:17 mike
121 * fix 0 mag problem when object applies force to itself.
123 * Revision 1.188 1994/12/04 22:48:40 matt
124 * Physics & FVI now only build seglist for player objects, and they
125 * responsilby deal with buffer full conditions
127 * Revision 1.187 1994/12/04 22:14:07 mike
128 * apply instantaneous rotation to an object due to a force blow.
130 * Revision 1.186 1994/12/04 18:51:30 matt
131 * When weapons get stuck, delete them!
133 * Revision 1.185 1994/12/04 18:38:56 matt
134 * Added better handling of point-not-in-seg problem
136 * Revision 1.184 1994/11/27 23:13:42 matt
137 * Made changes for new mprintf calling convention
139 * Revision 1.183 1994/11/25 23:46:18 matt
140 * Fixed drag problems with framerates over 60Hz
142 * Revision 1.182 1994/11/25 22:15:52 matt
143 * Added asserts to try to trap frametime < 0 bug
145 * Revision 1.181 1994/11/21 11:42:44 mike
148 * Revision 1.180 1994/11/19 15:15:04 mike
149 * remove unused code and data
151 * Revision 1.179 1994/11/16 11:25:22 matt
152 * Abort physics if negative frametime
154 * Revision 1.178 1994/10/05 19:50:41 rob
155 * Removed a non-critical Int3 where an object's segnum is checked.
156 * Left mprintf message.
158 * Revision 1.177 1994/10/03 22:57:50 matt
159 * Fixed problem with matrix corruption of non-moving (but rotating) objects
161 * Revision 1.176 1994/09/28 09:23:28 mike
162 * Add useful information to mprintf(1,... error messages.
164 * Revision 1.175 1994/09/21 17:16:54 mike
165 * Make objects stuck in doors go away when door opens.
167 * Revision 1.174 1994/09/12 14:19:06 matt
168 * Drag & thrust now handled differently
170 * Revision 1.173 1994/09/09 14:21:12 matt
171 * Use new thrust flag
173 * Revision 1.172 1994/09/08 16:21:57 matt
174 * Cleaned up player-hit-wall code, and added object scrape handling
175 * Also added weapon-on-weapon hit sound
177 * Revision 1.171 1994/09/02 12:30:37 matt
178 * Fixed weapons which go through objects
180 * Revision 1.170 1994/09/02 11:55:14 mike
181 * Kill redefinition of a constant which is properly defined in object.h
183 * Revision 1.169 1994/09/02 11:35:01 matt
186 * Revision 1.168 1994/09/02 11:32:48 matt
187 * Fixed object/object collisions, so you can't fly through robots anymore.
188 * Cleaned up object damage system.
190 * Revision 1.167 1994/08/30 21:58:15 matt
191 * Made phys_apply_force() do nothing to an object if it's not a phys object
193 * Revision 1.166 1994/08/26 10:47:01 john
194 * New version of controls.
196 * Revision 1.165 1994/08/25 21:53:57 mike
197 * Prevent counts of -1 which eventually add up to a positive number in do_ai_frame, causing
198 * the too-many-retries behavior.
200 * Revision 1.164 1994/08/25 18:43:33 john
201 * First revision of new control code.
203 * Revision 1.163 1994/08/17 22:18:05 mike
204 * Make robots which have rotvel or rotthrust, but not movement, move.
206 * Revision 1.162 1994/08/13 17:31:18 mike
209 * Revision 1.161 1994/08/11 18:59:16 mike
210 * *** empty log message ***
212 * Revision 1.160 1994/08/10 19:53:47 mike
213 * Debug code (which is still in...)
214 * and adapt to changed interface to create_path_to_player.
216 * Revision 1.159 1994/08/08 21:38:43 matt
217 * Cleaned up a code a little and optimized a little
219 * Revision 1.158 1994/08/08 15:21:50 mike
220 * Trap retry count >= 4, but don't do AI hack unless >= 6.
222 * Revision 1.157 1994/08/08 11:47:15 matt
223 * Cleaned up fvi and physics a little
225 * Revision 1.156 1994/08/05 10:10:10 yuan
226 * Commented out debug stuff that was killing framerate.
228 * Revision 1.155 1994/08/04 19:12:36 matt
229 * Changed a bunch of vecmat calls to use multiple-function routines, and to
230 * allow the use of C macros for some functions
232 * Revision 1.154 1994/08/04 16:33:57 mike
233 * Kill a pile of RCS stuff.
234 * Call create_path_to_player for a stuck object.
236 * Revision 1.153 1994/08/04 00:21:02 matt
237 * Cleaned up fvi & physics error handling; put in code to make sure objects
238 * are in correct segment; simplified segment finding for objects and points
240 * Revision 1.152 1994/08/01 16:25:34 matt
241 * Check for moved_time == 0 when computing hit speed
243 * Revision 1.151 1994/08/01 13:30:32 matt
244 * Made fvi() check holes in transparent walls, and changed fvi() calling
245 * parms to take all input data in query structure.
247 * Revision 1.150 1994/07/29 23:41:46 matt
248 * Fixed turn banking, which changed when I added rotational velocity
250 * Revision 1.149 1994/07/27 20:53:23 matt
251 * Added rotational drag & thrust, so turning now has momemtum like moving
261 static char rcsid[] = "$Id: physics.c,v 1.6 2004-05-22 01:06:25 btb Exp $";
291 //Global variables for physics system
293 #define ROLL_RATE 0x2000
294 #define DAMP_ANG 0x400 //min angle to bank
296 #define TURNROLL_SCALE (0x4ec4/2)
298 #define MAX_OBJECT_VEL i2f(100)
300 #define BUMP_HACK 1 //if defined, bump player when he gets stuck
302 //--unused-- int mike_mode=0;
304 //check point against each side of segment. return bitmask, where bit
305 //set means behind that side
307 int Physics_cheat_flag = 0;
308 extern char BounceCheat;
310 //##//returns the distance of a point (checkp) from a plane (defined by norm & planep)
311 //##fix dist_to_plane(vms_vector *checkp,vms_vector *norm,vms_vector *planep)
313 //## vms_vector deltap;
315 //## vm_vec_sub(&deltap,checkp,planep);
317 //## return vm_vec_dot(&deltap,norm);
320 //--unused-- int dpjm_old_joy_x, dpjm_old_joy_y;
322 int floor_levelling=0;
324 //--unused-- level_with_floor()
326 //--unused-- floor_levelling=1;
329 //make sure matrix is orthogonal
330 void check_and_fix_matrix(vms_matrix *m)
334 vm_vector_2_matrix(&tempm,&m->fvec,&m->uvec,NULL);
339 void do_physics_align_object( object * obj )
341 vms_vector desired_upvec;
342 fixang delta_ang,roll_ang;
343 //vms_vector forvec = {0,0,f1_0};
344 vms_matrix temp_matrix;
345 fix d,largest_d=-f1_0;
349 // bank player according to segment orientation
351 //find side of segment that player is most alligned with
356 get_side_normal( &Segments[obj->segnum], i, 0, &_tv1 );
357 d = vm_vec_dot(&_tv1,&obj->orient.uvec);
359 d = vm_vec_dot(&Segments[obj->segnum].sides[i].normals[0],&obj->orient.uvec);
362 if (d > largest_d) {largest_d = d; best_side=i;}
365 if (floor_levelling) {
367 // old way: used floor's normal as upvec
369 get_side_normal(&Segments[obj->segnum], 3, 0, &desired_upvec );
371 desired_upvec = Segments[obj->segnum].sides[3].normals[0];
375 else // new player leveling code: use normal of side closest to our up vec
376 if (get_num_faces(&Segments[obj->segnum].sides[best_side])==2) {
378 vms_vector normals[2];
379 get_side_normals(&Segments[obj->segnum], best_side, &normals[0], &normals[1] );
381 desired_upvec.x = (normals[0].x + normals[1].x) / 2;
382 desired_upvec.y = (normals[0].y + normals[1].y) / 2;
383 desired_upvec.z = (normals[0].z + normals[1].z) / 2;
385 vm_vec_normalize(&desired_upvec);
387 side *s = &Segments[obj->segnum].sides[best_side];
388 desired_upvec.x = (s->normals[0].x + s->normals[1].x) / 2;
389 desired_upvec.y = (s->normals[0].y + s->normals[1].y) / 2;
390 desired_upvec.z = (s->normals[0].z + s->normals[1].z) / 2;
392 vm_vec_normalize(&desired_upvec);
397 get_side_normal(&Segments[obj->segnum], best_side, 0, &desired_upvec );
399 desired_upvec = Segments[obj->segnum].sides[best_side].normals[0];
402 if (labs(vm_vec_dot(&desired_upvec,&obj->orient.fvec)) < f1_0/2) {
403 fixang save_delta_ang;
406 vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,&desired_upvec,NULL);
408 save_delta_ang = delta_ang = vm_vec_delta_ang(&obj->orient.uvec,&temp_matrix.uvec,&obj->orient.fvec);
410 delta_ang += obj->mtype.phys_info.turnroll;
412 if (abs(delta_ang) > DAMP_ANG) {
413 vms_matrix rotmat, new_pm;
415 roll_ang = fixmul(FrameTime,ROLL_RATE);
417 if (abs(delta_ang) < roll_ang) roll_ang = delta_ang;
418 else if (delta_ang<0) roll_ang = -roll_ang;
420 tangles.p = tangles.h = 0; tangles.b = roll_ang;
421 vm_angles_2_matrix(&rotmat,&tangles);
423 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
424 obj->orient = new_pm;
426 else floor_levelling=0;
431 void set_object_turnroll(object *obj)
435 desired_bank = -fixmul(obj->mtype.phys_info.rotvel.y,TURNROLL_SCALE);
437 if (obj->mtype.phys_info.turnroll != desired_bank) {
438 fixang delta_ang,max_roll;
440 max_roll = fixmul(ROLL_RATE,FrameTime);
442 delta_ang = desired_bank - obj->mtype.phys_info.turnroll;
444 if (labs(delta_ang) < max_roll)
445 max_roll = delta_ang;
448 max_roll = -max_roll;
450 obj->mtype.phys_info.turnroll += max_roll;
455 //list of segments went through
456 int phys_seglist[MAX_FVI_SEGS],n_phys_segs;
459 #define MAX_IGNORE_OBJS 100
462 #define EXTRA_DEBUG 1 //no extra debug when NDEBUG is on
466 object *debug_obj=NULL;
469 #define XYZ(v) (v)->x,(v)->y,(v)->z
473 int Total_retries=0, Total_sims=0;
474 int Dont_move_ai_objects=0;
479 extern int disable_new_fvi_stuff;
480 // -----------------------------------------------------------------------------------------------------------
481 // add rotational velocity & acceleration
482 void do_physics_sim_rot(object *obj)
485 vms_matrix rotmat,new_orient;
489 Assert(FrameTime > 0); //Get MATT if hit this!
491 pi = &obj->mtype.phys_info;
493 if (!(pi->rotvel.x || pi->rotvel.y || pi->rotvel.z || pi->rotthrust.x || pi->rotthrust.y || pi->rotthrust.z))
496 if (obj->mtype.phys_info.drag) {
501 count = FrameTime / FT;
505 drag = (obj->mtype.phys_info.drag*5)/2;
507 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
509 vm_vec_copy_scale(&accel,&obj->mtype.phys_info.rotthrust,fixdiv(f1_0,obj->mtype.phys_info.mass));
513 vm_vec_add2(&obj->mtype.phys_info.rotvel,&accel);
515 vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-drag);
518 //do linear scale on remaining bit of time
520 vm_vec_scale_add2(&obj->mtype.phys_info.rotvel,&accel,k);
521 vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-fixmul(k,drag));
523 else if (! (obj->mtype.phys_info.flags & PF_FREE_SPINNING)) {
527 total_drag = fixmul(total_drag,f1_0-drag);
529 //do linear scale on remaining bit of time
531 total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
533 vm_vec_scale(&obj->mtype.phys_info.rotvel,total_drag);
538 //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) ));
542 //unrotate object for bank caused by turn
543 if (obj->mtype.phys_info.turnroll) {
546 tangles.p = tangles.h = 0;
547 tangles.b = -obj->mtype.phys_info.turnroll;
548 vm_angles_2_matrix(&rotmat,&tangles);
549 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
550 obj->orient = new_pm;
553 tangles.p = fixmul(obj->mtype.phys_info.rotvel.x,FrameTime);
554 tangles.h = fixmul(obj->mtype.phys_info.rotvel.y,FrameTime);
555 tangles.b = fixmul(obj->mtype.phys_info.rotvel.z,FrameTime);
557 vm_angles_2_matrix(&rotmat,&tangles);
558 vm_matrix_x_matrix(&new_orient,&obj->orient,&rotmat);
559 obj->orient = new_orient;
561 if (obj->mtype.phys_info.flags & PF_TURNROLL)
562 set_object_turnroll(obj);
564 //re-rotate object for bank caused by turn
565 if (obj->mtype.phys_info.turnroll) {
568 tangles.p = tangles.h = 0;
569 tangles.b = obj->mtype.phys_info.turnroll;
570 vm_angles_2_matrix(&rotmat,&tangles);
571 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
572 obj->orient = new_pm;
575 check_and_fix_matrix(&obj->orient);
578 // -----------------------------------------------------------------------------------------------------------
579 //Simulate a physics object for this frame
580 void do_physics_sim(object *obj)
582 int ignore_obj_list[MAX_IGNORE_OBJS],n_ignore_objs;
586 vms_vector frame_vec; //movement in this frame
587 vms_vector new_pos,ipos; //position after this frame
590 int WallHitSeg, WallHitSide;
596 fix sim_time,old_sim_time;
597 vms_vector start_pos;
599 fix moved_time; //how long objected moved before hit something
600 vms_vector save_p0,save_p1;
602 int orig_segnum = obj->segnum;
605 Assert(obj->type != OBJ_NONE);
606 Assert(obj->movement_type == MT_PHYSICS);
609 if (Dont_move_ai_objects)
610 if (obj->control_type == CT_AI)
614 pi = &obj->mtype.phys_info;
616 do_physics_sim_rot(obj);
618 if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z))
621 objnum = obj-Objects;
625 disable_new_fvi_stuff = (obj->type != OBJ_PLAYER);
627 sim_time = FrameTime;
632 if (obj == debug_obj) {
633 printf("object %d:\n start pos = %x %x %x\n",objnum,XYZ(&obj->pos));
634 printf(" thrust = %x %x %x\n",XYZ(&obj->mtype.phys_info.thrust));
635 printf(" sim_time = %x\n",sim_time);
638 //check for correct object segment
639 if(!get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask == 0)
642 mprintf((0,"Warning: object %d not in given seg!\n",objnum));
644 //Int3(); Removed by Rob 10/5/94
645 if (!update_object_seg(obj)) {
647 mprintf((0,"Warning: can't find seg for object %d - moving\n",objnum));
649 if (!(Game_mode & GM_MULTI))
651 compute_segment_center(&obj->pos,&Segments[obj->segnum]);
652 obj->pos.x += objnum;
657 start_pos = obj->pos;
661 Assert(obj->mtype.phys_info.brakes==0); //brakes not used anymore?
663 //if uses thrust, cannot have zero drag
664 Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0);
666 //mprintf((0,"thrust=%x speed=%x\n",vm_vec_mag(&obj->mtype.phys_info.thrust),vm_vec_mag(&obj->mtype.phys_info.velocity)));
670 if ((drag = obj->mtype.phys_info.drag) != 0) {
676 count = sim_time / FT;
680 if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
682 vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass));
686 vm_vec_add2(&obj->mtype.phys_info.velocity,&accel);
688 vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag);
691 //do linear scale on remaining bit of time
693 vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k);
695 vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag));
701 total_drag = fixmul(total_drag,f1_0-drag);
703 //do linear scale on remaining bit of time
705 total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
707 vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag);
712 if (obj == debug_obj)
713 printf(" velocity = %x %x %x\n",XYZ(&obj->mtype.phys_info.velocity));
720 vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time);
723 if (obj == debug_obj)
724 printf(" pass %d, frame_vec = %x %x %x\n",count,XYZ(&frame_vec));
727 if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) )
732 // If retry count is getting large, then we are trying to do something stupid.
734 if (obj->type == OBJ_PLAYER) {
741 vm_vec_add(&new_pos,&obj->pos,&frame_vec);
744 if (obj == debug_obj)
745 printf(" desired_pos = %x %x %x\n",XYZ(&new_pos));
748 ignore_obj_list[n_ignore_objs] = -1;
751 if (obj == debug_obj) {
752 printf(" FVI parms: p0 = %8x %8x %8x, segnum=%x, size=%x\n",XYZ(&obj->pos),obj->segnum,obj->size);
753 printf(" p1 = %8x %8x %8x\n",XYZ(&new_pos));
758 fq.startseg = obj->segnum;
761 fq.thisobjnum = objnum;
762 fq.ignore_obj_list = ignore_obj_list;
763 fq.flags = FQ_CHECK_OBJS;
765 if (obj->type == OBJ_WEAPON)
766 fq.flags |= FQ_TRANSPOINT;
768 if (obj->type == OBJ_PLAYER)
769 fq.flags |= FQ_GET_SEGLIST;
771 //@@ if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask != 0)
778 fate = find_vector_intersection(&fq,&hit_info);
779 // Matt: Mike's hack.
780 if (fate == HIT_OBJECT) {
781 object *objp = &Objects[hit_info.hit_object];
783 if ((objp->type == OBJ_WEAPON) && ((objp->id == PROXIMITY_ID) || (objp->id == SUPERPROX_ID)))
788 if (fate == HIT_BAD_P0) {
789 mprintf((0,"Warning: Bad p0 in physics! Object = %i, type = %i [%s]\n", obj-Objects, obj->type, Object_type_names[obj->type]));
794 if (obj->type == OBJ_PLAYER) {
797 if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0])
800 for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1); )
801 phys_seglist[n_phys_segs++] = hit_info.seglist[i++];
805 if (obj == debug_obj)
806 printf(" fate = %d, hit_pnt = %8x %8x %8x\n",fate,XYZ(&hit_info.hit_pnt));;
809 ipos = hit_info.hit_pnt;
810 iseg = hit_info.hit_seg;
811 WallHitSide = hit_info.hit_side;
812 WallHitSeg = hit_info.hit_side_seg;
814 if (iseg==-1) { //some sort of horrible error
816 mprintf((1,"iseg==-1 in physics! Object = %i, type = %i (%s)\n", obj-Objects, obj->type, Object_type_names[obj->type]));
819 //compute_segment_center(&ipos,&Segments[obj->segnum]);
821 //iseg = obj->segnum;
823 if (obj->type == OBJ_WEAPON)
824 obj->flags |= OF_SHOULD_BE_DEAD;
828 Assert(!((fate==HIT_WALL) && ((WallHitSeg == -1) || (WallHitSeg > Highest_segment_index))));
830 //if(!get_seg_masks(&hit_info.hit_pnt,hit_info.hit_seg,0).centermask==0)
833 save_pos = obj->pos; //save the object's position
834 save_seg = obj->segnum;
836 // update object's position and segment number
840 if (obj == debug_obj)
841 printf(" new pos = %x %x %x\n",XYZ(&obj->pos));
844 if ( iseg != obj->segnum )
845 obj_relink(objnum, iseg );
847 //if start point not in segment, move object to center of segment
848 if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask !=0 )
852 if ((n=find_object_seg(obj))==-1) {
854 if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) {
855 obj->pos = obj->last_pos;
856 obj_relink(objnum, n );
859 compute_segment_center(&obj->pos,&Segments[obj->segnum]);
860 obj->pos.x += objnum;
862 if (obj->type == OBJ_WEAPON)
863 obj->flags |= OF_SHOULD_BE_DEAD;
868 //calulate new sim time
870 //vms_vector moved_vec;
871 vms_vector moved_vec_n;
872 fix attempted_dist,actual_dist;
874 old_sim_time = sim_time;
876 actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos);
878 if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) { //moved backwards
880 //don't change position or sim_time
882 //******* mprintf((0,"Obj %d moved backwards\n",obj-Objects));
885 if (obj == debug_obj)
886 printf(" Warning: moved backwards!\n");
891 //iseg = obj->segnum; //don't change segment
893 obj_relink(objnum, save_seg );
899 //if (obj == debug_obj)
900 // printf(" moved_vec = %x %x %x\n",XYZ(&moved_vec));
902 attempted_dist = vm_vec_mag(&frame_vec);
904 sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist);
906 moved_time = old_sim_time - sim_time;
908 if (sim_time < 0 || sim_time>old_sim_time) {
910 mprintf((0,"Bogus sim_time = %x, old = %x\n",sim_time,old_sim_time));
911 if (obj == debug_obj)
912 printf(" Bogus sim_time = %x, old = %x, attempted_dist = %x, actual_dist = %x\n",sim_time,old_sim_time,attempted_dist,actual_dist);
913 //Int3(); Removed by Rob
915 sim_time = old_sim_time;
916 //WHY DOES THIS HAPPEN??
923 if (obj == debug_obj)
924 printf(" new sim_time = %x\n",sim_time);
934 //@@fix total_d,moved_d;
935 fix hit_speed,wall_part;
939 vm_vec_sub(&moved_v,&obj->pos,&save_pos);
941 wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm);
943 if (wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0)
944 collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt );
946 scrape_object_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt );
948 Assert( WallHitSeg > -1 );
949 Assert( WallHitSide > -1 );
951 if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) {
952 int forcefield_bounce; //bounce off a forcefield
954 Assert(BounceCheat || !(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick
956 forcefield_bounce = (TmapInfo[Segments[WallHitSeg].sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD);
958 if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) { //stop moving
960 // mprintf((0, "Object %i stuck at %i:%i\n", obj-Objects, WallHitSeg, WallHitSide));
961 add_stuck_object(obj, WallHitSeg, WallHitSide);
963 vm_vec_zero(&obj->mtype.phys_info.velocity);
967 else { // Slide object along wall
970 //We're constrained by wall, so subtract wall part from
973 wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity);
975 // mprintf((0, "%d", f2i(vm_vec_mag(&hit_info.hit_wallnorm)) ));
977 if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) { //bounce off wall
978 wall_part *= 2; //Subtract out wall part twice to achieve bounce
980 if (forcefield_bounce) {
981 check_vel = 1; //check for max velocity
982 if (obj->type == OBJ_PLAYER)
983 wall_part *= 2; //player bounce twice as much
986 if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) {
987 Assert(obj->mtype.phys_info.flags & PF_BOUNCE);
988 if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE)
989 obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE);
991 obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE;
994 bounced = 1; //this object bounced
997 vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part);
999 // mprintf((0, "Velocity at bounce time = %d\n", f2i(vm_vec_mag(&obj->mtype.phys_info.velocity))));
1001 //if (obj==ConsoleObject)
1002 // mprintf((0,"player vel = %x\n",vm_vec_mag_quick(&obj->mtype.phys_info.velocity)));
1005 fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
1007 if (vel > MAX_OBJECT_VEL)
1008 vm_vec_scale(&obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel));
1011 if (bounced && obj->type == OBJ_WEAPON)
1012 vm_vector_2_matrix(&obj->orient,&obj->mtype.phys_info.velocity,&obj->orient.uvec,NULL);
1015 if (obj == debug_obj) {
1016 printf(" sliding - wall_norm %x %x %x %x\n",wall_part,XYZ(&hit_info.hit_wallnorm));
1017 printf(" wall_part %x, new velocity = %x %x %x\n",wall_part,XYZ(&obj->mtype.phys_info.velocity));
1030 // Mark the hit object so that on a retry the fvi code
1031 // ignores this object.
1033 Assert(hit_info.hit_object != -1);
1035 // Calculcate the hit point between the two objects.
1036 { vms_vector *ppos0, *ppos1, pos_hit;
1038 ppos0 = &Objects[hit_info.hit_object].pos;
1040 size0 = Objects[hit_info.hit_object].size;
1042 Assert(size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!?
1043 //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1));
1044 //vm_vec_add2(&pos_hit, ppos0);
1045 vm_vec_sub(&pos_hit, ppos1, ppos0);
1046 vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1));
1048 old_vel = obj->mtype.phys_info.velocity;
1050 collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit);
1054 // Let object continue its movement
1055 if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) {
1056 //obj->pos = save_pos;
1058 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)) {
1059 //if (Objects[hit_info.hit_object].type == OBJ_POWERUP)
1060 ignore_obj_list[n_ignore_objs++] = hit_info.hit_object;
1069 if (TactileStick && obj==ConsoleObject && !(FrameCount & 15))
1070 Tactile_Xvibrate_clear ();
1076 Int3(); // Unexpected collision type: start point not in specified segment.
1077 mprintf((0,"Warning: Bad p0 in physics!!!\n"));
1080 // Unknown collision type returned from find_vector_intersection!!
1086 } while ( try_again );
1088 // Pass retry count info to AI.
1089 if (obj->control_type == CT_AI) {
1091 Ai_local_info[objnum].retry_count = count-1;
1093 Total_retries += count-1;
1099 //I'm not sure why we do this. I wish there were a comment that
1100 //explained it. I think maybe it only needs to be done if the object
1101 //is sliding, but I don't know
1102 if (!obj_stopped && !bounced) { //Set velocity from actual movement
1103 vms_vector moved_vec;
1105 vm_vec_sub(&moved_vec,&obj->pos,&start_pos);
1106 vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime));
1109 if (obj==ConsoleObject && (obj->mtype.phys_info.velocity.x==0 && obj->mtype.phys_info.velocity.y==0 && obj->mtype.phys_info.velocity.z==0) &&
1110 !(obj->mtype.phys_info.thrust.x==0 && obj->mtype.phys_info.thrust.y==0 && obj->mtype.phys_info.thrust.z==0)) {
1111 vms_vector center,bump_vec;
1113 //bump player a little towards center of segment to unstick
1115 compute_segment_center(¢er,&Segments[obj->segnum]);
1116 vm_vec_normalized_dir_quick(&bump_vec,¢er,&obj->pos);
1118 //don't bump player toward center of reactor segment
1119 if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN)
1120 vm_vec_negate(&bump_vec);
1122 vm_vec_scale_add2(&obj->pos,&bump_vec,obj->size/5);
1124 //if moving away from seg, might move out of seg, so update
1125 if (Segment2s[obj->segnum].special == SEGMENT_IS_CONTROLCEN)
1126 update_object_seg(obj);
1131 //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0);
1133 //if (obj->control_type == CT_FLYING)
1134 if (obj->mtype.phys_info.flags & PF_LEVELLING)
1135 do_physics_align_object( obj );
1138 //hack to keep player from going through closed doors
1139 if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (Physics_cheat_flag!=0xBADA55) ) {
1142 sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]);
1144 if (sidenum != -1) {
1146 if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) {
1148 int vertnum,num_faces,i;
1154 s = &Segments[orig_segnum].sides[sidenum];
1156 if (orig_segnum==-1)
1157 Error("orig_segnum == -1 in physics");
1159 create_abs_vertex_lists(&num_faces, vertex_list, orig_segnum, sidenum, __FILE__, __LINE__);
1161 //let's pretend this wall is not triangulated
1162 vertnum = vertex_list[0];
1164 if (vertex_list[i] < vertnum)
1165 vertnum = vertex_list[i];
1170 get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn );
1171 dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]);
1172 vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist);
1175 dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]);
1176 vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist);
1178 update_object_seg(obj);
1184 //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #ifndef NDEBUG
1185 //if end point not in segment, move object to last pos, or segment center
1186 if (get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask != 0)
1188 if (find_object_seg(obj)==-1) {
1192 if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) {
1193 obj->pos = obj->last_pos;
1194 obj_relink(objnum, n );
1197 compute_segment_center(&obj->pos,&Segments[obj->segnum]);
1198 obj->pos.x += objnum;
1200 if (obj->type == OBJ_WEAPON)
1201 obj->flags |= OF_SHOULD_BE_DEAD;
1204 //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #endif
1209 //--unused-- //tell us what the given object will do (as far as hiting walls) in
1210 //--unused-- //the given time (in seconds) t. Igores acceleration (sorry)
1211 //--unused-- //if check_objects is set, check with objects, else just with walls
1212 //--unused-- //returns fate, fills in hit time. If fate==HIT_NONE, hit_time undefined
1213 //--unused-- int physics_lookahead(object *obj,fix t,int fvi_flags,fix *hit_time, fvi_info *hit_info)
1215 //--unused-- vms_vector new_pos;
1216 //--unused-- int objnum,fate;
1217 //--unused-- fvi_query fq;
1219 //--unused-- Assert(obj->movement_type == MT_PHYSICS);
1221 //--unused-- objnum = obj-Objects;
1223 //--unused-- vm_vec_scale_add(&new_pos, &obj->pos, &obj->mtype.phys_info.velocity, t);
1225 //--unused-- fq.p0 = &obj->pos;
1226 //--unused-- fq.startseg = obj->segnum;
1227 //--unused-- fq.p1 = &new_pos;
1228 //--unused-- fq.rad = obj->size;
1229 //--unused-- fq.thisobjnum = objnum;
1230 //--unused-- fq.ignore_obj_list = NULL;
1231 //--unused-- fq.flags = fvi_flags;
1233 //--unused-- fate = find_vector_intersection(&fq,hit_info);
1235 //--unused-- if (fate != HIT_NONE) {
1236 //--unused-- fix dist,speed;
1238 //--unused-- dist = vm_vec_dist(&obj->pos, &hit_info->hit_pnt);
1240 //--unused-- speed = vm_vec_mag(&obj->mtype.phys_info.velocity);
1242 //--unused-- *hit_time = fixdiv(dist,speed);
1246 //--unused-- return fate;
1250 //Applies an instantaneous force on an object, resulting in an instantaneous
1251 //change in velocity.
1252 void phys_apply_force(object *obj,vms_vector *force_vec)
1255 // Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass,
1256 // in collision with crazy reactor robot thing on d2levf-s.
1257 if (obj->mtype.phys_info.mass == 0)
1260 if (obj->movement_type != MT_PHYSICS)
1264 if (TactileStick && obj==&Objects[Players[Player_num].objnum])
1265 Tactile_apply_force (force_vec,&obj->orient);
1268 //Add in acceleration due to force
1269 vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass));
1274 // ----------------------------------------------------------------
1275 // Do *dest = *delta unless:
1276 // *delta is pretty small
1277 // and they are of different signs.
1278 void physics_set_rotvel_and_saturate(fix *dest, fix delta)
1280 if ((delta ^ *dest) < 0) {
1281 if (abs(delta) < F1_0/8) {
1282 // mprintf((0, "D"));
1285 // mprintf((0, "d"));
1288 // mprintf((0, "!"));
1293 // ------------------------------------------------------------------------------------------------------
1294 // Note: This is the old ai_turn_towards_vector code.
1295 // phys_apply_rot used to call ai_turn_towards_vector until I fixed it, which broke phys_apply_rot.
1296 void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate)
1298 vms_angvec dest_angles, cur_angles;
1299 fix delta_p, delta_h;
1300 vms_vector *rotvel_ptr = &obj->mtype.phys_info.rotvel;
1302 // Make this object turn towards the goal_vector. Changes orientation, doesn't change direction of movement.
1303 // If no one moves, will be facing goal_vector in 1 second.
1305 // Detect null vector.
1306 if ((goal_vector->x == 0) && (goal_vector->y == 0) && (goal_vector->z == 0))
1309 // Make morph objects turn more slowly.
1310 if (obj->control_type == CT_MORPH)
1313 vm_extract_angles_vector(&dest_angles, goal_vector);
1314 vm_extract_angles_vector(&cur_angles, &obj->orient.fvec);
1316 delta_p = (dest_angles.p - cur_angles.p);
1317 delta_h = (dest_angles.h - cur_angles.h);
1319 if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
1320 if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
1321 if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
1322 if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
1324 delta_p = fixdiv(delta_p, rate);
1325 delta_h = fixdiv(delta_h, rate);
1327 if (abs(delta_p) < F1_0/16) delta_p *= 4;
1328 if (abs(delta_h) < F1_0/16) delta_h *= 4;
1330 physics_set_rotvel_and_saturate(&rotvel_ptr->x, delta_p);
1331 physics_set_rotvel_and_saturate(&rotvel_ptr->y, delta_h);
1335 // -----------------------------------------------------------------------------
1336 // Applies an instantaneous whack on an object, resulting in an instantaneous
1337 // change in orientation.
1338 void phys_apply_rot(object *obj,vms_vector *force_vec)
1342 if (obj->movement_type != MT_PHYSICS)
1345 vecmag = vm_vec_mag(force_vec)/8;
1346 if (vecmag < F1_0/256)
1348 else if (vecmag < obj->mtype.phys_info.mass >> 14)
1351 rate = fixdiv(obj->mtype.phys_info.mass, vecmag);
1352 if (obj->type == OBJ_ROBOT) {
1355 // Changed by mk, 10/24/95, claw guys should not slow down when attacking!
1356 if (!Robot_info[obj->id].thief && !Robot_info[obj->id].attack_type) {
1357 if (obj->ctype.ai_info.SKIP_AI_COUNT * FrameTime < 3*F1_0/4) {
1358 fix tval = fixdiv(F1_0, 8*FrameTime);
1363 if ( (d_rand() * 2) < (tval & 0xffff))
1365 obj->ctype.ai_info.SKIP_AI_COUNT += addval;
1366 // -- mk: too much stuff making hard to see my debug messages...mprintf((0, "FrameTime = %7.3f, addval = %i\n", f2fl(FrameTime), addval));
1375 // Turn amount inversely proportional to mass. Third parameter is seconds to do 360 turn.
1376 physics_turn_towards_vector(force_vec, obj, rate);
1382 //this routine will set the thrust for an object to a value that will
1383 //(hopefully) maintain the object's current velocity
1384 void set_thrust_from_velocity(object *obj)
1388 Assert(obj->movement_type == MT_PHYSICS);
1390 k = fixmuldiv(obj->mtype.phys_info.mass,obj->mtype.phys_info.drag,(f1_0-obj->mtype.phys_info.drag));
1392 vm_vec_copy_scale(&obj->mtype.phys_info.thrust,&obj->mtype.phys_info.velocity,k);