Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Code for flying through the mines
23
 *
24
 */
25
 
26
 
27
#include <stdio.h>
28
#include <stdlib.h>
29
 
30
#include "joy.h"
31
#include "dxxerror.h"
32
 
33
#include "inferno.h"
34
#include "segment.h"
35
#include "object.h"
36
#include "physics.h"
37
#include "robot.h"
38
#include "key.h"
39
#include "game.h"
40
#include "collide.h"
41
#include "fvi.h"
42
#include "newdemo.h"
43
#include "gameseg.h"
44
#include "timer.h"
45
#include "ai.h"
46
#include "wall.h"
47
#include "laser.h"
48
#if defined(DXX_BUILD_DESCENT_II)
49
#include "bm.h"
50
#include "player.h"
51
#define MAX_OBJECT_VEL  i2f(100)
52
#endif
53
 
54
#include "dxxsconf.h"
55
#include "compiler-range_for.h"
56
 
57
//Global variables for physics system
58
 
59
#define ROLL_RATE       0x2000
60
#define DAMP_ANG        0x400 //min angle to bank
61
#define TURNROLL_SCALE  (0x4ec4/2)
62
 
63
//check point against each side of segment. return bitmask, where bit
64
//set means behind that side
65
 
66
//make sure matrix is orthogonal
67
void check_and_fix_matrix(vms_matrix &m)
68
{
69
        m = vm_vector_2_matrix(m.fvec,&m.uvec,nullptr);
70
}
71
 
72
 
73
static void do_physics_align_object(object_base &obj)
74
{
75
        vms_vector desired_upvec;
76
        fixang delta_ang,roll_ang;
77
        fix largest_d = INT32_MIN;
78
        const shared_side *best_side = nullptr;
79
        // bank player according to segment orientation
80
 
81
        //find side of segment that player is most alligned with
82
 
83
        range_for (auto &i, vcsegptr(obj.segnum)->shared_segment::sides)
84
        {
85
                const auto d = vm_vec_dot(i.normals[0], obj.orient.uvec);
86
 
87
                if (largest_d < d)
88
                {
89
                        largest_d = d;
90
                        best_side = &i;
91
                }
92
        }
93
 
94
        // new player leveling code: use normal of side closest to our up vec
95
        if (!best_side)
96
                return;
97
        if (!get_side_is_quad(*best_side))
98
        {
99
                desired_upvec = vm_vec_avg(best_side->normals[0], best_side->normals[1]);
100
                vm_vec_normalize(desired_upvec);
101
        }
102
        else
103
                desired_upvec = best_side->normals[0];
104
 
105
        if (labs(vm_vec_dot(desired_upvec, obj.orient.fvec)) < f1_0 / 2)
106
        {
107
                vms_angvec tangles;
108
 
109
                const auto temp_matrix = vm_vector_2_matrix(obj.orient.fvec, &desired_upvec, nullptr);
110
 
111
                delta_ang = vm_vec_delta_ang(obj.orient.uvec, temp_matrix.uvec, obj.orient.fvec);
112
 
113
                delta_ang += obj.mtype.phys_info.turnroll;
114
 
115
                if (abs(delta_ang) > DAMP_ANG) {
116
                        roll_ang = fixmul(FrameTime,ROLL_RATE);
117
 
118
                        if (abs(delta_ang) < roll_ang) roll_ang = delta_ang;
119
                        else if (delta_ang<0) roll_ang = -roll_ang;
120
 
121
                        tangles.p = tangles.h = 0;  tangles.b = roll_ang;
122
                        const auto &&rotmat = vm_angles_2_matrix(tangles);
123
                        obj.orient = vm_matrix_x_matrix(obj.orient, rotmat);
124
                }
125
        }
126
 
127
}
128
 
129
static void set_object_turnroll(object_base &obj)
130
{
131
        fixang desired_bank;
132
 
133
        desired_bank = -fixmul(obj.mtype.phys_info.rotvel.y, TURNROLL_SCALE);
134
 
135
        if (obj.mtype.phys_info.turnroll != desired_bank)
136
        {
137
                fixang delta_ang,max_roll;
138
 
139
                max_roll = fixmul(ROLL_RATE,FrameTime);
140
 
141
                delta_ang = desired_bank - obj.mtype.phys_info.turnroll;
142
 
143
                if (labs(delta_ang) < max_roll)
144
                        max_roll = delta_ang;
145
                else
146
                        if (delta_ang < 0)
147
                                max_roll = -max_roll;
148
 
149
                obj.mtype.phys_info.turnroll += max_roll;
150
        }
151
 
152
}
153
 
154
 
155
#define MAX_IGNORE_OBJS 100
156
 
