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