1 /*
2 PHYSICS.C
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 Wednesday, May 11, 1994 9:32:16 AM
22 
23 Saturday, May 21, 1994 11:36:31 PM
24 	missing effects due to map (i.e., gravity and collision detection).  last day in san
25 	jose after WWDC.
26 Sunday, May 22, 1994 11:14:55 AM
27 	there are two viable methods of running a synchronized network game.  the first is doom's,
28 	where each player shares with each other player only his control information for that tick
29 	(this imposes a maximum frame rate, as the state-of-the-world will be advanced at the same
30 	time on all machines).  the second is the continuous lag-tolerant model where each player
31 	shares absolute information with each other player as often as possible and local machines
32 	do their best at guessing what everyone else in the game is doing until they get better
33 	information.  whichever choice is made will change the physics drastically.  we're going to
34 	take the latter approach, and cache the KeyMap at interrupt time to be batch-processed
35 	later at frame time.
36 
37 Feb. 4, 2000 (Loren Petrich):
38 	Changed halt() to assert(false) for better debugging
39 
40 Feb 6, 2000 (Loren Petrich):
41 	Added access to size of physics-definition structure
42 
43 Feb 20, 2000 (Loren Petrich):
44 	Fixed chase-cam behavior: DROP_DEAD_HEIGHT is effectively zero for it.
45 	Also, set up-and-down bob to zero when it is active.
46 
47 Aug 31, 2000 (Loren Petrich):
48 	Added stuff for unpacking and packing
49 
50 May 16, 2002 (Woody Zenfell):
51     Letting user decide whether to auto-recenter when running
52 
53  June 14, 2003 (Woody Zenfell):
54 	update_player_physics_variables() can now operate in a reduced-impact mode
55 		that changes less of the game state.  Useful for partial-game-state
56 		save-and-restore code (as used by prediction mechanism).
57 */
58 
59 /*
60 running backwards shouldn�t mean doom in a fistfight
61 
62 //who decides on the physics model, anyway?  static_world-> or player->
63 //falling through gridlines and crapping on elevators has to do with variables->flags being wrong after the player dies
64 //absolute (or nearly-absolute) positioning information for yaw, pitch and velocity
65 //the physics model is too soft (more noticable at high frame rates)
66 //we can continually boot ourselves out of nearly-orthogonal walls by tiny amounts, resulting in a slide
67 //it�s fairly obvious that players can still end up in walls
68 //the recenter key should work faster
69 */
70 
71 #ifdef DEBUG
72 //#define DIVERGENCE_CHECK
73 #endif
74 
75 #include "cseries.h"
76 #include "render.h"
77 #include "map.h"
78 #include "player.h"
79 #include "interface.h"
80 #include "monsters.h"
81 #include "preferences.h"
82 
83 #define DONT_REPEAT_DEFINITIONS
84 #include "monster_definitions.h"
85 
86 #include "media.h"
87 
88 // LP addition:
89 #include "ChaseCam.h"
90 #include "Packing.h"
91 
92 #include <string.h>
93 #include <cstdlib>
94 #include <algorithm>
95 
96 /* ---------- constants */
97 
98 #define COEFFICIENT_OF_ABSORBTION 2
99 #define SMALL_ENOUGH_VELOCITY (constants->climbing_acceleration)
100 #define CLOSE_ENOUGH_TO_FLOOR WORLD_TO_FIXED(WORLD_ONE/16)
101 
102 #define AIRBORNE_HEIGHT WORLD_TO_FIXED(WORLD_ONE/16)
103 
104 #define DROP_DEAD_HEIGHT WORLD_TO_FIXED(WORLD_ONE_HALF)
105 
106 #define FLAGS_WHICH_PREVENT_RECENTERING (_turning|_looking|_sidestepping|_looking_vertically|_look_dont_turn|_sidestep_dont_turn)
107 
108 /* ---------- private prototypes */
109 
110 /* needed for set_player_position -SB */
111 /*static*/ struct physics_constants *get_physics_constants_for_model(short physics_model, uint32 action_flags);
112 /*static*/ void instantiate_physics_variables(struct physics_constants *constants, struct physics_variables *variables, short player_index, bool first_time, bool take_action);
113 static void physics_update(struct physics_constants *constants, struct physics_variables *variables, struct player_data *player, uint32 action_flags);
114 
115 /* ---------- globals */
116 
117 /* import constants, structures and globals for physics models */
118 #include "physics_models.h"
119 
120 /* ---------- code */
121 
122 #ifdef DIVERGENCE_CHECK
123 #define SAVED_POINT_COUNT 8192
124 static world_point3d *saved_points;
125 static angle *saved_thetas;
126 static short saved_point_count, saved_point_iterations= 0;
127 static bool saved_divergence_warning;
128 #endif
129 
130 static struct physics_constants physics_models[NUMBER_OF_PHYSICS_MODELS];
131 static fixed_yaw_pitch vir_aim_delta = {0, 0};
132 
133 /* every other field in the player structure should be valid when this call is made */
initialize_player_physics_variables(short player_index)134 void initialize_player_physics_variables(
135 	short player_index)
136 {
137 	if (player_index == local_player_index)
138 		resync_virtual_aim();
139 
140 	struct player_data *player= get_player_data(player_index);
141 	struct monster_data *monster= get_monster_data(player->monster_index);
142 	struct object_data *object= get_object_data(monster->object_index);
143 	struct physics_variables *variables= &player->variables;
144 	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, 0);
145 
146 //#ifdef DEBUG
147 	obj_set(*variables, 0x80);
148 //#endif
149 
150 	variables->head_direction= 0;
151 	variables->adjusted_yaw= variables->direction= INTEGER_TO_FIXED(object->facing);
152 	variables->adjusted_pitch= variables->elevation= 0;
153 	variables->angular_velocity= variables->vertical_angular_velocity= 0;
154 	variables->velocity= 0, variables->perpendicular_velocity= 0;
155 	variables->position.x= WORLD_TO_FIXED(object->location.x);
156 	variables->position.y= WORLD_TO_FIXED(object->location.y);
157 	variables->position.z= WORLD_TO_FIXED(object->location.z);
158 	variables->last_position= variables->position;
159 	variables->last_direction= variables->direction;
160 	/* .floor_height, .ceiling_height and .media_height will be calculated by instantiate, below */
161 
162 	variables->external_angular_velocity= 0;
163 	variables->external_velocity.i= variables->external_velocity.j= variables->external_velocity.k= 0;
164 	variables->actual_height= constants->height;
165 
166 	variables->step_phase= 0;
167 	variables->step_amplitude= 0;
168 
169 	variables->action= _player_stationary;
170 	variables->old_flags= variables->flags= 0; /* not recentering, not above ground, not below ground (i.e., on floor) */
171 
172 	/* setup shadow variables in player_data structure */
173 	instantiate_physics_variables(get_physics_constants_for_model(static_world->physics_model, 0),
174 		&player->variables, player_index, true, true);
175 
176 #ifdef DIVERGENCE_CHECK
177 	if (!saved_point_iterations)
178 	{
179 		saved_points= new world_point3d[SAVED_POINT_COUNT];
180 		saved_thetas= new angle[SAVED_POINT_COUNT];
181 	}
182 	saved_point_count= 0;
183 	saved_point_iterations+= 1;
184 	saved_divergence_warning= false;
185 #endif
186 }
187 
update_player_physics_variables(short player_index,uint32 action_flags,bool predictive)188 void update_player_physics_variables(
189 	short player_index,
190 	uint32 action_flags,
191 	bool predictive)
192 {
193 	struct player_data *player= get_player_data(player_index);
194 	struct physics_variables *variables= &player->variables;
195 	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, action_flags);
196 
197 	physics_update(constants, variables, player, action_flags);
198 	instantiate_physics_variables(constants, variables, player_index, false, !predictive);
199 
200 #ifdef DIVERGENCE_CHECK
201 	if (saved_point_count<SAVED_POINT_COUNT)
202 	{
203 		struct object_data *object= get_object_data(get_monster_data(player->monster_index)->object_index);
204 		world_point3d p= object->location;
205 		world_point3d *q= saved_points+saved_point_count;
206 		angle *facing= saved_thetas+saved_point_count;
207 
208 		if (saved_point_iterations==1)
209 		{
210 			saved_points[saved_point_count]= p;
211 			*facing= object->facing;
212 		}
213 		else
214 		{
215 			if (p.x!=q->x||p.y!=q->y||p.z!=q->z||*facing!=object->facing&&!saved_divergence_warning)
216 			{
217 				dprintf("divergence @ tick %d: (%d,%d,%d,%d)!=(%d,%d,%d,%d)", saved_point_count,
218 					q->x, q->y, q->z, *facing, p.x, p.y, p.z, object->facing);
219 				saved_divergence_warning= true;
220 			}
221 		}
222 
223 		saved_point_count+= 1;
224 	}
225 #endif
226 }
227 
adjust_player_for_polygon_height_change(short monster_index,short polygon_index,world_distance new_floor_height,world_distance new_ceiling_height)228 void adjust_player_for_polygon_height_change(
229 	short monster_index,
230 	short polygon_index,
231 	world_distance new_floor_height,
232 	world_distance new_ceiling_height)
233 {
234 	short player_index= monster_index_to_player_index(monster_index);
235 	struct player_data *player= get_player_data(player_index);
236 	struct physics_variables *variables= &player->variables;
237 	struct polygon_data *polygon= get_polygon_data(polygon_index);
238 	world_distance old_floor_height= polygon->floor_height;
239 
240 	(void) (new_ceiling_height);
241 
242 	if (player->supporting_polygon_index==polygon_index)
243 	{
244 		if (FIXED_TO_WORLD(variables->position.z)<=old_floor_height) /* must be <= */
245 		{
246 			variables->floor_height= variables->position.z= WORLD_TO_FIXED(new_floor_height);
247 			if (film_profile.fix_sliding_on_platforms && variables->external_velocity.k < 0)
248 			{
249 				variables->external_velocity.k = 0;
250 			}
251 
252 			if (PLAYER_IS_DEAD(player)) variables->external_velocity.k= 0;
253 		}
254 	}
255 }
256 
accelerate_player(short monster_index,world_distance vertical_velocity,angle direction,world_distance velocity)257 void accelerate_player(
258 	short monster_index,
259 	world_distance vertical_velocity,
260 	angle direction,
261 	world_distance velocity)
262 {
263 	short player_index= monster_index_to_player_index(monster_index);
264 	struct player_data *player= get_player_data(player_index);
265 	struct physics_variables *variables= &player->variables;
266 	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, 0);
267 
268 	variables->external_velocity.k+= WORLD_TO_FIXED(vertical_velocity);
269 	variables->external_velocity.k= PIN(variables->external_velocity.k, -constants->terminal_velocity, constants->terminal_velocity);
270 
271 	if (get_monster_definition_external(_monster_marine)->flags & _monster_can_grenade_climb)
272 	{
273 		variables->external_velocity.i= (cosine_table[direction]*velocity)>>(TRIG_SHIFT+WORLD_FRACTIONAL_BITS-FIXED_FRACTIONAL_BITS);
274 		variables->external_velocity.j= (sine_table[direction]*velocity)>>(TRIG_SHIFT+WORLD_FRACTIONAL_BITS-FIXED_FRACTIONAL_BITS);
275 	} else {
276 		variables->external_velocity.i+= (cosine_table[direction]*velocity)>>(TRIG_SHIFT+WORLD_FRACTIONAL_BITS-FIXED_FRACTIONAL_BITS);
277 		variables->external_velocity.j+= (sine_table[direction]*velocity)>>(TRIG_SHIFT+WORLD_FRACTIONAL_BITS-FIXED_FRACTIONAL_BITS);
278 	}
279 }
280 
get_absolute_pitch_range(_fixed * minimum,_fixed * maximum)281 void get_absolute_pitch_range(
282 	_fixed *minimum,
283 	_fixed *maximum)
284 {
285 	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, 0);
286 
287 	*minimum= -constants->maximum_elevation;
288 	*maximum= constants->maximum_elevation;
289 }
290 
kill_player_physics_variables(short player_index)291 void kill_player_physics_variables(
292 	short player_index)
293 {
294 }
295 
296 /* return a number in [-FIXED_ONE,FIXED_ONE] (arguably) */
get_player_forward_velocity_scale(short player_index)297 _fixed get_player_forward_velocity_scale(
298 	short player_index)
299 {
300 	struct player_data *player= get_player_data(player_index);
301 	struct physics_variables *variables= &player->variables;
302 	struct physics_constants *constants= get_physics_constants_for_model(static_world->physics_model, _run_dont_walk);
303 	_fixed dx= variables->position.x - variables->last_position.x;
304 	_fixed dy= variables->position.y - variables->last_position.y;
305 
306 	return INTEGER_TO_FIXED(((dx*cosine_table[FIXED_INTEGERAL_PART(variables->direction)] +
307 		dy*sine_table[FIXED_INTEGERAL_PART(variables->direction)])>>TRIG_SHIFT))/constants->maximum_forward_velocity;
308 }
309 
virtual_aim_delta()310 fixed_yaw_pitch virtual_aim_delta()
311 {
312 	return vir_aim_delta;
313 }
314 
resync_virtual_aim()315 void resync_virtual_aim()
316 {
317 	vir_aim_delta = {0, 0};
318 }
319 
process_aim_input(uint32 action_flags,fixed_yaw_pitch delta)320 uint32 process_aim_input(uint32 action_flags, fixed_yaw_pitch delta)
321 {
322 	// Classic behavior modes
323 	const bool classic_precision = !input_preferences->extra_mouse_precision;
324 	const bool classic_limits = input_preferences->classic_aim_speed_limits;
325 
326 	// Classic precision behavior:
327 	// - round magnitudes within (0, FIXED_ONE) to FIXED_ONE
328 	// - round toward zero instead of nearest
329 	// - lock virtual aim to physical aim
330 
331 	auto clamp = [classic_limits](fixed_angle theta, int encoding_bits) -> fixed_angle
332 	{
333 		const angle encoding_bias = (1<<encoding_bits)/2;
334 		const angle encoding_limit = encoding_bias - 1; // encoding supports [-bias, bias-1] but we want +/- symmetry
335 		const angle limit = classic_limits ? encoding_bias/2 : encoding_limit;
336 		return A1_PIN(theta, -limit*FIXED_ONE, limit*FIXED_ONE);
337 	};
338 
339 	auto round = [classic_precision](fixed_angle theta) -> angle
340 	{
341 		return classic_precision ?
342 			SGN(theta) * std::max<angle>(1, std::abs(theta/FIXED_ONE)) :
343 			(theta + SGN(theta)*FIXED_ONE/2) / FIXED_ONE;
344 	};
345 
346 	auto encode = [](angle theta, int encoding_bits) -> uint32
347 	{
348 		return (theta + (1<<encoding_bits)/2) & ((1<<encoding_bits) - 1);
349 	};
350 
351 	const physics_variables& local_phys = local_player->variables;
352 
353 	// The delta from the current physical aim to the requested virtual aim
354 	const fixed_yaw_pitch full_delta = {delta.yaw + vir_aim_delta.yaw, delta.pitch + vir_aim_delta.pitch};
355 
356 	// Process yaw input
357 	if (!(action_flags & _override_absolute_yaw))
358 	{
359 		const fixed_angle target = clamp(full_delta.yaw, ABSOLUTE_YAW_BITS); // the high-precision target delta
360 		const angle payload = round(target); // the low-precision delta to be encoded
361 
362 		if (payload || local_phys.angular_velocity)
363 			action_flags = SET_ABSOLUTE_YAW(action_flags, encode(payload, ABSOLUTE_YAW_BITS)) | _absolute_yaw_mode;
364 
365 		// Update virtual yaw
366 		vir_aim_delta.yaw = classic_precision ? 0 : target - payload*FIXED_ONE;
367 		assert(std::abs(vir_aim_delta.yaw) <= FIXED_ONE/2);
368 	}
369 
370 	// Explicit and automatic recentering do not occur under absolute pitch mode; therefore we
371 	// 1) try to always use absolute pitch mode if the user doesn't want auto-recentering; and
372 	// 2) always avoid absolute pitch mode while an explicit recentering operation is in progress
373 	// (pitch control is necessarily locked out until the recentering completes; no way to cancel)
374 
375 	const bool explicitly_recentering = local_phys.flags & _RECENTERING_BIT;
376 
377 	// Process pitch input
378 	if (!(action_flags & _override_absolute_pitch) && !explicitly_recentering)
379 	{
380 		const fixed_angle target = clamp(full_delta.pitch, ABSOLUTE_PITCH_BITS); // the high-precision target delta
381 		const angle payload = round(target); // the low-precision delta to be encoded
382 
383 		if (payload || local_phys.vertical_angular_velocity || dont_auto_recenter())
384 			action_flags = SET_ABSOLUTE_PITCH(action_flags, encode(payload, ABSOLUTE_PITCH_BITS)) | _absolute_pitch_mode;
385 
386 		// Update virtual pitch
387 		vir_aim_delta.pitch = classic_precision ? 0 : target - payload*FIXED_ONE;
388 		assert(std::abs(vir_aim_delta.pitch) <= FIXED_ONE/2);
389 	}
390 
391 	return action_flags;
392 }
393 
394 
395 /* ---------- private code */
396 
get_physics_constants_for_model(short physics_model,uint32 action_flags)397 /*static*/ struct physics_constants *get_physics_constants_for_model(
398 	short physics_model,
399 	uint32 action_flags)
400 {
401 	struct physics_constants *constants;
402 
403 	switch (physics_model)
404 	{
405 		case _editor_model:
406 		case _earth_gravity_model: constants= physics_models + ((action_flags&_run_dont_walk) ? _model_game_running : _model_game_walking); break;
407 		case _low_gravity_model:
408 			assert(false);
409 			break;
410 		default:
411 			assert(false);
412 			break;
413 	}
414 
415 	return constants;
416 }
417 
instantiate_physics_variables(struct physics_constants * constants,struct physics_variables * variables,short player_index,bool first_time,bool take_action)418 /*static*/ void instantiate_physics_variables(
419 	struct physics_constants *constants,
420 	struct physics_variables *variables,
421 	short player_index,
422 	bool first_time,
423 	bool take_action)
424 {
425 	struct player_data *player= get_player_data(player_index);
426 	struct monster_data *monster= get_monster_data(player->monster_index);
427 	struct object_data *legs= get_object_data(monster->object_index);
428 	struct object_data *torso= get_object_data(legs->parasitic_object);
429 	short old_polygon_index= legs->polygon;
430 	world_point3d new_location;
431 	world_distance adjusted_floor_height, adjusted_ceiling_height, object_floor;
432 	bool clipped;
433 	_fixed step_height;
434 	angle facing, elevation;
435 	_fixed fixed_facing;
436 
437 	/* convert to world coordinates before doing collision detection */
438 	new_location.x= FIXED_TO_WORLD(variables->position.x);
439 	new_location.y= FIXED_TO_WORLD(variables->position.y);
440 	new_location.z= FIXED_TO_WORLD(variables->position.z);
441 
442 	/* check for 2d collisions with walls and knock the player back out of the wall (because of
443 		the way the physics updates work, we don�t worry about collisions with the floor or
444 		ceiling).  ONLY MODIFY THE PLAYER�S FIXED_POINT3D POSITION IF WE HAD A COLLISION */
445 	if (PLAYER_IS_DEAD(player)) new_location.z+= FIXED_TO_WORLD(DROP_DEAD_HEIGHT);
446 	if (take_action && !first_time && player->last_supporting_polygon_index!=player->supporting_polygon_index) changed_polygon(player->last_supporting_polygon_index, player->supporting_polygon_index, player_index);
447 	player->last_supporting_polygon_index= first_time ? NONE : player->supporting_polygon_index;
448 	clipped= keep_line_segment_out_of_walls(legs->polygon, &legs->location, &new_location,
449 		WORLD_ONE/3, FIXED_TO_WORLD(variables->actual_height), &adjusted_floor_height, &adjusted_ceiling_height,
450 		&player->supporting_polygon_index);
451 	if (PLAYER_IS_DEAD(player)) new_location.z-= FIXED_TO_WORLD(DROP_DEAD_HEIGHT);
452 
453 	/* check for 2d collisions with solid objects and knock the player back out of the object.
454 		ONLY MODIFY THE PLAYER�S FIXED_POINT3D POSITION IF WE HAD A COLLISION. */
455 	object_floor= INT16_MIN;
456 	{
457 		short obstruction_index= legal_player_move(player->monster_index, &new_location, &object_floor);
458 
459 		if (obstruction_index!=NONE)
460 		{
461 			struct object_data *object= get_object_data(obstruction_index);
462 
463 			switch (GET_OBJECT_OWNER(object))
464 			{
465 				case _object_is_monster:
466 					if(take_action)
467 						bump_monster(player->monster_index, object->permutation);
468 				case _object_is_scenery:
469 					new_location.x= legs->location.x, new_location.y= legs->location.y;
470 					clipped= true;
471 					break;
472 
473 				default:
474 					assert(false);
475 					break;
476 			}
477 		}
478 	}
479 
480 	/* translate_map_object will handle crossing polygon boundaries */
481 	if (translate_map_object(monster->object_index, &new_location, NONE))
482 	{
483 		if (old_polygon_index==legs->polygon) clipped= true; /* oops; trans_map_obj destructively changed our position */
484 		if(take_action)
485 			monster_moved(player->monster_index, old_polygon_index);
486 	}
487 
488 	/* if our move got clipped, copy the new coordinate back into the physics variables */
489 	if (clipped)
490 	{
491 		variables->position.x= WORLD_TO_FIXED(new_location.x);
492 		variables->position.y= WORLD_TO_FIXED(new_location.y);
493 		variables->position.z= WORLD_TO_FIXED(new_location.z);
494 	}
495 
496 	/* shadow position in player structure, build camera location */
497 	step_height= (constants->step_amplitude*sine_table[variables->step_phase>>(FIXED_FRACTIONAL_BITS-ANGULAR_BITS+1)])>>TRIG_SHIFT;
498 	step_height= (step_height*variables->step_amplitude)>>FIXED_FRACTIONAL_BITS;
499 
500 	player->camera_location= new_location;
501 	if (PLAYER_IS_DEAD(player) && new_location.z<adjusted_floor_height) new_location.z= adjusted_floor_height;
502 	player->location= new_location;
503 	player->camera_location.z+= FIXED_TO_WORLD(step_height+variables->actual_height-constants->camera_height);
504 	player->step_height = FIXED_TO_WORLD(step_height);
505 	player->camera_polygon_index= legs->polygon;
506 
507 	/* shadow facing in player structure and object structure */
508 	fixed_facing= variables->direction+variables->head_direction;
509 	facing= FIXED_INTEGERAL_PART(fixed_facing), facing= NORMALIZE_ANGLE(facing);
510 	elevation= FIXED_INTEGERAL_PART(variables->elevation), elevation= NORMALIZE_ANGLE(elevation);
511 	legs->location.z= player->location.z;
512 	legs->facing= NORMALIZE_ANGLE(FIXED_INTEGERAL_PART(variables->direction)), torso->facing= player->facing= facing;
513 	player->elevation= elevation;
514 
515 	/* initialize floor_height and ceiling_height for next call to physics_update() */
516 	variables->floor_height= WORLD_TO_FIXED(MAX(adjusted_floor_height, object_floor));
517 	variables->ceiling_height= WORLD_TO_FIXED(adjusted_ceiling_height);
518 	{
519 		short media_index= get_polygon_data(legs->polygon)->media_index;
520 		// LP change: idiot-proofing
521 		media_data *media = get_media_data(media_index);
522 		world_distance media_height= (media_index==NONE || !media) ? INT16_MIN : media->height;
523 
524 		if (player->location.z<media_height) variables->flags|= _FEET_BELOW_MEDIA_BIT; else variables->flags&= (uint16)~_FEET_BELOW_MEDIA_BIT;
525 		if (player->camera_location.z<media_height) variables->flags|= _HEAD_BELOW_MEDIA_BIT; else variables->flags&= (uint16)~_HEAD_BELOW_MEDIA_BIT;
526 	}
527 
528 	// so our sounds come from the right place
529 	monster->sound_location= player->camera_location;
530 	monster->sound_polygon_index= player->camera_polygon_index;
531 }
532 
533 /* separate physics_constant structures are passed in for running/walking modes */
534 // ZZZ note: 'player' is only used in this routine for PLAYER_IS_DEAD(player) - as such,
535 // perhaps this should take an "is_dead" flag as a parameter instead of the player structure.
physics_update(struct physics_constants * constants,struct physics_variables * variables,struct player_data * player,uint32 action_flags)536 static void physics_update(
537 	struct physics_constants *constants,
538 	struct physics_variables *variables,
539 	struct player_data *player,
540 	uint32 action_flags)
541 {
542 	fixed_point3d new_position;
543 	short sine, cosine;
544 	_fixed delta_z;
545 	_fixed delta; /* used as a scratch �change� variable */
546 
547 	const bool player_is_local = (player == local_player);
548 
549 	if (PLAYER_IS_DEAD(player)) /* dead players immediately loose all bodily control */
550 	{
551 		int32 dot_product;
552 
553 		cosine= cosine_table[FIXED_INTEGERAL_PART(variables->direction)], sine= sine_table[FIXED_INTEGERAL_PART(variables->direction)];
554 		dot_product= ((((variables->velocity*cosine)>>TRIG_SHIFT) + variables->external_velocity.i)*cosine +
555 			(((variables->velocity*sine)>>TRIG_SHIFT) + variables->external_velocity.j)*sine)>>TRIG_SHIFT;
556 
557 		if (dot_product>0 && dot_product<(constants->maximum_forward_velocity>>4)) dot_product= 0;
558 		switch (SGN(dot_product))
559 		{
560 			case -1: action_flags= _looking_up; break;
561 			case 1: action_flags= _looking_down; break;
562 			case 0: action_flags= 0; break;
563 			default:
564 				assert(false);
565 				break;
566 		}
567 
568 		variables->floor_height-= DROP_DEAD_HEIGHT;
569 
570 		// Prohibit twitching while dead (!)
571 		if (player_is_local)
572 			resync_virtual_aim();
573 	}
574 	delta_z= variables->position.z-variables->floor_height;
575 
576 	/* process modifier keys (sidestepping and looking) into normal actions */
577 	if ((action_flags&_turning) && (action_flags&_sidestep_dont_turn) && !(action_flags&_absolute_yaw_mode))
578 	{
579 		if (action_flags&_turning_left) action_flags|= _sidestepping_left;
580 		if (action_flags&_turning_right) action_flags|= _sidestepping_right;
581 		action_flags&= ~_turning;
582 	}
583 	if ((action_flags&_moving) && (action_flags&_look_dont_turn) && !(action_flags&_absolute_position_mode))
584 	{
585 		if (action_flags&_moving_forward) action_flags|= _looking_up;
586 		if (action_flags&_moving_backward) action_flags|= _looking_down;
587 		action_flags&= ~_moving;
588 		action_flags&= ~_absolute_pitch_mode;
589 	}
590 
591 	/* handle turning left or right; if we�ve exceeded our maximum velocity lock out user actions
592 		until we return to a legal range */
593 	if (action_flags&_absolute_yaw_mode)
594 	{
595 		variables->angular_velocity= (GET_ABSOLUTE_YAW(action_flags)-MAXIMUM_ABSOLUTE_YAW/2)<<(FIXED_FRACTIONAL_BITS); // !!!!!!!!!!
596 	}
597 	else
598 	{
599 		if (variables->angular_velocity<-constants->maximum_angular_velocity||variables->angular_velocity>constants->maximum_angular_velocity) action_flags&= ~_turning;
600 		switch (action_flags&_turning)
601 		{
602 			case _turning_left:
603 				delta= variables->angular_velocity>0 ? constants->angular_acceleration+constants->angular_deceleration : constants->angular_acceleration;
604 				variables->angular_velocity= FLOOR(variables->angular_velocity-delta, -constants->maximum_angular_velocity);
605 				break;
606 			case _turning_right:
607 				delta= variables->angular_velocity<0 ? constants->angular_acceleration+constants->angular_deceleration : constants->angular_acceleration;
608 				variables->angular_velocity= CEILING(variables->angular_velocity+delta, constants->maximum_angular_velocity);
609 				break;
610 
611 			default: /* slow down */
612 				variables->angular_velocity= (variables->angular_velocity>=0) ?
613 					FLOOR(variables->angular_velocity-constants->angular_deceleration, 0) :
614 					CEILING(variables->angular_velocity+constants->angular_deceleration, 0);
615 				break;
616 		}
617 
618 		/* handling looking left/right */
619 		switch (action_flags&_looking)
620 		{
621 			case _looking_left:
622 				variables->head_direction= FLOOR(variables->head_direction-constants->fast_angular_velocity, -constants->fast_angular_maximum);
623 				break;
624 			case _looking_right:
625 				variables->head_direction= CEILING(variables->head_direction+constants->fast_angular_velocity, constants->fast_angular_maximum);
626 				break;
627 			case _looking: /* do nothing if both keys are down */
628 				break;
629 
630 			default: /* recenter head */
631 				variables->head_direction= (variables->head_direction>=0) ?
632 					FLOOR(variables->head_direction-constants->fast_angular_velocity, 0) :
633 					CEILING(variables->head_direction+constants->fast_angular_velocity, 0);
634 		}
635 
636 		// Let yaw controls resync virtual yaw
637 		if (player_is_local && (action_flags & (_turning|_looking)) != 0)
638 			vir_aim_delta.yaw = 0;
639 	}
640 
641 	if (action_flags&_absolute_pitch_mode)
642 	{
643 		variables->vertical_angular_velocity= (GET_ABSOLUTE_PITCH(action_flags)-MAXIMUM_ABSOLUTE_PITCH/2)<<(FIXED_FRACTIONAL_BITS);
644 	}
645 	else
646 	{
647 		/* if the user touched the recenter key, set the recenter flag and override all up/down
648 			keypresses with our own */
649 		if (action_flags&_looking_center) variables->flags|= _RECENTERING_BIT;
650 		if (variables->flags&_RECENTERING_BIT)
651 		{
652 			action_flags&= ~_looking_vertically;
653 			action_flags|= variables->elevation<0 ? _looking_up : _looking_down;
654 		}
655 
656 		/* handle looking up and down; if we�re moving at our terminal velocity forward or backward,
657 			without any side-to-side motion, recenter our head vertically */
658 
659 		if (!(action_flags&FLAGS_WHICH_PREVENT_RECENTERING)) /* can�t recenter if any of these are true */
660 		{
661 			if (((action_flags&_moving_forward) && (variables->velocity==constants->maximum_forward_velocity)) ||
662 				((action_flags&_moving_backward) && (variables->velocity==-constants->maximum_backward_velocity)))
663 			{
664 				if (variables->elevation<0)
665 				{
666 					variables->elevation= CEILING(variables->elevation+constants->angular_recentering_velocity, 0);
667 				}
668 				else
669 				{
670 					variables->elevation= FLOOR(variables->elevation-constants->angular_recentering_velocity, 0);
671 				}
672 
673 				// Let auto-recentering resync virtual pitch
674 				if (player_is_local)
675 					vir_aim_delta.pitch = 0;
676 			}
677 		}
678 
679 		switch (action_flags&_looking_vertically)
680 		{
681 			case _looking_down:
682 				delta= variables->vertical_angular_velocity>0 ? constants->angular_acceleration+constants->angular_deceleration : constants->angular_acceleration;
683 				variables->vertical_angular_velocity= FLOOR(variables->vertical_angular_velocity-delta, PLAYER_IS_DEAD(player) ? -(constants->maximum_angular_velocity>>3) : -constants->maximum_angular_velocity);
684 				break;
685 			case _looking_up:
686 				delta= variables->vertical_angular_velocity<0 ? constants->angular_acceleration+constants->angular_deceleration : constants->angular_acceleration;
687 				variables->vertical_angular_velocity= CEILING(variables->vertical_angular_velocity+delta, PLAYER_IS_DEAD(player) ? (constants->maximum_angular_velocity>>3) : constants->maximum_angular_velocity);
688 				break;
689 
690 			default: /* if no key is being held down, decelerate; if the player is moving try and return to phi==0 */
691 				variables->vertical_angular_velocity= (variables->vertical_angular_velocity>=0) ?
692 					FLOOR(variables->vertical_angular_velocity-constants->angular_deceleration, 0) :
693 					CEILING(variables->vertical_angular_velocity+constants->angular_deceleration, 0);
694 				break;
695 		}
696 
697 		// Let pitch controls resync virtual pitch
698 		if (player_is_local && (action_flags & _looking_vertically) != 0)
699 			vir_aim_delta.pitch = 0;
700 	}
701 
702 	/* if we�re on the ground (or rising up from it), allow movement; if we�re flying through
703 		the air, don�t let the player adjust his velocity in any way */
704 	if (delta_z<=0 || (variables->flags&_HEAD_BELOW_MEDIA_BIT))
705 	{
706 		if (action_flags&_absolute_position_mode)
707 		{
708 			short encoded_delta= GET_ABSOLUTE_POSITION(action_flags)-MAXIMUM_ABSOLUTE_POSITION/2;
709 
710 			if (encoded_delta<0)
711 			{
712 				variables->velocity= (encoded_delta*constants->maximum_backward_velocity)>>(ABSOLUTE_POSITION_BITS-1);
713 			}
714 			else
715 			{
716 				variables->velocity= (encoded_delta*constants->maximum_forward_velocity)>>(ABSOLUTE_POSITION_BITS-1);
717 			}
718 		}
719 		else
720 		{
721 			/* handle moving forward or backward; if we�ve exceeded our maximum velocity lock out user actions
722 				until we return to a legal range */
723 			if (variables->velocity<-constants->maximum_backward_velocity||variables->velocity>constants->maximum_forward_velocity) action_flags&= ~_moving;
724 			switch (action_flags&_moving)
725 			{
726 				case _moving_forward:
727 					delta= variables->velocity<0 ? constants->deceleration+constants->acceleration : constants->acceleration;
728 					variables->velocity= CEILING(variables->velocity+delta, constants->maximum_forward_velocity);
729 					break;
730 				case _moving_backward:
731 					delta= variables->velocity>0 ? constants->deceleration+constants->acceleration : constants->acceleration;
732 					variables->velocity= FLOOR(variables->velocity-delta, -constants->maximum_backward_velocity);
733 					break;
734 
735 				default: /* slow down */
736 					variables->velocity= (variables->velocity>=0) ?
737 						FLOOR(variables->velocity-constants->deceleration, 0) :
738 						CEILING(variables->velocity+constants->deceleration, 0);
739 					break;
740 			}
741 		}
742 
743 		/* handle sidestepping left or right; if we�ve exceeded our maximum velocity lock out user actions
744 			until we return to a legal range */
745 		if (variables->perpendicular_velocity<-constants->maximum_perpendicular_velocity||variables->perpendicular_velocity>constants->maximum_perpendicular_velocity) action_flags&= ~_sidestepping;
746 		switch (action_flags&_sidestepping)
747 		{
748 			case _sidestepping_left:
749 				delta= variables->perpendicular_velocity>0 ? constants->acceleration+constants->deceleration : constants->acceleration;
750 				variables->perpendicular_velocity= FLOOR(variables->perpendicular_velocity-delta, -constants->maximum_perpendicular_velocity);
751 				break;
752 			case _sidestepping_right:
753 				delta= variables->perpendicular_velocity<0 ? constants->acceleration+constants->deceleration : constants->acceleration;
754 				variables->perpendicular_velocity= CEILING(variables->perpendicular_velocity+delta, constants->maximum_perpendicular_velocity);
755 				break;
756 
757 			default: /* slow down */
758 				variables->perpendicular_velocity= (variables->perpendicular_velocity>=0) ?
759 					FLOOR(variables->perpendicular_velocity-constants->deceleration, 0) :
760 					CEILING(variables->perpendicular_velocity+constants->deceleration, 0);
761 				break;
762 		}
763 	}
764 
765 	/* change vertical_velocity based on difference between player height and surface height
766 		(if we are standing on an object, like a body, take that into account, too: this
767 		means a player could actually use bodies as ramps to reach ledges he couldn't
768 		otherwise jump to).  we should think about absorbing forward (or perpendicular)
769 		velocity to compensate for an increase in vertical velocity, which would slow down
770 		a player climbing stairs, etc. */
771 	if (delta_z<0)
772 	{
773 		variables->external_velocity.k= CEILING(variables->external_velocity.k+constants->climbing_acceleration, constants->terminal_velocity);
774 	}
775 	if (delta_z>0)
776 	{
777 		_fixed gravity= constants->gravitational_acceleration;
778 		_fixed terminal_velocity= constants->terminal_velocity;
779 
780 		if (static_world->environment_flags&_environment_low_gravity) gravity>>= 1;
781 		if (variables->flags&_FEET_BELOW_MEDIA_BIT) gravity>>= 1, terminal_velocity>>= 1;
782 
783 		variables->external_velocity.k= FLOOR(variables->external_velocity.k-gravity, -terminal_velocity);
784 	}
785 
786 	if ((action_flags&_swim) && (variables->flags&_HEAD_BELOW_MEDIA_BIT) && variables->external_velocity.k<10*constants->climbing_acceleration)
787 	{
788 		variables->external_velocity.k+= constants->climbing_acceleration;
789 	}
790 
791 	// Apply vertical angular velocity
792 	variables->elevation+= variables->vertical_angular_velocity;
793 
794 	// Clamp virtual pitch to the effective limits of the low-precision physical pitch
795 	// (this won't enlarge the virtual pitch delta)
796 	if (player_is_local)
797 	{
798 		const fixed_angle min_pitch = FIXED_INTEGERAL_PART(-constants->maximum_elevation) * FIXED_ONE;
799 		const fixed_angle max_pitch = FIXED_INTEGERAL_PART(constants->maximum_elevation) * FIXED_ONE;
800 		const fixed_angle unclamped_physical_pitch = FIXED_INTEGERAL_PART(variables->elevation) * FIXED_ONE;
801 		const fixed_angle unclamped_virtual_pitch = unclamped_physical_pitch + vir_aim_delta.pitch;
802 		const fixed_angle clamped_physical_pitch = A1_PIN(unclamped_physical_pitch, min_pitch, max_pitch);
803 		const fixed_angle clamped_virtual_pitch = A1_PIN(unclamped_virtual_pitch, min_pitch, max_pitch);
804 		const fixed_angle new_delta = clamped_virtual_pitch - clamped_physical_pitch;
805 		assert(std::abs(new_delta) <= std::abs(vir_aim_delta.pitch));
806 		vir_aim_delta.pitch = new_delta;
807 	}
808 
809 	// Clamp high-precision physical pitch to physics model limits
810 	// (note that the low-precision pitch can slightly violate a non-integral lower bound due to rounding toward -inf)
811 	variables->elevation= PIN(variables->elevation, -constants->maximum_elevation, constants->maximum_elevation);
812 
813 	// If we're explicitly recentering and have reached or passed 0 pitch, stop at 0
814 	if ((variables->flags&_RECENTERING_BIT) && !(action_flags&_absolute_pitch_mode))
815 	{
816 		if ((variables->elevation<=0&&(action_flags&_looking_down))||(variables->elevation>=0&&(action_flags&_looking_up)))
817 		{
818 			variables->elevation= variables->vertical_angular_velocity= 0;
819 			variables->flags&= (uint16)~_RECENTERING_BIT;
820 		}
821 	}
822 
823 	/* change the player�s heading based on his angular velocities */
824 	variables->last_direction= variables->direction;
825 	variables->direction+= variables->angular_velocity;
826 	if (variables->direction<0) variables->direction+= INTEGER_TO_FIXED(FULL_CIRCLE);
827 	if (variables->direction>=INTEGER_TO_FIXED(FULL_CIRCLE)) variables->direction-= INTEGER_TO_FIXED(FULL_CIRCLE);
828 
829 	/* change the player�s x,y position based on his direction and velocities (parallel and perpendicular)  */
830 	new_position= variables->position;
831 	cosine= cosine_table[FIXED_INTEGERAL_PART(variables->direction)], sine= sine_table[FIXED_INTEGERAL_PART(variables->direction)];
832 	new_position.x+= (variables->velocity*cosine-variables->perpendicular_velocity*sine)>>TRIG_SHIFT;
833 	new_position.y+= (variables->velocity*sine+variables->perpendicular_velocity*cosine)>>TRIG_SHIFT;
834 
835 	/* set above/below floor flags, remember old flags */
836 	variables->old_flags= variables->flags;
837 	if (new_position.z<variables->floor_height) variables->flags|= _BELOW_GROUND_BIT; else variables->flags&= (uint16)~_BELOW_GROUND_BIT;
838 	if (new_position.z>variables->floor_height) variables->flags|= _ABOVE_GROUND_BIT; else variables->flags&= (uint16)~_ABOVE_GROUND_BIT;
839 
840 	/* if we just landed on the ground, or we just came up through the ground, absorb some of
841 		the player�s external_velocity.k (and in the case of hitting the ground, reflect it) */
842 	if (variables->external_velocity.k>0 && (variables->old_flags&_BELOW_GROUND_BIT) && !(variables->flags&_BELOW_GROUND_BIT))
843 	{
844 		variables->external_velocity.k/= 2*COEFFICIENT_OF_ABSORBTION; /* slow down */
845 	}
846 	if (variables->external_velocity.k>0 && new_position.z+variables->actual_height>=variables->ceiling_height)
847 	{
848 		variables->external_velocity.k/= -COEFFICIENT_OF_ABSORBTION, new_position.z= variables->ceiling_height-variables->actual_height; // &&variables->position.z+variables->actual_height<variables->ceiling_height
849 	}
850 	if (variables->external_velocity.k<0&&!(variables->old_flags&_BELOW_GROUND_BIT)&&!(variables->flags&_ABOVE_GROUND_BIT))
851 	{
852 		variables->external_velocity.k/= -COEFFICIENT_OF_ABSORBTION;
853 	}
854 
855 	_fixed small_enough_velocity;
856 	if (get_monster_definition_external(_monster_marine)->flags & _monster_can_grenade_climb) {
857 		_fixed gravity= constants->gravitational_acceleration;
858 		if (static_world->environment_flags&_environment_low_gravity) gravity>>= 1;
859 		if (variables->flags&_FEET_BELOW_MEDIA_BIT) gravity>>= 1;
860 
861 		small_enough_velocity = gravity;
862 	}
863 	else
864 	{
865 		small_enough_velocity = SMALL_ENOUGH_VELOCITY;
866 	}
867 	if (ABS(variables->external_velocity.k)<small_enough_velocity &&
868 		ABS(variables->floor_height-new_position.z)<CLOSE_ENOUGH_TO_FLOOR)
869 	{
870 		variables->external_velocity.k= 0, new_position.z= variables->floor_height;
871 		variables->flags&= ~(_BELOW_GROUND_BIT|_ABOVE_GROUND_BIT);
872 	}
873 
874 	/* change the player�s z position based on his vertical velocity (if we hit the ground coming down
875 		then bounce and absorb most of the blow */
876 	new_position.x+= variables->external_velocity.i;
877 	new_position.y+= variables->external_velocity.j;
878 	new_position.z+= variables->external_velocity.k;
879 
880 	{
881 		short dx= variables->external_velocity.i, dy= variables->external_velocity.j;
882 		_fixed delta= (delta_z<=0) ? constants->external_deceleration : (constants->external_deceleration>>2);
883 		int32 magnitude= isqrt(dx*dx + dy*dy);
884 
885 		if (magnitude && magnitude>ABS(delta))
886 		{
887 			variables->external_velocity.i-= (dx*delta)/magnitude;
888 			variables->external_velocity.j-= (dy*delta)/magnitude;
889 		}
890 		else
891 		{
892 			variables->external_velocity.i= variables->external_velocity.j= 0;
893 		}
894 	}
895 
896 	/* lower the player�s externally-induced angular velocity */
897 	variables->external_angular_velocity= (variables->external_angular_velocity>=0) ?
898 		FLOOR(variables->external_angular_velocity-constants->external_angular_deceleration, 0) :
899 		CEILING(variables->external_angular_velocity+constants->external_angular_deceleration, 0);
900 
901 	/* instantiate new position, save old position */
902 	variables->last_position= variables->position;
903 	variables->position= new_position;
904 
905 	/* if the player is moving, adjust step_phase by step_delta (if the player isn�t moving
906 		continue to adjust step_phase until it is zero)  if the player is in the air, don�t
907 		update phase until he lands. */
908 	variables->flags&= (uint16)~_STEP_PERIOD_BIT;
909 	if (constants->maximum_forward_velocity)
910 		variables->step_amplitude= (MAX(ABS(variables->velocity), ABS(variables->perpendicular_velocity))*FIXED_ONE)/constants->maximum_forward_velocity;
911 	else	// CB: "Missed Island" physics would produce a division by 0
912 		variables->step_amplitude= MAX(ABS(variables->velocity), ABS(variables->perpendicular_velocity))*FIXED_ONE;
913 	if (delta_z>=0)
914 	{
915 		if (variables->velocity||variables->perpendicular_velocity)
916 		{
917 //			fixed old_step_phase= variables->step_phase;
918 
919 			if ((variables->step_phase+= constants->step_delta)>=FIXED_ONE)
920 			{
921 				variables->step_phase-= FIXED_ONE;
922 				variables->flags|= _STEP_PERIOD_BIT;
923 			}
924 //			else
925 //			{
926 //				if (variables->step_phase>=FIXED_ONE_HALF && old_step_phase<FIXED_ONE_HALF)
927 //				{
928 //					variables->flags|= _STEP_PERIOD_BIT;
929 //				}
930 //			}
931 		}
932 		else
933 		{
934 			if (variables->step_phase)
935 			{
936 				if (variables->step_phase>FIXED_ONE_HALF)
937 				{
938 					if ((variables->step_phase+= constants->step_delta)>=FIXED_ONE) variables->step_phase= 0;
939 				}
940 				else
941 				{
942 					if ((variables->step_phase-= constants->step_delta)<0) variables->step_phase= 0;
943 				}
944 			}
945 		}
946 	}
947 
948 	if (delta_z >= (PLAYER_IS_DEAD(player) ? (AIRBORNE_HEIGHT+DROP_DEAD_HEIGHT) : AIRBORNE_HEIGHT))
949 	{
950 		variables->action= _player_airborne;
951 	}
952 	else
953 	{
954 		if (variables->angular_velocity||variables->velocity||variables->perpendicular_velocity)
955 		{
956 			variables->action= (action_flags&_run_dont_walk) ? _player_running : _player_walking;
957 		}
958 		else
959 		{
960 			variables->action= (variables->external_velocity.i||variables->external_velocity.j||variables->external_velocity.k) ? _player_sliding : _player_stationary;
961 		}
962 	}
963 }
964 
965 
unpack_physics_constants(uint8 * Stream,size_t Count)966 uint8 *unpack_physics_constants(uint8 *Stream, size_t Count)
967 {
968 	return unpack_physics_constants(Stream,physics_models,Count);
969 }
970 
unpack_physics_constants(uint8 * Stream,physics_constants * Objects,size_t Count)971 uint8 *unpack_physics_constants(uint8 *Stream, physics_constants *Objects, size_t Count)
972 {
973 	uint8* S = Stream;
974 	physics_constants* ObjPtr = Objects;
975 
976 	for (size_t k = 0; k < Count; k++, ObjPtr++)
977 	{
978 		StreamToValue(S,ObjPtr->maximum_forward_velocity);
979 		StreamToValue(S,ObjPtr->maximum_backward_velocity);
980 		StreamToValue(S,ObjPtr->maximum_perpendicular_velocity);
981 		StreamToValue(S,ObjPtr->acceleration);
982 		StreamToValue(S,ObjPtr->deceleration);
983 		StreamToValue(S,ObjPtr->airborne_deceleration);
984 		StreamToValue(S,ObjPtr->gravitational_acceleration);
985 		StreamToValue(S,ObjPtr->climbing_acceleration);
986 		StreamToValue(S,ObjPtr->terminal_velocity);
987 		StreamToValue(S,ObjPtr->external_deceleration);
988 
989 		StreamToValue(S,ObjPtr->angular_acceleration);
990 		StreamToValue(S,ObjPtr->angular_deceleration);
991 		StreamToValue(S,ObjPtr->maximum_angular_velocity);
992 		StreamToValue(S,ObjPtr->angular_recentering_velocity);
993 		StreamToValue(S,ObjPtr->fast_angular_velocity);
994 		StreamToValue(S,ObjPtr->fast_angular_maximum);
995 		StreamToValue(S,ObjPtr->maximum_elevation);
996 		StreamToValue(S,ObjPtr->external_angular_deceleration);
997 
998 		StreamToValue(S,ObjPtr->step_delta);
999 		StreamToValue(S,ObjPtr->step_amplitude);
1000 		StreamToValue(S,ObjPtr->radius);
1001 		StreamToValue(S,ObjPtr->height);
1002 		StreamToValue(S,ObjPtr->dead_height);
1003 		StreamToValue(S,ObjPtr->camera_height);
1004 		StreamToValue(S,ObjPtr->splash_height);
1005 
1006 		StreamToValue(S,ObjPtr->half_camera_separation);
1007 	}
1008 
1009 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_physics_constants));
1010 	return S;
1011 }
1012 
unpack_m1_physics_constants(uint8 * Stream,size_t Count)1013 uint8* unpack_m1_physics_constants(uint8* Stream, size_t Count)
1014 {
1015 	static const int SIZEOF_old_physics_entry = 100;
1016 	uint8* S = Stream + SIZEOF_old_physics_entry; // first is "editor" record
1017 	physics_constants* ObjPtr = physics_models;
1018 
1019 	for (size_t k = 0; k < Count - 1; k++, ObjPtr++)
1020 	{
1021 		StreamToValue(S,ObjPtr->maximum_forward_velocity);
1022 		StreamToValue(S,ObjPtr->maximum_backward_velocity);
1023 		StreamToValue(S,ObjPtr->maximum_perpendicular_velocity);
1024 		StreamToValue(S,ObjPtr->acceleration);
1025 		StreamToValue(S,ObjPtr->deceleration);
1026 		StreamToValue(S,ObjPtr->airborne_deceleration);
1027 		StreamToValue(S,ObjPtr->gravitational_acceleration);
1028 		StreamToValue(S,ObjPtr->climbing_acceleration);
1029 		StreamToValue(S,ObjPtr->terminal_velocity);
1030 		StreamToValue(S,ObjPtr->external_deceleration);
1031 
1032 		StreamToValue(S,ObjPtr->angular_acceleration);
1033 		StreamToValue(S,ObjPtr->angular_deceleration);
1034 		StreamToValue(S,ObjPtr->maximum_angular_velocity);
1035 		StreamToValue(S,ObjPtr->angular_recentering_velocity);
1036 		StreamToValue(S,ObjPtr->fast_angular_velocity);
1037 		StreamToValue(S,ObjPtr->fast_angular_maximum);
1038 		StreamToValue(S,ObjPtr->maximum_elevation);
1039 		StreamToValue(S,ObjPtr->external_angular_deceleration);
1040 
1041 		StreamToValue(S,ObjPtr->step_delta);
1042 		StreamToValue(S,ObjPtr->step_amplitude);
1043 		StreamToValue(S,ObjPtr->radius);
1044 		StreamToValue(S,ObjPtr->height);
1045 		StreamToValue(S,ObjPtr->dead_height);
1046 		StreamToValue(S,ObjPtr->camera_height);
1047 		ObjPtr->splash_height = 0;
1048 
1049 		StreamToValue(S,ObjPtr->half_camera_separation);
1050 	}
1051 
1052 	return S;
1053 }
1054 
pack_physics_constants(uint8 * Stream,size_t Count)1055 uint8 *pack_physics_constants(uint8 *Stream, size_t Count)
1056 {
1057 	return pack_physics_constants(Stream,physics_models,Count);
1058 }
1059 
pack_physics_constants(uint8 * Stream,physics_constants * Objects,size_t Count)1060 uint8 *pack_physics_constants(uint8 *Stream, physics_constants *Objects, size_t Count)
1061 {
1062 	uint8* S = Stream;
1063 	physics_constants* ObjPtr = Objects;
1064 
1065 	for (size_t k = 0; k < Count; k++, ObjPtr++)
1066 	{
1067 		ValueToStream(S,ObjPtr->maximum_forward_velocity);
1068 		ValueToStream(S,ObjPtr->maximum_backward_velocity);
1069 		ValueToStream(S,ObjPtr->maximum_perpendicular_velocity);
1070 		ValueToStream(S,ObjPtr->acceleration);
1071 		ValueToStream(S,ObjPtr->deceleration);
1072 		ValueToStream(S,ObjPtr->airborne_deceleration);
1073 		ValueToStream(S,ObjPtr->gravitational_acceleration);
1074 		ValueToStream(S,ObjPtr->climbing_acceleration);
1075 		ValueToStream(S,ObjPtr->terminal_velocity);
1076 		ValueToStream(S,ObjPtr->external_deceleration);
1077 
1078 		ValueToStream(S,ObjPtr->angular_acceleration);
1079 		ValueToStream(S,ObjPtr->angular_deceleration);
1080 		ValueToStream(S,ObjPtr->maximum_angular_velocity);
1081 		ValueToStream(S,ObjPtr->angular_recentering_velocity);
1082 		ValueToStream(S,ObjPtr->fast_angular_velocity);
1083 		ValueToStream(S,ObjPtr->fast_angular_maximum);
1084 		ValueToStream(S,ObjPtr->maximum_elevation);
1085 		ValueToStream(S,ObjPtr->external_angular_deceleration);
1086 
1087 		ValueToStream(S,ObjPtr->step_delta);
1088 		ValueToStream(S,ObjPtr->step_amplitude);
1089 		ValueToStream(S,ObjPtr->radius);
1090 		ValueToStream(S,ObjPtr->height);
1091 		ValueToStream(S,ObjPtr->dead_height);
1092 		ValueToStream(S,ObjPtr->camera_height);
1093 		ValueToStream(S,ObjPtr->splash_height);
1094 
1095 		ValueToStream(S,ObjPtr->half_camera_separation);
1096 	}
1097 
1098 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_physics_constants));
1099 	return S;
1100 }
1101 
init_physics_constants()1102 void init_physics_constants()
1103 {
1104 	memcpy(physics_models, original_physics_models, sizeof(physics_models));
1105 }
1106 
1107 // LP addition: get number of physics models (restricted sense)
get_number_of_physics_models()1108 size_t get_number_of_physics_models() {return NUMBER_OF_PHYSICS_MODELS;}
1109