157
#ifndef NDEBUG
158
int     Total_retries=0, Total_sims=0;
159
int     Dont_move_ai_objects=0;
160
#endif
161
 
162
#define FT (f1_0/64)
163
 
164
//      -----------------------------------------------------------------------------------------------------------
165
// add rotational velocity & acceleration
166
namespace dsx {
167
static void do_physics_sim_rot(object_base &obj)
168
{
169
        vms_angvec      tangles;
170
        //fix           rotdrag_scale;
171
        physics_info *pi;
172
 
173
        Assert(FrameTime > 0);  //Get MATT if hit this!
174
 
175
        pi = &obj.mtype.phys_info;
176
 
177
        if (!(pi->rotvel.x || pi->rotvel.y || pi->rotvel.z || pi->rotthrust.x || pi->rotthrust.y || pi->rotthrust.z))
178
                return;
179
 
180
        if (obj.mtype.phys_info.drag)
181
        {
182
                int count;
183
                fix drag,r,k;
184
 
185
                count = FrameTime / FT;
186
                r = FrameTime % FT;
187
                k = fixdiv(r,FT);
188
 
189
                drag = (obj.mtype.phys_info.drag * 5) / 2;
190
 
191
                if (obj.mtype.phys_info.flags & PF_USES_THRUST)
192
                {
193
                        const auto accel = vm_vec_copy_scale(obj.mtype.phys_info.rotthrust, fixdiv(f1_0, obj.mtype.phys_info.mass));
194
                        while (count--) {
195
                                vm_vec_add2(obj.mtype.phys_info.rotvel, accel);
196
                                vm_vec_scale(obj.mtype.phys_info.rotvel, f1_0 - drag);
197
                        }
198
 
199
                        //do linear scale on remaining bit of time
200
 
201
                        vm_vec_scale_add2(obj.mtype.phys_info.rotvel, accel, k);
202
                        vm_vec_scale(obj.mtype.phys_info.rotvel, f1_0 - fixmul(k, drag));
203
                }
204
                else
205
#if defined(DXX_BUILD_DESCENT_II)
206
                        if (! (obj.mtype.phys_info.flags & PF_FREE_SPINNING))
207
#endif
208
                {
209
                        fix total_drag=f1_0;
210
 
211
                        while (count--)
212
                                total_drag = fixmul(total_drag,f1_0-drag);
213
 
214
                        //do linear scale on remaining bit of time
215
 
216
                        total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
217
 
218
                        vm_vec_scale(obj.mtype.phys_info.rotvel, total_drag);
219
                }
220
 
221
        }
222
 
223
        //now rotate object
224
 
225
        //unrotate object for bank caused by turn
226
        if (obj.mtype.phys_info.turnroll)
227
        {
228
                tangles.p = tangles.h = 0;
229
                tangles.b = -obj.mtype.phys_info.turnroll;
230
                const auto &&rotmat = vm_angles_2_matrix(tangles);
231
                obj.orient = vm_matrix_x_matrix(obj.orient, rotmat);
232
        }
233
 
234
        const auto frametime = FrameTime;
235
        tangles.p = fixmul(obj.mtype.phys_info.rotvel.x, frametime);
236
        tangles.h = fixmul(obj.mtype.phys_info.rotvel.y, frametime);
237
        tangles.b = fixmul(obj.mtype.phys_info.rotvel.z, frametime);
238
 
239
        obj.orient = vm_matrix_x_matrix(obj.orient, vm_angles_2_matrix(tangles));
240
 
241
        if (obj.mtype.phys_info.flags & PF_TURNROLL)
242
                set_object_turnroll(obj);
243
 
244
        //re-rotate object for bank caused by turn
245
        if (obj.mtype.phys_info.turnroll)
246
        {
247
                tangles.p = tangles.h = 0;
248
                tangles.b = obj.mtype.phys_info.turnroll;
249
                obj.orient = vm_matrix_x_matrix(obj.orient, vm_angles_2_matrix(tangles));
250
        }
251
 
252
        check_and_fix_matrix(obj.orient);
253
}
254
}
255
 
256
// On joining edges fvi tends to get inaccurate as hell. Approach is to check if the object interects with the wall and if so, move away from it.
257
static void fix_illegal_wall_intersection(const vmobjptridx_t obj)
258
{
259
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
260
        auto &Objects = LevelUniqueObjectState.Objects;
261
        auto &Vertices = LevelSharedVertexState.get_vertices();
262
        auto &vmobjptr = Objects.vmptr;
263
        if (!(obj->type == OBJ_PLAYER || obj->type == OBJ_ROBOT))
264
                return;
265
 
266
        auto &vcvertptr = Vertices.vcptr;
267
        const auto &&hresult = sphere_intersects_wall(vcvertptr, obj->pos, vcsegptridx(obj->segnum), obj->size);
268
        if (hresult.seg)
269
        {
270
                vm_vec_scale_add2(obj->pos, hresult.seg->sides[hresult.side].normals[0], FrameTime*10);
271
                update_object_seg(vmobjptr, LevelSharedSegmentState, LevelUniqueSegmentState, obj);
272
        }
273
}
274
 
