1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/physics.c $
21  * $Revision: 1.259 $
22  * $Author: xemu $
23  * $Date: 1994/11/10 16:05:25 $
24  *
25  */
26 
27 #define __PHYSICS_SRC
28 
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 
33 #include "ai.h"
34 #include "combat.h"
35 #include "criterr.h"
36 #include "cyber.h"
37 #include "cybmem.h"
38 #include "cybstrng.h"
39 #include "damage.h"
40 #include "diffq.h" // for time limit
41 #include "drugs.h"
42 #include "effect.h"
43 #include "faketime.h"
44 #include "framer8.h"
45 #include "froslew.h"
46 #include "gamestrn.h"
47 #include "grenades.h"
48 #include "hud.h"
49 #include "ice.h"
50 #include "invent.h"
51 #include "loops.h"
52 #include "lvldata.h"
53 #include "map.h"
54 #include "mapflags.h"
55 #include "mfdext.h"
56 #include "musicai.h"
57 #include "objbit.h"
58 #include "objects.h"
59 #include "objsim.h"
60 #include "objprop.h"
61 #include "objuse.h"
62 #include "otrip.h"
63 #include "physics.h"
64 #include "physunit.h"
65 #include "player.h"
66 #include "render.h"
67 #include "sfxlist.h"
68 #include "tilename.h"
69 #include "tools.h"
70 #include "trigger.h"
71 #include "wares.h"
72 #include "weapons.h"
73 #include "mouselook.h"
74 
75 /*
76 #include <fastmat.h>
77 #include <faceleth.h>
78 #include <btfunc.h>
79 */
80 
81 #define EXPAND_FIX(x) fix_int(x), fix_frac(x)
82 
83 #define sqr(fixval) (fix_mul(fixval, fixval))
84 
85 // INTERNAL PROTOTYPES
86 // --------------------
87 uchar safety_net_wont_you_back_me_up(ObjID oid);
88 void add_edms_delete(int ph);
89 void edms_delete_go(void);
90 void get_phys_state(int ph, State *new_state, ObjID id);
91 void physics_zero_all_controls(void);
92 errtype compare_locs(void);
93 void physics_set_relax(int axis, uchar relax);
94 void relax_axis(int axis);
95 errtype collide_objects(ObjID collision, ObjID victim, int bad);
96 void terrain_object_collide(physics_handle src, ObjID target);
97 errtype run_cspace_collisions(ObjID obj, ObjID exclude, ObjID exclude2);
98 void state_to_objloc(State *s, ObjLoc *l);
99 uchar get_phys_info(int ph, fix *list, int cnt);
100 fix ID2radius(ObjID id);
101 
102 // STANDARD MODELS
103 // ---------------
104 
105 #define STANDARD_HARDNESS  fix_make(15, 0)
106 #define STANDARD_ROUGHNESS fix_make(5, 0)
107 #define STANDARD_PEP       fix_make(5, 0)
108 #define DEFAULT_SIZE       (FIX_UNIT / 2)
109 #define STANDARD_HEIGHT    fix_make(0, 0xbd00)
110 
111 Robot standard_robot = {STANDARD_MASS, DEFAULT_SIZE, STANDARD_HARDNESS, STANDARD_PEP, STANDARD_GRAVITY, FALSE};
112 
113 Pelvis standard_pelvis = {STANDARD_MASS,   DEFAULT_SIZE, STANDARD_HARDNESS, STANDARD_PEP, STANDARD_GRAVITY,
114                           STANDARD_HEIGHT, FALSE};
115 
116 fix standard_corner[4] = {0, 0, 0, 0};
117 Dirac_frame standard_dirac = {
118     STANDARD_MASS,   STANDARD_HARDNESS, STANDARD_ROUGHNESS, STANDARD_GRAVITY,
119 #ifdef HMMMM
120     standard_corner, standard_corner,   standard_corner,    standard_corner,  standard_corner,
121     standard_corner, standard_corner,   standard_corner,    standard_corner,  standard_corner,
122 #endif
123 };
124 
125 extern ObjID physics_handle_id[];
126 extern int physics_handle_max;
127 
128 #define check_up(num)
129 
130 cams *motion_cam = NULL; // what to move, default null is the default camera
131 
132 // CONTROLS
133 // --------
134 
135 byte player_controls[CONTROL_BANKS][DEGREES_OF_FREEDOM] = {
136     {0, 0, 0, 0, 0, 0},
137     {0, 0, 0, 0, 0, 0},
138     {0, 0, 0, 0, 0, 0},
139     {0, 0, 0, 0, 0, 0}
140 };
141 
142 extern errtype (*state_generators[])(ObjID id, int x, int y, ObjLocState *ret);
143 long old_ticks;
144 
145 // Collision callback testing....
146 void cit_collision_callback(physics_handle C, physics_handle V, int32_t bad, int32_t DATA1, int32_t DATA2, fix location[3]);
147 void cit_awol_callback(physics_handle caller);
148 void cit_sleeper_callback(physics_handle caller);
149 void cit_autodestruct_callback(physics_handle caller);
150 
151 // mapping from physics controls to camera controls
152 int ctrl2cam[DEGREES_OF_FREEDOM] = {EYE_X, EYE_Y, EYE_Z, EYE_H, EYE_P, EYE_B};
153 
154 #define SLEW_SCALE_N 16
155 #define SLEW_SCALE_D 100
156 
157 #define MAX_EDMS_DELETE_OBJS 50
158 
159 #ifdef EDMS_SAFETY_NET
160 short safety_fail_oid = -1;
161 uchar safety_fail_count = 0;
162 #define SECRET_NET_OUT_P(x, y) (x > 0x20)
163 #define TOGGLEABLE_SNET
164 #endif
165 
166 uchar safety_net_on = TRUE;
167 short curr_edms_del = 0;
168 short edms_delete_queue[MAX_EDMS_DELETE_OBJS];
169 
safety_net_wont_you_back_me_up(ObjID oid)170 uchar safety_net_wont_you_back_me_up(ObjID oid) {
171     obj_move_to(oid, &objs[oid].loc, TRUE);
172     if (safety_fail_oid == oid) {
173         safety_fail_oid = -1;
174         return FALSE;
175     }
176     if (oid == PLAYER_OBJ)
177         safety_fail_oid = oid;
178     else if (safety_fail_oid == -1)
179         safety_fail_oid = oid;
180     safety_fail_count = 3;
181     return TRUE;
182 }
183 
add_edms_delete(int ph)184 void add_edms_delete(int ph) {
185     int i = 0;
186     uchar bad = FALSE;
187     extern char *get_object_lookname(ObjID id, char use_string[], int sz);
188 
189     for (i = 0; i < curr_edms_del; i++)
190         if (edms_delete_queue[i] == ph)
191             bad = TRUE;
192 
193     if (ph == -1)
194         bad = TRUE;
195 
196     if (!bad) {
197         edms_delete_queue[curr_edms_del] = ph;
198         curr_edms_del++;
199         objs[physics_handle_id[ph]].info.ph = -1;
200         physics_handle_id[ph] = OBJ_NULL;
201     }
202 }
203 
edms_delete_go()204 void edms_delete_go() {
205     int i;
206     for (i = 0; i < curr_edms_del; i++) {
207         if (edms_delete_queue[i] != -1)
208             EDMS_kill_object(edms_delete_queue[i]);
209         edms_delete_queue[i] = -1;
210     }
211     curr_edms_del = 0;
212 }
213 
get_phys_state(int ph,State * new_state,ObjID id)214 void get_phys_state(int ph, State *new_state, ObjID id) {
215     char use_mod = EDMS_ROBOT;
216     if (id != OBJ_NULL) {
217         use_mod = ObjProps[OPNUM(id)].physics_model;
218 #ifdef DIRAC_EDMS
219         // This is hacked on account of the player having 2 physics models.
220         // We may want to take an unused critter slot like the lifter bot to be the dirac-player
221         // like sonic is our pelvis-player.
222         if (id == PLAYER_OBJ)
223             use_mod = (global_fullmap->cyber) ? EDMS_DIRAC : EDMS_PELVIS;
224 #endif
225     }
226     switch (use_mod) {
227     case EDMS_PELVIS:
228         EDMS_get_pelvic_viewpoint(ph, new_state);
229         break;
230     case EDMS_DIRAC:
231         EDMS_get_Dirac_frame_viewpoint(ph, new_state);
232         break;
233     default:
234         EDMS_get_state(ph, new_state);
235         break;
236     }
237 }
238 
physics_zero_all_controls()239 void physics_zero_all_controls() { LG_memset(player_controls, 0, sizeof(player_controls)); }
240 
physics_set_player_controls(int bank,byte xvel,byte yvel,byte zvel,byte xyrot,byte yzrot,byte xzrot)241 errtype physics_set_player_controls(int bank, byte xvel, byte yvel, byte zvel, byte xyrot, byte yzrot, byte xzrot) {
242     if (xvel != CONTROL_NO_CHANGE)
243         player_controls[bank][CONTROL_XVEL] = xvel;
244     if (yvel != CONTROL_NO_CHANGE)
245         player_controls[bank][CONTROL_YVEL] = yvel;
246     if (zvel != CONTROL_NO_CHANGE)
247         player_controls[bank][CONTROL_ZVEL] = zvel;
248     if (xyrot != CONTROL_NO_CHANGE)
249         player_controls[bank][CONTROL_XYROT] = xyrot;
250     if (yzrot != CONTROL_NO_CHANGE)
251         player_controls[bank][CONTROL_YZROT] = yzrot;
252     if (xzrot != CONTROL_NO_CHANGE)
253         player_controls[bank][CONTROL_XZROT] = xzrot;
254     return OK;
255 }
256 
physics_set_one_control(int bank,int num,byte val)257 errtype physics_set_one_control(int bank, int num, byte val) {
258     if (val != CONTROL_NO_CHANGE)
259         player_controls[bank][num] = val;
260     return OK;
261 }
262 
physics_get_one_control(int bank,int num,byte * val)263 errtype physics_get_one_control(int bank, int num, byte *val) {
264     *val = player_controls[bank][num];
265     return OK;
266 }
267 
268 int old_x = -1, old_y = -1, old_lev = -1;
269 char old_bits = 0;
270 extern uchar decon_count;
271 extern uchar in_deconst;
272 extern uchar in_peril;
273 
274 #define TUNNEL_CONTROL_MAX fix_make(0x8, 0)
275 #define MATCHBOX_SPEED fix_make(0x3F, 0)
276 
277 // The concept here is that if you want something to happen when the player switches
278 // square, put it here...
279 
compare_locs(void)280 errtype compare_locs(void) {
281     extern void expose_player(byte damage, ubyte type, ushort hlife);
282     extern void check_hazard_regions(MapElem *);
283     MapElem *newElem, *oldElem;
284     extern int score_playing;
285 
286     if ((old_x != PLAYER_BIN_X) || (old_y != PLAYER_BIN_Y) || (old_lev != player_struct.level)) {
287         newElem = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y);
288         oldElem = MAP_GET_XY(old_x, old_y);
289 
290         // Change music
291         if (music_on) {
292             if (!global_fullmap->cyber) {
293                 in_deconst = me_bits_deconst(newElem);
294                 if (!in_deconst)
295                     decon_count = 0;
296                 in_peril = me_bits_peril(newElem);
297                 if (old_bits != me_bits_music(newElem)) {
298                     fade_into_location(PLAYER_BIN_X, PLAYER_BIN_Y);
299                     old_bits = me_bits_music(newElem);
300                 }
301             }
302         }
303 
304         // Abort physics if bad karma
305         if (physics_running && time_passes && (me_tiletype(newElem) == TILE_SOLID)) {
306             physics_running = FALSE;
307             critical_error(CRITERR_EXEC | 2);
308         }
309 
310         // Look for traps
311         if (time_passes)
312             check_entrance_triggers(old_x, old_y, PLAYER_BIN_X, PLAYER_BIN_Y);
313 
314         check_hazard_regions(newElem);
315 
316         old_x = PLAYER_BIN_X;
317         old_y = PLAYER_BIN_Y;
318         old_lev = player_struct.level;
319     }
320 
321     return (OK);
322 }
323 
324 uchar control_relax[DEGREES_OF_FREEDOM];
325 
physics_set_relax(int axis,uchar relax)326 void physics_set_relax(int axis, uchar relax) { control_relax[axis] = relax; }
327 
relax_axis(int axis)328 void relax_axis(int axis) {
329     switch (axis) {
330     case CONTROL_XZROT:
331         player_set_lean(0, player_struct.leany);
332         break;
333     case CONTROL_YZROT:
334         player_set_eye(0);
335         break;
336     }
337 }
338 
339 static ubyte crouch_controls[NUM_POSTURES] = {0, 6, 10};
340 
341 #define CSPACE_COLLIDE_DIST 0xD0
342 #define CSPACE_FAR_COLLIDE_DIST 0x120
343 
344 #define MAX_PITCH_RATE (FIXANG_PI / 4)
345 #define MAX_LEAN_RATE 400
346 
347 #define ITER_OBJSPECS(pmo, objspec) \
348     for (pmo = (objspec[OBJ_SPEC_NULL]).id; pmo != OBJ_SPEC_NULL; pmo = objspec[pmo].next)
349 
350 // Yow, we have GOT to be able to make this faster/better... -- Xemu
run_cspace_collisions(ObjID obj,ObjID exclude,ObjID exclude2)351 errtype run_cspace_collisions(ObjID obj, ObjID exclude, ObjID exclude2) {
352     ObjRefID oref = me_objref(MAP_GET_XY(OBJ_LOC_BIN_X(objs[obj].loc), OBJ_LOC_BIN_Y(objs[obj].loc)));
353     short use_dist;
354     while (oref != OBJ_REF_NULL) {
355         ObjID oid = objRefs[oref].obj;
356         ObjLoc l1 = objs[oid].loc;
357         ObjLoc l2 = objs[obj].loc;
358         switch (ID2TRIP(oid)) {
359         case CYBERTOG1_TRIPLE:
360         case CYBERTOG2_TRIPLE:
361         case CYBERTOG3_TRIPLE:
362             use_dist = CSPACE_FAR_COLLIDE_DIST;
363             break;
364         default:
365             use_dist = CSPACE_COLLIDE_DIST;
366             break;
367         }
368         if ((oid != obj) && (oid != exclude) && (oid != exclude2) && (objs[oid].obclass != CLASS_PHYSICS)) {
369             // This really ought to take PX into account!
370             if ((abs(l1.x - l2.x) < use_dist) && (abs(l1.y - l2.y) < use_dist) &&
371                 ((abs(l1.z - l2.z) << SLOPE_SHIFT_D) < use_dist)) {
372                 obj_cspace_collide(oid, obj);
373             }
374         }
375         oref = objRefs[oref].next;
376     }
377     return (OK);
378 }
379 
380 #define PLAYER_JIGGLE_THRESHOLD 0x3 // this is in objsys coords
381 
382 #define MAX_SPRINT (fix_make(25, 0))
383 #define MAX_JOG (fix_make(8, 0))
384 #define MAX_BOOSTER (fix_make(50, 0))
385 #define SKATE_ALPHA_CUTOFF (fix_make(8, 0))
386 #define MAX_BOOSTER_ALPHA (fix_make(40, 0))
387 
388 extern void physics_set_relax(int axis, uchar relax);
389 
390 // Takes a physics state and converts it into an Objloc
391 // externed in objsim.c
state_to_objloc(State * s,ObjLoc * l)392 void state_to_objloc(State *s, ObjLoc *l) {
393     l->x = obj_coord_from_fix(s->X);
394     l->y = obj_coord_from_fix(s->Y);
395     l->z = obj_height_from_fix(s->Z);
396     l->h = obj_angle_from_phys(s->alpha);
397 #ifdef WHY_DOESNT_THIS_WORK
398     l->p = obj_angle_from_phys(s->beta);
399     l->b = obj_angle_from_phys(s->gamma);
400 #endif
401 }
402 
403 #define NO_DEFAULT_FORWARD_IN_CSPACE
404 
405 #define CYB_VEL_DELTA 32
406 #define CYB_VEL_DELTA2 16
407 ubyte old_head = 0, old_pitch = 0;
408 short last_deltap = 0, last_deltah = 0;
409 
physics_run(void)410 errtype physics_run(void) {
411     int i;
412     extern int avail_memory(int debug_src);
413 
414     uchar update = FALSE;
415     int deltat = player_struct.deltat;
416     fix plr_y, plr_z, time_diff;
417     fix plr_alpha;
418     State new_state;
419     uchar some_move = FALSE;
420     extern int fire_kickback;
421     extern uchar hack_takeover;
422     static long kickback_time = 0; // i bet this static will someday bite our butts, like save/rest mid kickback?
423 #ifdef EDMS_SAFETY_NET
424     uchar allow_move = TRUE;
425 #endif
426 
427     // Run the mouse look
428     mouse_look_physics();
429 
430     // Here we are computing the values of the player's controls
431     // from the values of the original control banks.  The value
432     // of each control is the average of its non-zero control
433     // values from each control bank, or zero if all are zero.
434     for (i = 0; i < DEGREES_OF_FREEDOM; i++) {
435         int b, n = 0;
436         short control = 0;
437         for (b = 0; b < CONTROL_BANKS; b++)
438             if (player_controls[b][i] != 0) {
439                 control += player_controls[b][i];
440                 n++;
441             }
442         if (n > 0) {
443             some_move = TRUE; // should really do the n!=1 for 2 and 4 as shift too
444             if (n > 1) {
445                 control /= n;
446             }
447         } else if (control_relax[i]) {
448             relax_axis(i);
449         }
450         player_struct.controls[i] = control;
451     }
452     update = some_move; // well, set one of them only once
453     if (physics_running && time_passes) {
454         int i;
455         ObjID oid;
456         int damp;
457         fix plr_side;
458         fix plr_lean;
459         ObjSpecID osid;
460         {
461             fix crouch = fix_make(0, player_struct.lean_filter_state);
462             damp = 3 * sqr(STANDARD_HEIGHT - crouch) / sqr(STANDARD_HEIGHT) + 1;
463         }
464         osid = objPhysicss[0].id;
465         while (osid != OBJ_SPEC_NULL) {
466             // clear the terrain flag
467 
468             if (objPhysicss[osid].p3.x > 0)
469                 objPhysicss[osid].p3.x--;
470 
471             // clear the collision flag
472             if (objPhysicss[osid].p3.y > 0)
473                 objPhysicss[osid].p3.y--;
474 
475             osid = objPhysicss[osid].next;
476         }
477 
478         if (motionware_mode == MOTION_SKATES && damp > 1)
479             damp--;
480         // Here' s where we do leaning.
481         if (player_struct.foot_planted) {
482             plr_y = plr_alpha = plr_side = 0;
483             physics_set_relax(CONTROL_XZROT, FALSE);
484         } else {
485             byte leanx = player_struct.leanx;
486             byte ycntl = player_struct.controls[CONTROL_YVEL];
487             short maxlean = (int)(SPRINT_CONTROL_THRESHOLD - abs(ycntl)) * CONTROL_MAX_VAL / SPRINT_CONTROL_THRESHOLD;
488             plr_side = fix_make(player_struct.controls[CONTROL_XVEL], 0) / 7 / damp;
489             plr_alpha = fix_make(player_struct.controls[CONTROL_XYROT], 0) / -5; // / damp;
490             if (ycntl <= SPRINT_CONTROL_THRESHOLD)
491                 plr_y = ycntl * MAX_JOG / SPRINT_CONTROL_THRESHOLD;
492             else
493                 plr_y = (ycntl - SPRINT_CONTROL_THRESHOLD) * (MAX_SPRINT - MAX_JOG) /
494                             (CONTROL_MAX_VAL - SPRINT_CONTROL_THRESHOLD) +
495                         MAX_JOG;
496             plr_y /= damp;
497             physics_set_relax(CONTROL_XZROT, abs((short)leanx) > maxlean);
498         }
499         if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber) {
500             // Increase some controls to reflect smaller timestep
501             plr_y = plr_y << 2;
502             plr_alpha = plr_alpha << 2;
503             plr_side = plr_side << 2;
504         }
505         switch (motionware_mode) {
506         case MOTION_BOOST: {
507             plr_y = MAX_BOOSTER;
508             // whoop whoop hardcoded version number.
509             if (player_struct.hardwarez[CPTRIP(MOTION_HARD_TRIPLE)] < 3) {
510                 if (plr_alpha < 0)
511                     plr_alpha = -MAX_BOOSTER_ALPHA;
512                 if (plr_alpha > 0)
513                     plr_alpha = MAX_BOOSTER_ALPHA;
514             }
515             break;
516         }
517         case MOTION_SKATES:
518             plr_y *= 2;
519             if (plr_y > 3 * MAX_SPRINT / 2) {
520                 plr_y = 3 * MAX_SPRINT / 2;
521             } else if (plr_y < -MAX_SPRINT) {
522                 plr_y = -MAX_SPRINT;
523             }
524             plr_side >>= 2;
525             if (abs(plr_alpha) > SKATE_ALPHA_CUTOFF) {
526                 if (plr_alpha > 0)
527                     plr_alpha = SKATE_ALPHA_CUTOFF + (plr_alpha - SKATE_ALPHA_CUTOFF) / 2;
528                 else
529                     plr_alpha = -(SKATE_ALPHA_CUTOFF + (-plr_alpha - SKATE_ALPHA_CUTOFF) / 2);
530             }
531 
532             break;
533         }
534 
535         time_diff = fix_make(deltat, 0); // do this here for constant length kickback hack
536 
537         if (time_diff > fix_make(CIT_CYCLE, 0) / MIN_FRAME_RATE)
538             time_diff = fix_make(CIT_CYCLE, 0) / MIN_FRAME_RATE;
539 
540         if (player_struct.controls[CONTROL_XZROT] != 0) {
541             short delta = player_struct.controls[CONTROL_XZROT] * MAX_LEAN_RATE / CONTROL_MAX_VAL;
542             int leanx = player_struct.leanx;
543             leanx = lg_min(CONTROL_MAX_VAL, lg_max(leanx + delta * deltat / CIT_CYCLE, -CONTROL_MAX_VAL));
544             player_set_lean(leanx, player_struct.leany);
545         }
546         if (player_struct.controls[CONTROL_YZROT] != 0) {
547             extern int player_get_eye_fixang(void);
548             extern void player_set_eye_fixang(int);
549             int delta = player_struct.controls[CONTROL_YZROT] * MAX_PITCH_RATE / CONTROL_MAX_VAL;
550             int eye = player_get_eye_fixang();
551             if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber)
552                 delta <<= 2;
553             eye = lg_min(FIXANG_PI / 2, lg_max(eye + delta * deltat / CIT_CYCLE, -FIXANG_PI / 2));
554             player_set_eye_fixang(eye);
555         }
556 
557         plr_lean = fix_make((int)player_struct.leanx, 0) / 3;
558         if (player_struct.controls[CONTROL_ZVEL] > 0) {
559             extern void activate_jumpjets(fix * x, fix * y, fix * z);
560             extern uchar jumpjets_active;
561 
562             player_set_posture(POSTURE_STAND);
563             plr_z = fix_make(player_struct.controls[CONTROL_ZVEL], 0);
564             activate_jumpjets(&plr_side, &plr_y, &plr_z);
565             jumpjets_active = plr_z < 0;
566         } else {
567             extern uchar jumpjets_active;
568             plr_z = fix_make(player_struct.controls[CONTROL_ZVEL], 0); /*  /3/damp; */
569             jumpjets_active = FALSE;
570         }
571 
572         if (global_fullmap->cyber) {
573             MapElem *pme = MAP_GET_XY(PLAYER_BIN_X, PLAYER_BIN_Y);
574             if (me_light_flr(pme)) {
575                 if (plr_y > TUNNEL_CONTROL_MAX)
576                     plr_y = TUNNEL_CONTROL_MAX;
577             }
578             // make controls non-linear.
579             plr_side = fix_mul(plr_side, abs(plr_side)) / 16;
580             plr_alpha = fix_mul(plr_alpha, abs(plr_alpha)) / 16;
581 
582 #ifdef NO_DEFAULT_FORWARD_IN_CSPACE
583             if (QUESTVAR_GET(CYBER_DIFF_QVAR) > 1)
584                 plr_z += fix_make(QUESTVAR_GET(CYBER_DIFF_QVAR) * 5, 0);
585 #endif
586 
587             // Effects of turbo, multiply all movement axes by 4
588             if (cspace_effect_times[CS_TURBO_EFF]) {
589                 plr_y = plr_y << 1;
590                 plr_z = plr_z << 1;
591                 plr_side = plr_side << 1;
592             }
593 #ifdef MATCHBOX_SUPPORT
594             else if (cspace_effect_times[CS_MATCHBOX_EFF]) {
595                 plr_z = MATCHBOX_SPEED;
596             }
597 #endif
598         }
599         if (!global_fullmap->cyber && player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)] > 0) {
600             // Reverse!
601             plr_alpha = -plr_alpha;
602             plr_side = -plr_side;
603             plr_lean = -plr_lean;
604         }
605 
606 #ifdef DIRAC_EDMS
607         if (global_fullmap->cyber) {
608             // printf("EDMS_control_Dirac_frame\n");
609             EDMS_control_Dirac_frame(PLAYER_PHYSICS, plr_z, plr_alpha + fix_make(mlook_vel_x, 5),
610                                      plr_y - fix_make(mlook_vel_y, 5), plr_side);
611         } else
612 #endif
613             // printf("EDMS_control_pelvis %i %i %i %i %i %i %i\n", PLAYER_PHYSICS, plr_y, plr_alpha, plr_side,
614             // plr_lean, plr_z, crouch_controls[player_struct.posture]);
615             EDMS_control_pelvis(PLAYER_PHYSICS, plr_y, plr_alpha, plr_side, plr_lean, plr_z,
616                                 crouch_controls[player_struct.posture]);
617 
618 #ifdef SOLITON_HACK_REFLEX
619         if (player_struct.drug_status[DRUG_REFLEX] > 0 && !global_fullmap->cyber)
620             EDMS_soliton_vector((time_diff / CIT_CYCLE) >> 2);
621         else
622 #endif
623             EDMS_soliton_vector(time_diff / CIT_CYCLE);
624 
625         edms_delete_go();
626 
627         if (--safety_fail_count == 0)
628             safety_fail_oid = -1;
629         for (i = 0; i <= physics_handle_max; i++) // all ph
630         {
631             if ((oid = physics_handle_to_id(i)) != OBJ_NULL) // valid ph, get oid
632             {
633                 ObjLoc newloc;
634 
635                 newloc = objs[oid].loc;
636 
637                 EDMS_get_state(objs[oid].info.ph, &new_state);
638                 state_to_objloc(&new_state, &newloc);
639 
640                 if ((oid == PLAYER_OBJ) && global_fullmap->cyber) {
641                     ubyte new_head, new_pitch; //, new_bank;
642                     short new_deltah, new_deltap;
643                     State cyber_state;
644                     extern uchar new_cyber_orient;
645 
646                     get_phys_state(objs[PLAYER_OBJ].info.ph, &cyber_state, PLAYER_OBJ);
647 
648                     new_head = obj_angle_from_phys(cyber_state.alpha);
649                     new_pitch = obj_angle_from_phys(cyber_state.beta);
650 
651                     new_deltah = abs(new_head - old_head);
652                     new_deltap = abs(new_pitch - old_pitch);
653 
654                     if (((new_deltah < CYB_VEL_DELTA) && (new_deltap < CYB_VEL_DELTA)) || new_cyber_orient ||
655                         ((abs(last_deltah - new_deltah) < CYB_VEL_DELTA2) &&
656                          (abs(last_deltap - new_deltap) < CYB_VEL_DELTA2))) {
657                         old_head = new_head;
658                         old_pitch = new_pitch;
659                         last_deltah = last_deltap = 0;
660 
661                         if (new_cyber_orient)
662                             new_cyber_orient = FALSE;
663                     } else {
664                         last_deltah = new_deltah;
665                         last_deltap = new_deltap;
666                     }
667                 }
668 
669                 // See if we should snap the player out of his
670                 // reverie, either hack camera or automap induced.
671                 if (oid == PLAYER_OBJ) {
672                     if (hack_takeover &&
673                         (some_move || (abs(newloc.x - objs[oid].loc.x) + abs(newloc.y - objs[oid].loc.y) +
674                                            abs(newloc.z - objs[oid].loc.z) >
675                                        PLAYER_JIGGLE_THRESHOLD)))
676                         hack_camera_relinquish();
677                 }
678 
679 #ifdef EDMS_SAFETY_NET
680 #ifdef TOGGLEABLE_SNET
681                 if (safety_net_on)
682 #endif
683                 {
684                     if (me_tiletype(MAP_GET_XY(OBJ_LOC_BIN_X(newloc), OBJ_LOC_BIN_Y(newloc))) == TILE_SOLID) {
685                         safety_net_wont_you_back_me_up(oid);
686                         allow_move = FALSE;
687                     } else if (new_state.Z < fix_from_map_height(me_height_flr(
688                                                  MAP_GET_XY(OBJ_LOC_BIN_X(newloc), OBJ_LOC_BIN_Y(newloc))))) {
689                         if (safety_net_wont_you_back_me_up(oid))
690                             allow_move = FALSE;
691                         else {
692                             if (objs[oid].obclass == CLASS_CRITTER) {
693                                 newloc = objs[oid].loc;
694                                 newloc.z += 3;
695                                 safety_fail_oid = -1;
696                                 allow_move = TRUE;
697                             } else {
698                                 add_edms_delete(objs[oid].info.ph);
699                                 allow_move = FALSE; // just plain sit here and lose, eh?
700                             }
701                         }
702                     }
703                 }
704 
705                 if (allow_move)
706                     obj_move_to(oid, &newloc, FALSE);
707                 else
708                     allow_move = TRUE;
709 #else
710                 obj_move_to(oid, &newloc, FALSE);
711 #endif
712             }
713         }
714     } else if (some_move) { // what is going on here... ah-ha, we objslew.. no wrong
715         for (i = 0; i < DEGREES_OF_FREEDOM; i++)
716             if (player_struct.controls[i] != 0)
717                 fr_camera_slewcam(motion_cam, ctrl2cam[i], player_struct.controls[i] * SLEW_SCALE_N / SLEW_SCALE_D);
718     }
719     old_ticks = *tmd_ticks;
720     if (update || hack_takeover)
721         chg_set_flg(_current_3d_flag);
722     if (physics_running && global_fullmap->cyber) {
723         ObjSpecID specid;
724         // check for the player
725         run_cspace_collisions(PLAYER_OBJ, OBJ_NULL, OBJ_NULL);
726 
727         // check for all slow projectiles
728         ITER_OBJSPECS(specid, objPhysicss) {
729             run_cspace_collisions(objPhysicss[specid].id, PLAYER_OBJ, objPhysicss[specid].owner);
730         }
731     }
732     compare_locs();
733     return (OK);
734 }
735 
736 #ifdef NOT_YET // later, dude
737 
738 #ifdef WACKY_OLD_TERR_FUNC
739 
740 /// ---------------------------------------------------
741 /// HERE COMES THE TERRAIN FUNCTION
742 
743 /// 9/20 ML  I ain't tellin' you a secret...
744 ///          I ain't tellin' you goodBIYEYE...
745 
746 /* ---------------------------------
747    HAQ ALERT! HAQ ALERT!
748    Ok, oftimes we're squaring small numbers.
749    So we're going to use our own special
750    8-24 intermediate fixpoint representation to
751    store the squares
752    -------------------------------- */
753 
754 typedef fix wacky;
755 
756 // Our own special 8-24 fixmul
757 wacky wacky_mul(fix a, fix b);
758 #pragma aux wacky_mul = "imul    edx"    \
759                         "shr     eax,8"  \
760                         "shl     edx,24" \
761                         "or      eax,edx" parm[eax][edx] modify[eax edx];
762 
763 wacky wacky_div(wacky a, wacky b);
764 #pragma aux wacky_div = "mov     edx,eax" \
765                         "sar     edx,8"   \
766                         "shl     eax,24"  \
767                         "idiv    ebx" parm[eax][ebx] modify[eax edx];
768 
769 typedef fix pt3d[3];
770 
771 #define PTARGS(pt) fix_float((pt)[0]), fix_float((pt)[1]), fix_float((pt)[2])
772 
773 #define dotprod(vec1, vec2, result)          \
774     {                                        \
775         fix *v1 = (vec1);                    \
776         fix *v2 = (vec2);                    \
777         (result) = 0;                        \
778         result += fix_mul(*(v1++), *(v2++)); \
779         result += fix_mul(*(v1++), *(v2++)); \
780         result += fix_mul(*(v1++), *(v2++)); \
781     }
782 
783 #define wsqr(fixval) (wacky_mul(fixval, fixval))
784 #define magsquared(vec, res)    \
785     {                           \
786         fix *v1 = (vec);        \
787         (res) = 0;              \
788         (res) += wsqr(*(v1++)); \
789         (res) += wsqr(*(v1++)); \
790         (res) += wsqr(*(v1++)); \
791     }
792 
vec_equal(fix * v1,fix * v2)793 uchar vec_equal(fix *v1, fix *v2) {
794     if (*(v1++) == *(v2++) && *(v1++) == *(v2++) && *(v1++) == *(v2++))
795         return TRUE;
796     else
797         return FALSE;
798 }
799 
800 #define wacky2fix(m2) (m2 >> 8)
801 #define fix2wacky(m) (m << 8)
802 #define wacky_float(w) fix_float(wacky2fix(w))
803 
804 #define NORMAL_X 0
805 #define NORMAL_Y 1
806 #define NORMAL_Z 2
807 
compute_normal_code(pt3d norm)808 int compute_normal_code(pt3d norm) {
809     if (abs(norm[0]) > abs(norm[1])) {
810         if (abs(norm[0]) > abs(norm[2]))
811             return NORMAL_X;
812         else
813             return NORMAL_Z;
814     } else {
815         if (abs(norm[1]) > abs(norm[2]))
816             return NORMAL_Y;
817         else
818             return NORMAL_Z;
819     }
820 }
821 
822 #define PHYS_SPEW(x, y)
823 
824 #define crossprod(v1, v2, out)                                          \
825     {                                                                   \
826         fix *a1 = (v1);                                                 \
827         fix *a2 = (v1);                                                 \
828         fix *b1 = (v2);                                                 \
829         fix *b2 = (v2);                                                 \
830         fix *o = (out);                                                 \
831         fix z = fix_mul(*(v1), (*++b2)) - fix_mul((*++a2), *(v2));      \
832         *(o++) = fix_mul((*++a1), (*++b2)) - fix_mul((*++a2), (*++b1)); \
833         *(o++) = fix_mul((*++a1), *(v2)) - fix_mul(*(v1), (*++b1));     \
834         *(o++) = z;                                                     \
835     }
836 
project_onto_facelet(pt3d in,pt3d out,pt3d flet[NUM_POINTS])837 fix project_onto_facelet(pt3d in, pt3d out, pt3d flet[NUM_POINTS]) {
838     g3s_vector topt;
839     fix dp;
840     int i;
841     g3s_vector norm = *(g3s_vector *)(flet[NORM_IDX]);
842     g3_vec_sub(&topt, (g3s_vector *)in, (g3s_vector *)(flet[0]));
843     dp = g3_vec_dotprod(&topt, &norm);
844     g3_vec_scale(&norm, &norm, dp);
845     // Subtract out normal component
846     g3_vec_sub((g3s_vector *)out, (g3s_vector *)in, &norm);
847     PHYS_SPEW(DSRC_PHYSICS_Terrain,
848               ("Projection onto facelet: %q %q %q --> %q %q %q\nproj =%q\n", PTARGS(in), PTARGS(out), fix_float(dp)));
849     return dp;
850 }
851 
852 int normcode_indices[3][2] = {{1, 2}, {0, 2}, {0, 1}};
853 
854 // Takes a point on the plane of a facelet, and computes
855 // the distance from the projection to the facelet.  Returns
856 // zero if the point projects onto the interior of the facelet.
facelet_distance_sq_4points(pt3d pt,pt3d flet[NUM_POINTS],uchar normcode)857 fix facelet_distance_sq_4points(pt3d pt, pt3d flet[NUM_POINTS], uchar normcode) {
858     fix best_dsq = FIX_MAX; // minimum distance squared
859     int best_vert = -1;
860     fix *best_edge;
861     fix *cprod_edge;
862     fix cprod_msq;
863     pt3d edgen;
864     pt3d cprod;
865     pt3d edgep;
866     pt3d topt; // vertex to point;
867     {
868         pt3d *vert = flet;
869         fix result;
870 
871         g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert);
872         result = g3_vec_mag((g3s_vector *)topt);
873         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq));
874         if (result < best_dsq) {
875             best_dsq = result;
876             best_vert = 0;
877         }
878         vert++;
879         g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert);
880         result = g3_vec_mag((g3s_vector *)topt);
881         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq));
882         if (result < best_dsq) {
883             best_dsq = result;
884             best_vert = 1;
885         }
886         vert++;
887         g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert);
888         result = g3_vec_mag((g3s_vector *)topt);
889         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq));
890         if (result < best_dsq) {
891             best_dsq = result;
892             best_vert = 2;
893         }
894         vert++;
895         g3_vec_sub((g3s_vector *)topt, (g3s_vector *)pt, (g3s_vector *)vert);
896         result = g3_vec_mag((g3s_vector *)topt);
897         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq));
898         if (result < best_dsq) {
899             best_dsq = result;
900             best_vert = 3;
901         }
902     }
903     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Closest vertex %d\n", best_vert));
904     {
905         fix a1, a2, b1, b2, p1, p2, c1, c2;
906         int i1, i2;
907         fix d;
908         int nx = (best_vert + 1) & 0x3;
909         int pr = (best_vert + 3) & 0x3;
910         fix *ep = edgep;
911         fix *en = edgen;
912         fix *tp = topt;
913         fix *p = pt;
914         fix *vert = &flet[best_vert][0];
915         fix *prev = &flet[pr][0];
916         fix *next = &flet[nx][0];
917         if (vec_equal(prev, vert)) {
918             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Caught multiple on prev\n"));
919             pr = (pr + 3) & 0x3;
920             prev = flet[pr];
921         }
922         if (vec_equal(next, vert)) {
923             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Caught multiple on next\n"));
924             nx = (nx + 1) & 0x3;
925             next = flet[nx];
926         }
927         g3_vec_sub((g3s_vector *)ep, (g3s_vector *)prev, (g3s_vector *)vert);
928         g3_vec_sub((g3s_vector *)en, (g3s_vector *)next, (g3s_vector *)vert);
929         g3_vec_sub((g3s_vector *)tp, (g3s_vector *)p, (g3s_vector *)vert);
930         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("vert: (%d) %q %q %q\n", best_vert, PTARGS(vert)));
931         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("prev: (%d) %q %q %q\n", pr, PTARGS(prev)));
932         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("next: (%d) %q %q %q\n", nx, PTARGS(next)));
933         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Normal code is %d\n", normcode));
934 
935         // now throw out one of the coordinates
936         i1 = normcode_indices[normcode][0];
937         i2 = normcode_indices[normcode][1];
938 
939         a1 = edgep[i1];
940         a2 = edgep[i2];
941         b1 = edgen[i1];
942         b2 = edgen[i2];
943         p1 = topt[i1];
944         p2 = topt[i2];
945         // now that we've picked the coordinate system, transform the point into the edge basis.
946         c1 = fix_mul(b2, p1) - fix_mul(b1, p2);
947         c2 = fix_mul(a1, p2) - fix_mul(a2, p1);
948         d = fix_mul(a1, b2) - fix_mul(a2, b1);
949         if (d < 0)
950             c1 = -c1, c2 = -c2, d = -d;
951         PHYS_SPEW(DSRC_PHYSICS_Terrain,
952                   ("a: %q %q, b %q %q, c %q %q, p %q %q d %q\n", fix_float(a1), fix_float(a2), fix_float(b1),
953                    fix_float(b2), fix_float(c1), fix_float(c2), fix_float(p1), fix_float(p2), fix_float(d)));
954         if (c1 < 0 || c2 < 0) {
955             fix mag;
956             // we're outside the quadrilateral
957             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Outside the quad\n"));
958             if (c1 > 0) {
959                 p1 -= fix_mul(c1, a1);
960                 p2 -= fix_mul(c1, a2);
961             } else if (c2 > 0) {
962                 p1 -= fix_mul(c2, b1);
963                 p2 -= fix_mul(c2, b2);
964             } else {
965                 mag = g3_vec_mag((g3s_vector *)topt);
966                 PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag)));
967                 return mag;
968             }
969             topt[i1] = p1;
970             topt[i2] = p2;
971 
972             mag = g3_vec_mag((g3s_vector *)topt);
973             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag)));
974             return mag;
975         } else
976             return 0;
977     }
978 }
979 
980 #define mod3(x) (((x) > 2) ? (x)-3 : (x))
981 
facelet_distance_sq_3points(pt3d pt,pt3d flet[NUM_POINTS],uchar normcode)982 fix facelet_distance_sq_3points(pt3d pt, pt3d flet[NUM_POINTS], uchar normcode) {
983     int i;
984     fix best_dsq = FIX_MAX; // minimum distance squared
985     int best_vert = -1;
986     fix *best_edge;
987     fix *cprod_edge;
988     fix cprod_msq;
989     pt3d edgen;
990     pt3d cprod;
991     pt3d edgep;
992     pt3d topt; // vertex to point;
993     for (i = 0; i < 3; i++) {
994         int next = mod3(i + 1);
995         pt3d edge; // edge vector
996         // build the edge & point vectors
997         {
998             //         fix* e = edge;
999             //         fix* n = flet[next];
1000             fix *t = topt;
1001             fix *p = pt;
1002             fix *v = flet[i];
1003             //         *(e++) = *(n++) - *(v);
1004             *(t++) = *(p++) - *(v++);
1005             //         *(e++) = *(n++) - *(v);
1006             *(t++) = *(p++) - *(v++);
1007             //         *(e++) = *(n++) - *(v);
1008             *(t++) = *(p++) - *(v++);
1009         }
1010         {
1011             fix result;
1012             dotprod(topt, topt, result);
1013             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q dsq %d best_dsq %d\n", PTARGS(topt), result, best_dsq));
1014             if (result < best_dsq) {
1015                 best_dsq = result;
1016                 best_vert = i;
1017             }
1018         }
1019     }
1020     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Closest vertex %d\n", best_vert));
1021     {
1022         fix a1, a2, b1, b2, p1, p2, c1, c2;
1023         fix d;
1024         int nx = (best_vert < 2) ? best_vert + 1 : 0;
1025         int pr = (best_vert > 0) ? best_vert - 1 : 2;
1026         fix *ep = edgep;
1027         fix *en = edgen;
1028         fix *tp = topt;
1029         fix *p = pt;
1030         fix dp;
1031         fix cp, cn;
1032         fix dn;
1033         fix msp;
1034         fix msn;
1035         fix *vert = &flet[best_vert][0];
1036         fix *prev = &flet[pr][0];
1037         fix *next = &flet[nx][0];
1038         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("vert: (%d) %q %q %q\n", best_vert, PTARGS(vert)));
1039         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("prev: (%d) %q %q %q\n", pr, PTARGS(prev)));
1040         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("next: (%d) %q %q %q\n", nx, PTARGS(next)));
1041         *(ep++) = *(prev++) - *(vert);
1042         *(en++) = *(next++) - *(vert);
1043         *(tp++) = *(p++) - *(vert);
1044         vert++;
1045         *(ep++) = *(prev++) - *(vert);
1046         *(en++) = *(next++) - *(vert);
1047         *(tp++) = *(p++) - *(vert);
1048         vert++;
1049         *(ep++) = *(prev++) - *(vert);
1050         *(en++) = *(next++) - *(vert);
1051         *(tp++) = *(p++) - *(vert);
1052         vert++;
1053         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Normal code is %d\n", normcode));
1054 
1055         // now throw out one of the coordinates
1056         switch (normcode) {
1057         case NORMAL_X:
1058             a1 = edgep[1];
1059             a2 = edgep[2];
1060             b1 = edgen[1];
1061             b2 = edgen[2];
1062             p1 = topt[1];
1063             p2 = topt[2];
1064             break;
1065         case NORMAL_Y:
1066             a1 = edgep[0];
1067             a2 = edgep[2];
1068             b1 = edgen[0];
1069             b2 = edgen[2];
1070             p1 = topt[0];
1071             p2 = topt[2];
1072             break;
1073         case NORMAL_Z:
1074             a1 = edgep[0];
1075             a2 = edgep[1];
1076             b1 = edgen[0];
1077             b2 = edgen[1];
1078             p1 = topt[0];
1079             p2 = topt[1];
1080             break;
1081         }
1082         // now that we've picked the coordinate system, transform the point into the edge basis.
1083         c1 = fix_mul(b2, p1) - fix_mul(b1, p2);
1084         c2 = -fix_mul(a2, p1) + fix_mul(a1, p2);
1085         d = fix_mul(a1, b2) - fix_mul(a2, b1);
1086         if (d < 0)
1087             c1 = -c1, c2 = -c2, d = -d;
1088         PHYS_SPEW(DSRC_PHYSICS_Terrain,
1089                   ("a: %q %q, b %q %q, c %q %q, p %q %q d %q\n", fix_float(a1), fix_float(a2), fix_float(b1),
1090                    fix_float(b2), fix_float(c1), fix_float(c2), fix_float(p1), fix_float(p2), fix_float(d)));
1091         if (c1 < 0 || c2 < 0) {
1092             fix mag;
1093             // we're outside the quadrilateral
1094             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Outside the quad\n"));
1095             if (c1 > 0) {
1096                 p1 -= fix_mul(c1, a1);
1097                 p2 -= fix_mul(c1, a2);
1098             } else if (c2 > 0) {
1099                 p1 -= fix_mul(c2, b1);
1100                 p2 -= fix_mul(c2, b2);
1101             } else {
1102                 mag = g3_vec_mag((g3s_vector *)topt);
1103                 PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag)));
1104                 return mag;
1105             }
1106             switch (normcode) {
1107             case NORMAL_X:
1108                 topt[1] = p1;
1109                 topt[2] = p2;
1110                 break;
1111             case NORMAL_Y:
1112                 topt[0] = p1;
1113                 topt[2] = p2;
1114                 break;
1115             case NORMAL_Z:
1116                 topt[0] = p1;
1117                 topt[1] = p2;
1118                 break;
1119             }
1120 
1121             mag = g3_vec_mag((g3s_vector *)topt);
1122             PHYS_SPEW(DSRC_PHYSICS_Terrain, ("topt %q %q %q mag %q\n", PTARGS(topt), fix_float(mag)));
1123             return mag;
1124         } else
1125             return 0;
1126     }
1127 }
1128 
1129 // Given a facelet, translate it r units in the direction of its normal.
1130 // (Mutates the facelet in place, leaves the normal intact)
1131 
grow_facelet(fix r,fix flet[NUM_POINTS][3])1132 void grow_facelet(fix r, fix flet[NUM_POINTS][3]) {
1133     pt3d *coor = flet;
1134     g3s_vector norm;
1135     fix *realnorm = flet[NORM_IDX];
1136 
1137     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Growing by %q\n", fix_float(r)));
1138     // scale the normal vector
1139     g3_vec_scale(&norm, (g3s_vector *)realnorm, r);
1140     // now translate the coords three times
1141     g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm);
1142     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 0:  %q %q %q\n", PTARGS(*coor)));
1143     coor++;
1144     g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm);
1145     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 1:  %q %q %q\n", PTARGS(*coor)));
1146     coor++;
1147     g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm);
1148     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 2:  %q %q %q\n", PTARGS(*coor)));
1149     coor++;
1150     if (**coor != NO_POINT) // is there a fourth point?
1151     {
1152         g3_vec_add((g3s_vector *)*coor, (g3s_vector *)*coor, &norm);
1153         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("Point 3:  %q %q %q\n", PTARGS(*coor)));
1154     }
1155 }
1156 #else
1157 typedef fix pt3d[3];
1158 #endif
1159 
1160 #pragma disable_message(202)
FF_terrain(fix X,fix Y,fix Z,uchar fast,void * TFF)1161 uchar FF_terrain(fix X, fix Y, fix Z, uchar fast, void *TFF) { return (TRUE); }
FF_raycast(fix x,fix y,fix z,fix vec[3],fix range,fix where_hit[3],terrain_ff * tff)1162 uchar FF_raycast(fix x, fix y, fix z, fix vec[3], fix range, fix where_hit[3], terrain_ff *tff) { return (TRUE); }
1163 #pragma disable_message(202)
1164 
1165 // ?????
Terrain(fix fix_x,fix fix_y,fix fix_z,fix rad)1166 void Terrain(fix fix_x, fix fix_y, fix fix_z, fix rad) {}
1167 
1168 #ifdef OLD_TERR_FUNC
1169 extern fix tfunc_rad, tfunc_pt[3];
1170 extern fix tfunc_sum[3];
1171 extern int tfunc_cnt[3];
1172 extern fix tfunc_norms[3][3]; // floor, wall, ceil
1173 
full_3d_facelet_action(fix (* fleto)[3],int which)1174 void full_3d_facelet_action(fix (*fleto)[3], int which) // fix (*norm)[3], int *cnt, fix *sum)
1175 {
1176     fix proj;
1177     pt3d projpt;
1178     fix dist;
1179     pt3d *flet = fleto;
1180 
1181     PHYS_SPEW(DSRC_PHYSICS_Terrain, ("full 3d %d\n", which))
1182     grow_facelet(tfunc_rad, flet);
1183     proj = project_onto_facelet(tfunc_pt, projpt, flet);
1184     // proj must be between 0 and -rad
1185     if (proj > 0 || proj < -tfunc_rad)
1186         return;
1187     if (flet[3][0] == NO_POINT)
1188         dist = facelet_distance_sq_3points(projpt, flet, compute_normal_code(flet[NORM_IDX]));
1189     else
1190         dist = facelet_distance_sq_4points(projpt, flet, compute_normal_code(flet[NORM_IDX]));
1191 
1192     //   PHYS_SPEW(DSRC_PHYSICS_Terrain,("Distance from facelet %d is %q, proj %q
1193     //   \n",i,fix_float(dist),fix_float(proj)));
1194     // if we're closer than our radius,
1195     // scale and add to wall gradient.
1196     if (-proj <= tfunc_rad - dist) {
1197         tfunc_cnt[which]++;
1198         g3_vec_add((g3s_vector *)tfunc_norms[which], (g3s_vector *)tfunc_norms[which], (g3s_vector *)flet[NORM_IDX]);
1199         tfunc_sum[which] -= proj;
1200         PHYS_SPEW(DSRC_PHYSICS_Terrain, ("sum %q, which %d\n", fix_float(-proj), which));
1201     }
1202 }
1203 #endif
1204 
1205 ubyte param_matters[MAP_TYPES] = {
1206     0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1207 };
1208 #endif // NOT_YET
1209 
physics_init()1210 errtype physics_init() {
1211     EDMS_data init_data;
1212 
1213     // Start EDMS
1214     init_data.playfield_size = fix_make(MAP_XSIZE + 4, 0); // note assumption that X and Y sizes are same
1215     init_data.min_physics_handle = 0;
1216     init_data.collision_callback = cit_collision_callback;
1217     init_data.snooz_callback = cit_sleeper_callback;
1218     init_data.autodestruct_callback = cit_autodestruct_callback;
1219     init_data.awol_callback = cit_awol_callback;
1220     init_data.argblock_pointer = (void *)big_buffer;
1221     EDMS_startup(&init_data);
1222 
1223     // Create some defaults
1224     LG_memset(&standard_state, 0, sizeof(State)); // _memset32l(&standard_state,0,12);
1225 
1226     return (OK);
1227 }
1228 
1229 #define NUM_WARMUP_ITERATIONS 10
1230 #define EDMS_WARMUP_TIMESTEP fix_make(0, 0x2000)
1231 
1232 /* KLC - doesn't do anything.
1233 errtype physics_warmup()
1234 {
1235 //   int i;
1236 //   for (i=0; i < NUM_WARMUP_ITERATIONS; i++)
1237 //      EDMS_soliton_vector(EDMS_WARMUP_TIMESTEP);
1238    return(OK);
1239 }
1240 */
1241 
apply_gravity_to_one_object(ObjID oid,fix new_grav)1242 errtype apply_gravity_to_one_object(ObjID oid, fix new_grav) {
1243     errtype err = OK;
1244     if (CHECK_OBJ_PH(oid)) {
1245         switch (ObjProps[OPNUM(oid)].physics_model) {
1246         case EDMS_ROBOT: {
1247             Robot parms;
1248             EDMS_get_robot_parameters(objs[oid].info.ph, &parms);
1249             parms.gravity = new_grav;
1250             EDMS_set_robot_parameters(objs[oid].info.ph, &parms);
1251             break;
1252         }
1253         case EDMS_PELVIS: {
1254             Pelvis parms;
1255             EDMS_get_pelvis_parameters(objs[oid].info.ph, &parms);
1256             parms.gravity = new_grav;
1257             EDMS_set_pelvis_parameters(objs[oid].info.ph, &parms);
1258             break;
1259         }
1260         case EDMS_DIRAC: {
1261             Dirac_frame parms;
1262             EDMS_get_Dirac_frame_parameters(objs[oid].info.ph, &parms);
1263             parms.gravity = new_grav;
1264             EDMS_set_Dirac_frame_parameters(objs[oid].info.ph, &parms);
1265             break;
1266         }
1267         default:
1268             err = ERR_RANGE;
1269             break;
1270         }
1271     }
1272     return err;
1273 }
1274 
1275     // ----------------------------------------------------------------
1276     // Yucky coordinate transformation code.
1277 
1278 #define SIN(x) fix_sin(x)
1279 #define COS(x) fix_cos(x)
1280 
1281     // NOTE, the first index of the matrix is the ROW
1282 
1283     // ----------------------------------------------
1284     // AND NOW, THE THROWING CODE
1285 
1286 #define THROW_YANG_MAX FIXANG_PI / 6
1287 #define THROW_XANG_MAX FIXANG_PI / 4
1288 
ID2radius(ObjID id)1289 fix ID2radius(ObjID id) {
1290     int rad = ObjProps[OPNUM(id)].physics_xr;
1291     if (rad > 0)
1292         return fix_make(rad, 0) / PHYSICS_RADIUS_UNIT;
1293     else
1294         return standard_robot.size;
1295 }
1296 
1297 #define THROW_DISPLACE_RANGE (FIX_UNIT / 2)
1298 #define THROW_ORDER2_SCALE (FIX_UNIT * 2 / 3)
1299 #define THROW_RAYCAST_MASS fix_make(0, 0x2000)
1300 #define THROW_RAYCAST_SPEED fix_make(1, 0)
1301 
player_throw_object(ObjID proj_id,int x,int y,int lastx,int lasty,fix vel)1302 uchar player_throw_object(ObjID proj_id, int x, int y, int lastx, int lasty, fix vel) {
1303     LGPoint pos = MakePoint(x, y);
1304     LGPoint lastpos = MakePoint(lastx, lasty);
1305     ObjLoc loc;
1306     Combat_Pt posvec;
1307     Combat_Pt vector;
1308     Combat_Pt vector2;
1309     Combat_Pt locvec;
1310     fix scale_mag;
1311     State new_state;
1312     fix radius = ID2radius(proj_id);
1313 
1314     find_fire_vector(&pos, &vector);
1315     g3_vec_scale((g3s_vector *)&posvec, (g3s_vector *)&vector, THROW_DISPLACE_RANGE + radius);
1316     posvec.z += radius; // put the bottom of the object at the cursor, not the center.
1317     g3_vec_normalize((g3s_vector *)&posvec);
1318     if (CHECK_OBJ_PH(PLAYER_OBJ)) {
1319         if (global_fullmap->cyber)
1320             EDMS_get_Dirac_frame_viewpoint(objs[PLAYER_OBJ].info.ph, &new_state);
1321         else
1322             EDMS_get_pelvic_viewpoint(objs[PLAYER_OBJ].info.ph, &new_state);
1323     }
1324 
1325     //   loc = objs[PLAYER_OBJ].loc;
1326     locvec.x = new_state.X;
1327     locvec.y = new_state.Y;
1328     locvec.z = new_state.Z;
1329     vector2 = locvec;
1330 
1331     // raycast out from the player to find a good place to put the object
1332     ray_cast_vector(PLAYER_OBJ, &locvec, posvec, THROW_RAYCAST_MASS, radius, THROW_RAYCAST_SPEED,
1333                     THROW_DISPLACE_RANGE + radius);
1334 
1335     g3_vec_sub((g3s_vector *)&vector2, (g3s_vector *)&locvec, (g3s_vector *)&vector2);
1336     scale_mag = g3_vec_mag((g3s_vector *)&vector2);
1337     loc.p = loc.h = loc.b = 0;
1338 
1339     if (scale_mag < THROW_DISPLACE_RANGE + radius) {
1340         g3_vec_scale((g3s_vector *)&locvec, (g3s_vector *)&vector, lg_max(0, scale_mag - radius / 2));
1341         loc.x = obj_coord_from_fix(new_state.X + locvec.x);
1342         loc.y = obj_coord_from_fix(new_state.Y + locvec.y);
1343         loc.z = obj_height_from_fix(new_state.Z + locvec.z + radius);
1344         scale_mag = 0;
1345     } else {
1346         g3_vec_scale((g3s_vector *)&locvec, (g3s_vector *)&vector, lg_max(0, THROW_DISPLACE_RANGE));
1347         loc.x = obj_coord_from_fix(new_state.X + locvec.x);
1348         loc.y = obj_coord_from_fix(new_state.Y + locvec.y);
1349         loc.z = obj_height_from_fix(new_state.Z + locvec.z + radius);
1350         scale_mag = FIX_UNIT;
1351     }
1352     if (scale_mag == 0 || vel == 0)
1353         string_message_info(REF_STR_DropMessage);
1354 
1355     find_fire_vector(&lastpos, &vector2);
1356     g3_vec_scale((g3s_vector *)&vector2, (g3s_vector *)&vector2, THROW_ORDER2_SCALE);
1357     g3_vec_sub((g3s_vector *)&vector, (g3s_vector *)&vector, (g3s_vector *)&vector2);
1358     g3_vec_normalize((g3s_vector *)&vector);
1359     g3_vec_scale((g3s_vector *)&vector, (g3s_vector *)&vector, fix_mul(vel, scale_mag));
1360 
1361     obj_move_to_vel(proj_id, &loc, TRUE, vector.x, vector.y, vector.z);
1362     // let's ignore the thrown object
1363     EDMS_ignore_collisions(objs[PLAYER_OBJ].info.ph, objs[proj_id].info.ph);
1364     if (objs[proj_id].obclass == CLASS_GRENADE) {
1365         extern void reactivate_mine(ObjID id);
1366 
1367         // let's get it to hit player after a little time
1368         if (objs[proj_id].subclass == GRENADE_SUBCLASS_DIRECT)
1369             //      if (GrenadeProps[CPNUM(proj_id)].flags & GREN_MINE_TYPE)
1370             reactivate_mine(proj_id);
1371     }
1372     chg_set_flg(INVENTORY_UPDATE);
1373     return TRUE;
1374 }
1375 
get_phys_info(int ph,fix * list,int cnt)1376 uchar get_phys_info(int ph, fix *list, int cnt) {
1377     ObjID id = physics_handle_id[ph];
1378     State new_state;
1379     if (ph == -1)
1380         return FALSE;
1381     get_phys_state(ph, &new_state, id);
1382     if (ObjProps[OPNUM(id)].physics_model == EDMS_ROBOT) {
1383         if (cnt > 4)
1384             cnt = 4;
1385     }
1386 
1387     switch (cnt - 1) {
1388     default:
1389         if (cnt <= 0)
1390             return FALSE;
1391     case 5:
1392         list[5] = fixrad_to_fixang(new_state.gamma);
1393     case 4:
1394         list[4] = fixrad_to_fixang(-new_state.beta);
1395     case 3:
1396         list[3] = fixang_from_phys_angle(new_state.alpha);
1397     case 2:
1398         list[2] = new_state.Z;
1399     case 1:
1400         list[1] = new_state.Y;
1401     case 0:
1402         list[0] = new_state.X;
1403     }
1404     return TRUE;
1405 }
1406 
1407 //-------------------------------------------
1408 // POSTURE STUFF
1409 
player_set_posture(ubyte new_posture)1410 errtype player_set_posture(ubyte new_posture) {
1411     player_struct.posture = new_posture;
1412     return OK;
1413 }
1414 
1415 // --------------------------------------------
1416 // LEANING
1417 
player_set_lean(byte x,byte y)1418 errtype player_set_lean(byte x, byte y) {
1419     player_struct.leanx = x;
1420     player_struct.leany = y;
1421     return OK;
1422 }
1423 
1424 #define FIRST_ENRG_PROJ_TYPE 7
1425 
1426     // -----------------------------------------------
1427     // here is our wacky collision callback...
1428 
1429 #define ENERGY_MINE_DRAIN 10
1430 
1431 short PULSER_DAMAGE[10] = {10, 15, 25, 40, 60, 85, 125, 170, 260, 500};
1432 
1433 #define NUM_DRILL_LEVELS 5
1434 #define MAX_DRILL_DAMAGE 725
1435 #define DRILL_DAMAGE(lev) (MAX_DRILL_DAMAGE / (NUM_DRILL_LEVELS - (lev)))
1436 
1437 ubyte ice_offense_values[] = {1, 2, 3, 4};
1438 ubyte ice_penetration[] = {5, 5, 10, 15};
1439 ubyte ice_damage_modifiers[] = {10, 20, 40, 80};
1440 
collide_objects(ObjID collision,ObjID victim,int bad)1441 errtype collide_objects(ObjID collision, ObjID victim, int bad) {
1442     uchar destroy_me = TRUE;
1443 
1444     if (objs[collision].obclass == CLASS_GRENADE) {
1445         grenade_contact(collision, bad);
1446     } else if ((objs[collision].obclass == CLASS_PHYSICS) && (objs[collision].subclass == PHYSICS_SUBCLASS_SLOW)) {
1447         ObjID owner = objPhysicss[objs[collision].specID].owner;
1448         int a = objPhysicss[objs[collision].specID].bullet_triple;
1449         int cp_num;
1450         ObjLoc loc = objs[collision].loc;
1451         extern ObjID damage_sound_id;
1452         extern char damage_sound_fx;
1453         uchar special_proj = FALSE;
1454 
1455         // set that we've already collided this time
1456         if (objPhysicss[objs[collision].specID].p3.y)
1457             return OK;
1458         else
1459             objPhysicss[objs[collision].specID].p3.y = 3;
1460 
1461         if (owner != PLAYER_OBJ) {
1462             cp_num = CPNUM(owner);
1463             switch (objs[owner].obclass) {
1464             case CLASS_CRITTER:
1465                 attack_object(victim, CritterProps[cp_num].attacks[a].damage_type,
1466                               CritterProps[cp_num].attacks[a].damage_modifier,
1467                               CritterProps[cp_num].attacks[a].offense_value,
1468                               CritterProps[cp_num].attacks[a].penetration, 0, 100, NULL, 0, 0, NULL);
1469                 break;
1470             default:
1471                 if (global_fullmap->cyber && ICE_ICE_BABY(owner)) {
1472                     attack_object(victim, CYBER_PROJECTILE_TYPE, ice_damage_modifiers[a], ice_offense_values[a],
1473                                   ice_penetration[a], 0, 100, NULL, 0, 0, NULL);
1474                 }
1475                 break;
1476             }
1477         } else {
1478             extern void slow_proj_hit(ObjID id, ObjID victim);
1479 
1480             // Was the player firing a special projectile?
1481             switch (ID2TRIP(collision)) {
1482             case DRILLSLOW_TRIPLE:
1483                 if (ICE_ICE_BABY(victim)) {
1484                     char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple;
1485                     short dmg;
1486 
1487                     // Damage the ice -- higher level ICEs are tough, but we always
1488                     // do at least a little tiny bit of damage
1489                     dmg = lg_max(1, DRILL_DAMAGE(soft_lvl - 1) >> (ICE_LEVEL(victim)) * 2);
1490 
1491                     if (dmg < objs[victim].info.current_hp) {
1492                         // If it is still alive, agitate it
1493                         objs[victim].info.current_hp -= dmg;
1494                         SET_ICE_AGIT(victim, lg_min(ICE_AGIT(victim) + soft_lvl, MAX_AGIT));
1495                     } else {
1496                         objs[victim].info.current_hp = 0;
1497                         DEICE(victim);
1498                     }
1499                 }
1500                 special_proj = TRUE;
1501                 break;
1502             case CYBERSLOW_TRIPLE:
1503                 if (!(ICE_ICE_BABY(victim))) {
1504                     char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple;
1505                     simple_damage_object(victim, PULSER_DAMAGE[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0);
1506                     special_proj = TRUE;
1507                 }
1508                 break;
1509 #ifdef MANY_CYBERSPACE_WEAPONS
1510             case DISCSLOW_TRIPLE: {
1511                 char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple;
1512                 simple_damage_object(victim, disc_damage[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0);
1513                 special_proj = TRUE;
1514             } break;
1515             case SPEWSLOW_TRIPLE: {
1516                 char soft_lvl = objPhysicss[objs[collision].specID].bullet_triple;
1517                 simple_damage_object(victim, cyberspew_damage[soft_lvl - 1], CYBER_PROJECTILE_TYPE, 0);
1518                 special_proj = TRUE;
1519             }
1520 #else
1521             case SPEWSLOW_TRIPLE:
1522             case DISCSLOW_TRIPLE:
1523                 special_proj = TRUE;
1524                 break;
1525 #endif
1526             break;
1527             }
1528 
1529             if (!special_proj)
1530                 slow_proj_hit(collision, victim);
1531         }
1532 
1533         // Why are we destroying "victim" if it's a slow projectile?!?!
1534         // cause the other one can't damage this one - because it'll be destroyed.
1535         // only one call allowed
1536 
1537         // this big if here - says that if both objects are physics objects
1538         // do the thing the flags say! (what to destroy)
1539         if ((objs[victim].obclass == CLASS_PHYSICS) && (objs[victim].subclass == PHYSICS_SUBCLASS_SLOW)) {
1540             if (!(PhysicsProps[CPNUM(victim)].flags & PROJ_PRESERVE_PROJ_HIT))
1541                 ADD_DESTROYED_OBJECT(victim);
1542 
1543             if (!(PhysicsProps[CPNUM(collision)].flags & PROJ_PRESERVE_PROJ_HIT))
1544                 ADD_DESTROYED_OBJECT(collision);
1545         } else if (!(PhysicsProps[CPNUM(collision)].flags & PROJ_PRESERVE_HIT))
1546             ADD_DESTROYED_OBJECT(collision);
1547 
1548         if (damage_sound_fx != -1) {
1549             play_digi_fx_obj(damage_sound_fx, 1, damage_sound_id);
1550         }
1551         damage_sound_fx = -1;
1552     } else if ((ID2TRIP(collision) == ENERGY_MINE_TRIPLE) && (victim == PLAYER_OBJ)) {
1553         player_struct.energy = lg_max(0, player_struct.energy - ENERGY_MINE_DRAIN);
1554         if (!digi_fx_playing(SFX_ENERGY_DRAIN, NULL))
1555             play_digi_fx(SFX_ENERGY_DRAIN, 1);
1556         chg_set_flg(VITALS_UPDATE);
1557     }
1558     return (OK);
1559 }
1560 
terrain_object_collide(physics_handle src,ObjID target)1561 void terrain_object_collide(physics_handle src, ObjID target) {
1562     ObjID hit_obj = physics_handle_to_id(src);
1563     if (is_obj_destroyed(hit_obj) || is_obj_destroyed(target))
1564         return;
1565     collide_objects(target, hit_obj, 0);
1566 }
1567 
cit_collision_callback(physics_handle C,physics_handle V,int32_t bad,int32_t DATA1,int32_t DATA2,fix location[3])1568 void cit_collision_callback(physics_handle C, physics_handle V, int32_t bad, int32_t DATA1, int32_t DATA2, fix location[3]) {
1569     ObjID collision;
1570     ObjID victim;
1571 
1572     collision = physics_handle_to_id(C);
1573     if (is_obj_destroyed(collision))
1574         return;
1575     victim = physics_handle_to_id(V);
1576     if (is_obj_destroyed(victim))
1577         return;
1578     collide_objects(collision, victim, bad);
1579 }
1580 
cit_awol_callback(physics_handle caller)1581 void cit_awol_callback(physics_handle caller) {
1582     State s;
1583 
1584     if (caller != -1)
1585         get_phys_state(caller, &s, OBJ_NULL);
1586     else {
1587         return;
1588     }
1589     if ((fix_int(s.X) > global_fullmap->x_size) || (s.X < 0) || (fix_int(s.Y) > global_fullmap->y_size) || (s.Y < 0)) {
1590         if (caller != -1) {
1591             ADD_DESTROYED_OBJECT(physics_handle_to_id(caller));
1592         }
1593     }
1594 }
1595 
cit_sleeper_callback(physics_handle caller)1596 void cit_sleeper_callback(physics_handle caller) {
1597     ObjID id;
1598     id = physics_handle_to_id(caller);
1599 
1600     // Is this a kind of thing that wants to maintain it's physics-ness?
1601     // Live grenades should do this...
1602     if (ObjProps[OPNUM(id)].flags & EDMS_PRESERVE) {
1603         // Do put-to-sleep code here.
1604     } else {
1605         if (ID2TRIP(id) == L_MINE_TRIPLE) {
1606             if (objGrenades[objs[id].specID].flags & GREN_ACTIVE_FLAG) {
1607                 objGrenades[objs[id].specID].flags |= GREN_MINE_STILL;
1608                 EDMS_obey_collisions(caller);
1609                 return; // let's not put it to sleep
1610             }
1611         }
1612         add_edms_delete(caller);
1613     }
1614 }
1615 
cit_autodestruct_callback(physics_handle h)1616 void cit_autodestruct_callback(physics_handle h) {}
1617 
1618 uchar robot_antisocial = FALSE;
1619 
1620 // Build the model given a state and object ID, and assign appropriate
1621 // data into the object and do appropriate bookkeeping
assemble_physics_object(ObjID id,State * pnew_state)1622 errtype assemble_physics_object(ObjID id, State *pnew_state) {
1623     switch (ObjProps[OPNUM(id)].physics_model) {
1624     case EDMS_ROBOT: {
1625         Obj *pObj = &objs[id];
1626 
1627         Robot new_robot;
1628         instantiate_robot(ID2TRIP(id), &new_robot);
1629         pObj->info.ph = EDMS_make_robot(&new_robot, pnew_state);
1630         if (CHECK_OBJ_PH(id)) {
1631             if (robot_antisocial)
1632                 EDMS_make_robot_antisocial(pObj->info.ph);
1633 
1634 #ifdef SECRET_NON_COLLISION_BITS
1635             if ((global_fullmap->cyber) && (pObj->obclass == CLASS_PHYSICS) &&
1636                 (pObj->subclass == PHYSICS_SUBCLASS_SLOW))
1637                 set_secret_non_collision_bit(pObj->info.ph);
1638 #endif
1639             physics_handle_id[pObj->info.ph] = id;
1640         }
1641         break;
1642     }
1643 
1644     case EDMS_PELVIS: {
1645         Pelvis new_pelvis;
1646         instantiate_pelvis(ID2TRIP(id), &new_pelvis);
1647         objs[id].info.ph = EDMS_make_pelvis(&new_pelvis, pnew_state);
1648         physics_handle_id[objs[id].info.ph] = id;
1649         break;
1650     }
1651     case EDMS_DIRAC: {
1652         Dirac_frame new_dirac;
1653         instantiate_dirac(ID2TRIP(id), &new_dirac);
1654         objs[id].info.ph = EDMS_make_Dirac_frame(&new_dirac, pnew_state);
1655         physics_handle_id[objs[id].info.ph] = id;
1656         break;
1657     }
1658     }
1659     if ((CHECK_OBJ_PH(id)) && (objs[id].info.ph > physics_handle_max))
1660         physics_handle_max = objs[id].info.ph;
1661     return (OK);
1662 }
1663 
1664 // ======================================================================
1665 //                        MODEL STUFF
1666 
1667 // -------------------------------------------
1668 // instantiate_robot() fills in the fields of a robot
1669 // structure from object properties specified by triple,
1670 // and level properties.
1671 
instantiate_robot(int triple,Robot * new_robot)1672 void instantiate_robot(int triple, Robot *new_robot) {
1673     int newmass, newsize;
1674     short hard, pep;
1675 
1676     *new_robot = standard_robot;
1677     switch (level_gamedata.gravity) {
1678     case LEVEL_GRAV_LOW:
1679         new_robot->gravity = fix_div(standard_robot.gravity, fix_make(3, 0));
1680         break;
1681     case LEVEL_GRAV_ZERO:
1682         new_robot->gravity = 0;
1683         break;
1684     default:
1685         if (global_fullmap->cyber ||
1686             (((TRIP2CL(triple) == CLASS_CRITTER) && (CritterProps[CPTRIP(triple)].flags & AI_FLAG_FLYING)) ||
1687              (triple == ENERGY_MINE_TRIPLE)))
1688             new_robot->gravity = 0;
1689         break;
1690     }
1691     newmass = ObjProps[OPTRIP(triple)].mass;
1692     if (newmass > 0)
1693         new_robot->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN);
1694     newsize = ObjProps[OPTRIP(triple)].physics_xr;
1695     if (newsize > 0)
1696         new_robot->size = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT;
1697     hard = ObjProps[OPTRIP(triple)].hardness;
1698     if (hard > 0)
1699         new_robot->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT;
1700     pep = ObjProps[OPTRIP(triple)].pep;
1701     if (pep > 0)
1702         new_robot->pep = fix_make(pep, 0) / PHYS_PEP_UNIT;
1703     //   new_robot->cyber_space = global_fullmap->cyber ? 2 : 0;
1704     if (new_robot->gravity)
1705         new_robot->cyber_space = 0;
1706     else
1707         new_robot->cyber_space = 1;
1708 }
1709 
1710 // -------------------------------------------
1711 // instantiate_pelvis() fills in the fields of a pelvis
1712 // structure from object properties specified by triple,
1713 // and level properties.
1714 
instantiate_pelvis(int triple,Pelvis * new_pelvis)1715 void instantiate_pelvis(int triple, Pelvis *new_pelvis) {
1716     int newmass, newsize;
1717     short hard, pep;
1718 
1719     *new_pelvis = standard_pelvis;
1720     switch (level_gamedata.gravity) {
1721     case LEVEL_GRAV_LOW:
1722         new_pelvis->gravity = fix_div(standard_pelvis.gravity, fix_make(3, 0));
1723         break;
1724     case LEVEL_GRAV_ZERO:
1725         new_pelvis->gravity = 0;
1726         break;
1727     default:
1728         if (global_fullmap->cyber)
1729             new_pelvis->gravity = 0;
1730         break;
1731     }
1732 
1733     newmass = ObjProps[OPTRIP(triple)].mass;
1734     if (newmass > 0)
1735         new_pelvis->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN);
1736     newsize = ObjProps[OPTRIP(triple)].physics_xr;
1737     if (newsize > 0)
1738         new_pelvis->size = fix_make(newsize, 0) / PHYSICS_RADIUS_UNIT;
1739     hard = ObjProps[OPTRIP(triple)].hardness;
1740     if (hard > 0)
1741         new_pelvis->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT;
1742     pep = ObjProps[OPTRIP(triple)].pep;
1743     if (pep > 0)
1744         new_pelvis->pep = fix_make(pep, 0) / PHYS_PEP_UNIT;
1745     if (global_fullmap->cyber)
1746         new_pelvis->cyber_space = PELVIS_MODE_CYBER;
1747     else
1748         new_pelvis->cyber_space = (motionware_mode == MOTION_SKATES) ? PELVIS_MODE_SKATES : PELVIS_MODE_NORMAL;
1749 }
1750 
1751 // -------------------------------------------
1752 // instantiate_dirac() fills in the fields of a Dirac_frame
1753 // structure from object properties specified by triple,
1754 // and level properties.
1755 
instantiate_dirac(int triple,Dirac_frame * new_dirac)1756 void instantiate_dirac(int triple, Dirac_frame *new_dirac) {
1757     int newmass;
1758     short hard, rough;
1759 
1760     *new_dirac = standard_dirac;
1761     switch (level_gamedata.gravity) {
1762     case LEVEL_GRAV_LOW:
1763         new_dirac->gravity = fix_div(standard_dirac.gravity, fix_make(3, 0));
1764         break;
1765     case LEVEL_GRAV_ZERO:
1766         WARN("%s: Zero gravity level!", __FUNCTION__);
1767         new_dirac->gravity = 0;
1768         break;
1769     default:
1770         //         if (global_fullmap->cyber)
1771         //            new_dirac->gravity = 0;
1772         break;
1773     }
1774     newmass = ObjProps[OPTRIP(triple)].mass;
1775     if (newmass > 0)
1776         new_dirac->mass = fix_make(newmass, 0) / (PHYS_MASS_UNIT * PHYS_MASS_C_NUM / PHYS_MASS_C_DEN);
1777     hard = ObjProps[OPTRIP(triple)].hardness;
1778     if (hard > 0)
1779         new_dirac->hardness = fix_make(hard, 0) / PHYS_HARDNESS_UNIT;
1780     rough = ObjProps[OPTRIP(triple)].pep;
1781     if (rough > 0)
1782         new_dirac->roughness = fix_make(rough, 0) / PHYS_ROUGHNESS_UNIT;
1783 
1784     // Whut the heck do we do for the corners of a Dirac_frame?  Beyond my feeble ken, I fear.
1785     // DIRAC_FIX
1786 }
1787