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