275
namespace {
276
 
277
class ignore_objects_array_t
278
{
279
        using array_t = std::array<vcobjidx_t, MAX_IGNORE_OBJS>;
280
        array_t::iterator e;
281
        union {
282
                array_t a;
283
        };
284
public:
285
        /* The iterator should be initialized in a
286
         * member-initialization-list.  However, clang complains that the
287
         * union is uninitialized during the member-initialization-list, but
288
         * accepts the still-uninitialized union member once the constructor
289
         * body starts.  Assign the iterator in the body to silence this
290
         * useless clang warning.
291
         *
292
         * Known bad:
293
         *      clang-5
294
         *      clang-7
295
         */
296
        ignore_objects_array_t()
297
        {
298
                e = a.begin();
299
        }
300
        bool push_back(const vcobjidx_t o)
301
        {
302
                if (unlikely(e == a.end()))
303
                        return false;
304
                std::uninitialized_fill_n(e, 1, o);
305
                ++e;
306
                return true;
307
        }
308
        operator std::pair<const vcobjidx_t *, const vcobjidx_t *>() const
309
        {
310
                return {a.begin(), e};
311
        }
312
};
313
 
314
}
315
 
316
//      -----------------------------------------------------------------------------------------------------------
317
//Simulate a physics object for this frame
318
namespace dsx {
319
window_event_result do_physics_sim(const vmobjptridx_t obj, const vms_vector &obj_previous_position, phys_visited_seglist *const phys_segs)
320
{
321
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
322
        auto &Objects = LevelUniqueObjectState.Objects;
323
        auto &Vertices = LevelSharedVertexState.get_vertices();
324
        auto &vcobjptr = Objects.vcptr;
325
        auto &vmobjptr = Objects.vmptr;
326
        ignore_objects_array_t ignore_obj_list;
327
        int try_again;
328
        int fate=0;
329
        vms_vector ipos;                //position after this frame
330
        segnum_t WallHitSeg;
331
        int WallHitSide;
332
        fvi_info hit_info;
333
        fvi_query fq;
334
        vms_vector save_pos;
335
        fix drag;
336
        fix sim_time;
337
        vms_vector start_pos;
338
        int obj_stopped=0;
339
        fix moved_time;                 //how long objected moved before hit something
340
        physics_info *pi;
341
        auto orig_segnum = obj->segnum;
342
        int bounced=0;
343
        bool Player_ScrapeFrame=false;
344
        auto result = window_event_result::handled;
345
#if defined(DXX_BUILD_DESCENT_II)
346
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
347
#endif
348
 
349
        Assert(obj->movement_type == MT_PHYSICS);
350
 
351
#ifndef NDEBUG
352
        if (Dont_move_ai_objects)
353
                if (obj->control_type == CT_AI)
354
                        return window_event_result::ignored;
355
#endif
356
 
357
        pi = &obj->mtype.phys_info;
358
 
359
        do_physics_sim_rot(obj);
360
 
361
        if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z))
362
                return window_event_result::ignored;
363
 
364
        sim_time = FrameTime;
365
 
366
        start_pos = obj->pos;
367
 
368
                //if uses thrust, cannot have zero drag
369
        Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0);
370
 
371
        //do thrust & drag
372
        if ((drag = obj->mtype.phys_info.drag) != 0) {
373
 
374
                int count;
375
                fix r,k,have_accel;
376
 
377
                count = FrameTime / FT;
378
                r = FrameTime % FT;
379
                k = fixdiv(r,FT);
380
 
381
                if (obj->mtype.phys_info.flags & PF_USES_THRUST) {
382
 
383
                        const auto accel = vm_vec_copy_scale(obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass));
384
                        have_accel = (accel.x || accel.y || accel.z);
385
 
386
                        while (count--) {
387
                                if (have_accel)
388
                                        vm_vec_add2(obj->mtype.phys_info.velocity,accel);
389
 
390
                                vm_vec_scale(obj->mtype.phys_info.velocity,f1_0-drag);
391
                        }
392
 
393
                        //do linear scale on remaining bit of time
394
 
395
                        vm_vec_scale_add2(obj->mtype.phys_info.velocity,accel,k);
396
                        if (drag)
397
                                vm_vec_scale(obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag));
398
                }
399
                else if (drag)
400
                {
401
                        fix total_drag=f1_0;
402
 
403
                        while (count--)
404
                                total_drag = fixmul(total_drag,f1_0-drag);
405
 
406
                        //do linear scale on remaining bit of time
407
 
408
                        total_drag = fixmul(total_drag,f1_0-fixmul(k,drag));
409
 
410
                        vm_vec_scale(obj->mtype.phys_info.velocity,total_drag);
411
                }
412
        }
413
 
414
        int count = 0;
415
        auto &vcvertptr = Vertices.vcptr;
416
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
417
        auto &vcwallptr = Walls.vcptr;
418
        do {
419
                try_again = 0;
420
 
421
                //Move the object
422
                const auto frame_vec = vm_vec_copy_scale(obj->mtype.phys_info.velocity, sim_time);
423
 
424
                if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) )
425
                        break;
426
 
427
                count++;
428
 
429
                //      If retry count is getting large, then we are trying to do something stupid.
430
                if (count > 8) break; // in original code this was 3 for all non-player objects. still leave us some limit in case fvi goes apeshit.
431
 
432
                const auto new_pos = vm_vec_add(obj->pos,frame_vec);
433
                fq.p0                                           = &obj->pos;
434
                fq.startseg                             = obj->segnum;
435
                fq.p1                                           = &new_pos;
436
                fq.rad                                  = obj->size;
437
                fq.thisobjnum                   = obj;
438
                fq.ignore_obj_list      = ignore_obj_list;
439
                fq.flags                                        = FQ_CHECK_OBJS;
440
 
441
                if (obj->type == OBJ_WEAPON)
442
                        fq.flags |= FQ_TRANSPOINT;
443
 
444
                if (phys_segs)
445
                        fq.flags |= FQ_GET_SEGLIST;
446
 
447
                fate = find_vector_intersection(fq, hit_info);
448
                //      Matt: Mike's hack.
449
                if (fate == HIT_OBJECT) {
450
                        auto &objp = *vcobjptr(hit_info.hit_object);
451
 
452
                        if ((objp.type == OBJ_WEAPON && is_proximity_bomb_or_player_smart_mine(get_weapon_id(objp))) ||
453
                                objp.type == OBJ_POWERUP) // do not increase count for powerups since they *should* not change our movement
454
                                count--;
455
                }
456
 
457
#ifndef NDEBUG
458
                if (fate == HIT_BAD_P0) {
459
                        Int3();
460
                }
461
#endif
462
 
463
                if (phys_segs && !hit_info.seglist.empty())
464
                {
465
                        auto n_phys_segs = phys_segs->nsegs;
466
                        if (n_phys_segs && phys_segs->seglist[n_phys_segs - 1] == hit_info.seglist[0])
467
                                n_phys_segs--;
468
                        range_for (const auto &hs, hit_info.seglist)
469
                        {
470
                                if (!(n_phys_segs < phys_segs->seglist.size() - 1))
471
                                        break;
472
                                phys_segs->seglist[n_phys_segs++] = hs;
473
                        }
474
                        phys_segs->nsegs = n_phys_segs;
475
                }
476
 
477
                ipos = hit_info.hit_pnt;
478
                auto iseg = hit_info.hit_seg;
479
                WallHitSide = hit_info.hit_side;
480
                WallHitSeg = hit_info.hit_side_seg;
481
 
482
                if (iseg==segment_none) {               //some sort of horrible error
483
                        if (obj->type == OBJ_WEAPON)
484
                                obj->flags |= OF_SHOULD_BE_DEAD;
485
                        break;
486
                }
487
 
488
                Assert(!((fate==HIT_WALL) && ((WallHitSeg == segment_none) || (WallHitSeg > Highest_segment_index))));
489
 
490
                save_pos = obj->pos;                    //save the object's position
491
                auto save_seg = obj->segnum;
492
 
493
                // update object's position and segment number
494
                obj->pos = ipos;
495
 
496
                const auto &&obj_segp = Segments.vmptridx(iseg);
497
                if ( iseg != obj->segnum )
498
                        obj_relink(vmobjptr, Segments.vmptr, obj, obj_segp);
499
 
500
                //if start point not in segment, move object to center of segment
501
                if (get_seg_masks(vcvertptr, obj->pos, Segments.vcptr(obj->segnum), 0).centermask != 0)
502
                {
503
                        auto n = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj);
504
                        if (n == segment_none)
505
                        {
506
                                //Int3();
507
                                if (obj->type == OBJ_PLAYER && (n = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj_previous_position, obj_segp)) != segment_none)
508
                                {
509
                                        obj->pos = obj_previous_position;
510
                                        obj_relink(vmobjptr, Segments.vmptr, obj, n);
511
                                }
512
                                else {
513
                                        compute_segment_center(vcvertptr, obj->pos, obj_segp);
514
                                        obj->pos.x += obj;
515
                                }
516
                                if (obj->type == OBJ_WEAPON)
517
                                        obj->flags |= OF_SHOULD_BE_DEAD;
518
                        }
519
                        return window_event_result::ignored;
520
                }
521
 
522
                //calulate new sim time
523
                {
524
                        //vms_vector moved_vec;
525
                        vms_vector moved_vec_n;
526
                        fix attempted_dist,actual_dist;
527
 
528
                        actual_dist = vm_vec_normalized_dir(moved_vec_n,obj->pos,save_pos);
529
 
530
                        if (fate==HIT_WALL && vm_vec_dot(moved_vec_n,frame_vec) < 0) {          //moved backwards
531
 
532
                                //don't change position or sim_time
533
 
534
                                obj->pos = save_pos;
535
 
536
                                //iseg = obj->segnum;           //don't change segment
537
 
538
                                obj_relink(vmobjptr, Segments.vmptr, obj, Segments.vmptridx(save_seg));
539
 
540
                                moved_time = 0;
541
                        }
542
                        else {
543
                                fix old_sim_time;
544
 
545
                                attempted_dist = vm_vec_mag(frame_vec);
546
 
547
                                old_sim_time = sim_time;
548
 
549
                                sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist);
550
 
551
                                moved_time = old_sim_time - sim_time;
552
 
553
                                if (sim_time < 0 || sim_time>old_sim_time) {
554
                                        sim_time = old_sim_time;
555
                                        //WHY DOES THIS HAPPEN??
556
 
557
                                        moved_time = 0;
558
                                }
559
                        }
560
                }
561
 
562
 
563
                switch( fate )          {
564
 
565
                        case HIT_WALL:          {
566
                                fix hit_speed=0,wall_part=0;
567
 
568
                                // Find hit speed       
569
 
570
                                const auto moved_v = vm_vec_sub(obj->pos,save_pos);
571
 
572
                                wall_part = vm_vec_dot(moved_v,hit_info.hit_wallnorm);
573
 
574
                                if ((wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0) || obj->type == OBJ_WEAPON || obj->type == OBJ_DEBRIS)
575
                                        result = collide_object_with_wall(
576
#if defined(DXX_BUILD_DESCENT_II)
577
                                                LevelSharedSegmentState.DestructibleLights,
578
#endif
579
                                                obj, hit_speed, Segments.vmptridx(WallHitSeg), WallHitSide, hit_info.hit_pnt);
580
                                /*
581
                                 * Due to the nature of this loop, it's possible that a local player may receive scrape damage multiple times in one frame.
582
                                 * Check if we received damage and do not apply more damage (nor produce damage sounds/flashes/bumps, etc) for the rest of the loop.
583
                                 * It's possible that other walls later in the loop would still be valid for scraping but due to the generalized outcome, this should be negligible (practical wall sliding is handled below).
584
                                 * NOTE: Remote players will return false and never receive damage. But since we handle only one object (remote or local) per loop, this is no problem.
585
                                 */
586
                                if (obj->type == OBJ_PLAYER && Player_ScrapeFrame == false)
587
                                        Player_ScrapeFrame = scrape_player_on_wall(obj, Segments.vmptridx(WallHitSeg), WallHitSide, hit_info.hit_pnt);
588
 
589
                                Assert( WallHitSeg != segment_none );
590
                                Assert( WallHitSide > -1 );
591
 
592
                                if ( !(obj->flags&OF_SHOULD_BE_DEAD) )  {
593
                                        int forcefield_bounce;          //bounce off a forcefield
594
 
595
#if defined(DXX_BUILD_DESCENT_II)
596
                                        if (!cheats.bouncyfire)
597
#endif
598
                                        Assert(!(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE));     //can't be bounce and stick
599
 
600
#if defined(DXX_BUILD_DESCENT_I)
601
                                        /*
602
                                         * Force fields are not supported in Descent 1.  Use
603
                                         * this as a placeholder to make the code match the
604
                                         * force field handling in Descent 2.
605
                                         */
606
                                        forcefield_bounce = 0;
607
#elif defined(DXX_BUILD_DESCENT_II)
608
                                        forcefield_bounce = (TmapInfo[Segments[WallHitSeg].unique_segment::sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD);
609
                                        int check_vel=0;
610
#endif
611
 
612
                                        if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) {            //stop moving
613
 
614
                                                LevelUniqueStuckObjectState.add_stuck_object(vcwallptr, obj, Segments.vmptr(WallHitSeg), WallHitSide);
615
 
616
                                                vm_vec_zero(obj->mtype.phys_info.velocity);
617
                                                obj_stopped = 1;
618
                                                try_again = 0;
619
                                        }
620
                                        else {                                  // Slide object along wall
621
 
622
                                                wall_part = vm_vec_dot(hit_info.hit_wallnorm,obj->mtype.phys_info.velocity);
623
 
624
                                                // if wall_part, make sure the value is sane enough to get usable velocity computed
625
                                                if (wall_part < 0 && wall_part > -f1_0) wall_part = -f1_0;
626
                                                if (wall_part > 0 && wall_part < f1_0) wall_part = f1_0;
627
 
628
                                                if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) {            //bounce off wall
629
                                                        wall_part *= 2; //Subtract out wall part twice to achieve bounce
630
 
631
#if defined(DXX_BUILD_DESCENT_II)
632
                                                        if (forcefield_bounce) {
633
                                                                check_vel = 1;                          //check for max velocity
634
                                                                if (obj->type == OBJ_PLAYER)
635
                                                                        wall_part *= 2;         //player bounce twice as much
636
                                                        }
637
 
638
                                                        if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) {
639
                                                                Assert(obj->mtype.phys_info.flags & PF_BOUNCE);
640
                                                                if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE)
641
                                                                        obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE);
642
                                                                else
643
                                                                        obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE;
644
                                                        }
645
 
646
                                                        bounced = 1;            //this object bounced
647
#endif
648
                                                }
649
 
650
                                                vm_vec_scale_add2(obj->mtype.phys_info.velocity,hit_info.hit_wallnorm,-wall_part);
651
 
652
#if defined(DXX_BUILD_DESCENT_II)
653
                                                if (check_vel) {
654
                                                        fix vel = vm_vec_mag_quick(obj->mtype.phys_info.velocity);
655
 
656
                                                        if (vel > MAX_OBJECT_VEL)
657
                                                                vm_vec_scale(obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel));
658
                                                }
659
 
660
                                                if (bounced && obj->type == OBJ_WEAPON)
661
                                                        vm_vector_2_matrix(obj->orient,obj->mtype.phys_info.velocity,&obj->orient.uvec,nullptr);
662
#endif
663
 
664
                                                try_again = 1;
665
                                        }
666
                                }
667
 
668
                                break;
669
                        }
670
 
671
                        case HIT_OBJECT:                {
672
                                vms_vector old_vel;
673
 
674
                                // Mark the hit object so that on a retry the fvi code
675
                                // ignores this object.
676
 
677
                                Assert(hit_info.hit_object != object_none);
678
                                //      Calculcate the hit point between the two objects.
679
                                {
680
                                        fix                     size0, size1;
681
                                        const auto &&hit = obj.absolute_sibling(hit_info.hit_object);
682
                                        const auto &ppos0 = hit->pos;
683
                                        const auto &ppos1 = obj->pos;
684
                                        size0 = hit->size;
685
                                        size1 = obj->size;
686
                                        Assert(size0+size1 != 0);       // Error, both sizes are 0, so how did they collide, anyway?!?
687
                                        //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1));
688
                                        //vm_vec_add2(&pos_hit, ppos0);
689
                                        auto pos_hit = vm_vec_sub(ppos1, ppos0);
690
                                        vm_vec_scale_add(pos_hit,ppos0,pos_hit,fixdiv(size0, size0 + size1));
691
 
692
                                        old_vel = obj->mtype.phys_info.velocity;
693
 
694
                                        collide_two_objects( obj, hit, pos_hit);
695
 
696
                                }
697
 
698
                                // Let object continue its movement
699
                                if ( !(obj->flags&OF_SHOULD_BE_DEAD)  ) {
700
                                        //obj->pos = save_pos;
701
 
702
                                        if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) {
703
                                                //if (Objects[hit_info.hit_object].type == OBJ_POWERUP)
704
                                                if (ignore_obj_list.push_back(hit_info.hit_object))
705
                                                try_again = 1;
706
                                        }
707
                                }
708
 
709
                                break;
710
                        }      
711
                        case HIT_NONE:         
712
                                break;
713
 
714
#ifndef NDEBUG
715
                        case HIT_BAD_P0:
716
                                Int3();         // Unexpected collision type: start point not in specified segment.
717
                                break;
718
                        default:
719
                                // Unknown collision type returned from find_vector_intersection!!
720
                                Int3();
721
                                break;
722
#endif
723
                }
724
        } while ( try_again );
725
 
726
        //      Pass retry count info to AI.
727
        if (obj->control_type == CT_AI) {
728
                if (count > 0) {
729
                        obj->ctype.ai_info.ail.retry_count = count-1;
730
#ifndef NDEBUG
731
                        Total_retries += count-1;
732
                        Total_sims++;
733
#endif
734
                }
735
        }
736
 
737
        // After collision with objects and walls, set velocity from actual movement
738
        if (!obj_stopped && !bounced
739
                && ((obj->type == OBJ_PLAYER) || (obj->type == OBJ_ROBOT) || (obj->type == OBJ_DEBRIS))
740
                && ((fate == HIT_WALL) || (fate == HIT_OBJECT) || (fate == HIT_BAD_P0))
741
                )
742
        {      
743
                const auto moved_vec = vm_vec_sub(obj->pos,start_pos);
744
                vm_vec_copy_scale(obj->mtype.phys_info.velocity,moved_vec,fixdiv(f1_0,FrameTime));
745
        }
746
 
747
        fix_illegal_wall_intersection(obj);
748
 
749
        //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0);
750
 
751
        //if (obj->control_type == CT_FLYING)
752
        if (obj->mtype.phys_info.flags & PF_LEVELLING)
753
                do_physics_align_object( obj );
754
 
755
        //hack to keep player from going through closed doors
756
        if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (!cheats.ghostphysics) ) {
757
 
758
                const cscusegment orig_segp = vcsegptr(orig_segnum);
759
                const auto &&sidenum = find_connect_side(vcsegptridx(obj->segnum), orig_segp);
760
                if (sidenum != side_none)
761
                {
762
 
763
                        if (! (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, orig_segp, sidenum) & WID_FLY_FLAG))
764
                        {
765
                                fix dist;
766
 
767
                                //bump object back
768
 
769
                                auto &s = orig_segp.s.sides[sidenum];
770
 
771
                                const auto v = create_abs_vertex_lists(orig_segp, s, sidenum);
772
                                const auto &vertex_list = v.second;
773
 
774
                                //let's pretend this wall is not triangulated
775
                                const auto b = begin(vertex_list);
776
                                const auto vertnum = *std::min_element(b, std::next(b, 4));
777
 
778
                                dist = vm_dist_to_plane(start_pos, s.normals[0], vcvertptr(vertnum));
779
                                vm_vec_scale_add(obj->pos, start_pos, s.normals[0], obj->size-dist);
780
                                update_object_seg(vmobjptr, LevelSharedSegmentState, LevelUniqueSegmentState, obj);
781
 
782
                        }
783
                }
784
        }
785
 
786
//--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL   #ifndef NDEBUG
787
        //if end point not in segment, move object to last pos, or segment center
788
        if (get_seg_masks(vcvertptr, obj->pos, vcsegptr(obj->segnum), 0).centermask != 0)
789
        {
790
                if (find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj) == segment_none)
791
                {
792
                        segnum_t n;
793
 
794
                        //Int3();
795
                        const auto &&obj_segp = Segments.vmptridx(obj->segnum);
796
                        if (obj->type == OBJ_PLAYER && (n = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj_previous_position, obj_segp)) != segment_none)
797
                        {
798
                                obj->pos = obj_previous_position;
799
                                obj_relink(vmobjptr, Segments.vmptr, obj, Segments.vmptridx(n));
800
                        }
801
                        else {
802
                                compute_segment_center(vcvertptr, obj->pos, obj_segp);
803
                                obj->pos.x += obj;
804
                        }
805
                        if (obj->type == OBJ_WEAPON)
806
                                obj->flags |= OF_SHOULD_BE_DEAD;
807
                }
808
        }
809
 
810
        return result;
811
//--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL   #endif
812
}
813
}
814
 
815
namespace dcx {
816
 
817
//Applies an instantaneous force on an object, resulting in an instantaneous
818
//change in velocity.
819
void phys_apply_force(object_base &obj, const vms_vector &force_vec)
820
{
821
        if (obj.movement_type != MT_PHYSICS)
822
                return;
823
 
824
        //      Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass,
825
        //      in collision with crazy reactor robot thing on d2levf-s.
826
        if (obj.mtype.phys_info.mass == 0)
827
                return;
828
 
829
        //Add in acceleration due to force
830
        vm_vec_scale_add2(obj.mtype.phys_info.velocity, force_vec, fixdiv(f1_0, obj.mtype.phys_info.mass));
831
}
832
 
833
//      ----------------------------------------------------------------
834
//      Do *dest = *delta unless:
835
//                              *delta is pretty small
836
//              and     they are of different signs.
837
static void physics_set_rotvel_and_saturate(fix &dest, fix delta)
838
{
839
        if ((delta ^ dest) < 0) {
840
                if (abs(delta) < F1_0/8) {
841
                        dest = delta/4;
842
                } else
843
                        dest = delta;
844
        } else {
845
                dest = delta;
846
        }
847
}
848
 
849
static inline vms_angvec vm_extract_angles_vector(const vms_vector &v)
850
{
851
        vms_angvec a;
852
        return vm_extract_angles_vector(a, v), a;
853
}
854
 
855
//      ------------------------------------------------------------------------------------------------------
856
//      Note: This is the old ai_turn_towards_vector code.
857
//      phys_apply_rot used to call ai_turn_towards_vector until I fixed it, which broke phys_apply_rot.
858
void physics_turn_towards_vector(const vms_vector &goal_vector, object_base &obj, fix rate)
859
{
860
        fix                     delta_p, delta_h;
861
 
862
        // Make this object turn towards the goal_vector.  Changes orientation, doesn't change direction of movement.
863
        // If no one moves, will be facing goal_vector in 1 second.
864
 
865
        //      Detect null vector.
866
        if ((goal_vector.x == 0) && (goal_vector.y == 0) && (goal_vector.z == 0))
867
                return;
868
 
869
        //      Make morph objects turn more slowly.
870
        if (obj.control_type == CT_MORPH)
871
                rate *= 2;
872
 
873
        const auto dest_angles = vm_extract_angles_vector(goal_vector);
874
        const auto cur_angles = vm_extract_angles_vector(obj.orient.fvec);
875
 
876
        delta_p = (dest_angles.p - cur_angles.p);
877
        delta_h = (dest_angles.h - cur_angles.h);
878
 
879
        if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
880
        if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
881
        if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
882
        if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
883
 
884
        delta_p = fixdiv(delta_p, rate);
885
        delta_h = fixdiv(delta_h, rate);
886
 
887
        if (abs(delta_p) < F1_0/16) delta_p *= 4;
888
        if (abs(delta_h) < F1_0/16) delta_h *= 4;
889
 
890
        auto &rotvel_ptr = obj.mtype.phys_info.rotvel;
891
        physics_set_rotvel_and_saturate(rotvel_ptr.x, delta_p);
892
        physics_set_rotvel_and_saturate(rotvel_ptr.y, delta_h);
893
        rotvel_ptr.z = 0;
894
}
895
 
896
}
897
 
898
namespace dsx {
899
 
900
//      -----------------------------------------------------------------------------
901
//      Applies an instantaneous whack on an object, resulting in an instantaneous
902
//      change in orientation.
903
void phys_apply_rot(object &obj, const vms_vector &force_vec)
904
{
905
        fix     rate;
906
 
907
        if (obj.movement_type != MT_PHYSICS)
908
                return;
909
 
910
#if defined(DXX_BUILD_DESCENT_II)
911
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
912
#endif
913
        auto vecmag = vm_vec_mag(force_vec);
914
        if (vecmag < F1_0/32)
915
                rate = 4*F1_0;
916
        else if (vecmag < obj.mtype.phys_info.mass >> 11)
917
                rate = 4*F1_0;
918
        else {
919
                rate = fixdiv(obj.mtype.phys_info.mass, vecmag / 8);
920
                if (obj.type == OBJ_ROBOT) {
921
                        if (rate < F1_0/4)
922
                                rate = F1_0/4;
923
#if defined(DXX_BUILD_DESCENT_I)
924
                        obj.ctype.ai_info.SKIP_AI_COUNT = 2;
925
#elif defined(DXX_BUILD_DESCENT_II)
926
                        //      Changed by mk, 10/24/95, claw guys should not slow down when attacking!
927
                        if (!Robot_info[get_robot_id(obj)].thief && !Robot_info[get_robot_id(obj)].attack_type) {
928
                                if (obj.ctype.ai_info.SKIP_AI_COUNT * FrameTime < 3*F1_0/4) {
929
                                        fix     tval = fixdiv(F1_0, 8*FrameTime);
930
                                        int     addval;
931
 
932
                                        addval = f2i(tval);
933
 
934
                                        if ( (d_rand() * 2) < (tval & 0xffff))
935
                                                addval++;
936
                                        obj.ctype.ai_info.SKIP_AI_COUNT += addval;
937
                                }
938
                        }
939
#endif
940
                } else {
941
                        if (rate < F1_0/2)
942
                                rate = F1_0/2;
943
                }
944
        }
945
 
946
        //      Turn amount inversely proportional to mass.  Third parameter is seconds to do 360 turn.
947
        physics_turn_towards_vector(force_vec, obj, rate);
948
}
949
 
950
}
951
 
952
namespace dcx {
953
 
954
//this routine will set the thrust for an object to a value that will
955
//(hopefully) maintain the object's current velocity
956
void set_thrust_from_velocity(object_base &obj)
957
{
958
        Assert(obj.movement_type == MT_PHYSICS);
959
        auto &phys_info = obj.mtype.phys_info;
960
        vm_vec_copy_scale(phys_info.thrust, phys_info.velocity,
961
                fixmuldiv(phys_info.mass, phys_info.drag, F1_0 - phys_info.drag)
962
        );
963
}
964
 
965
}