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-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Editor object functions.
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <stdarg.h>
29
#include <math.h>
30
#include <string.h>
31
 
32
#include "inferno.h"
33
#include "segment.h"
34
#include "editor.h"
35
#include "editor/esegment.h"
36
#include "editor/eobject.h"
37
 
38
#include "objpage.h"
39
#include "maths.h"
40
#include "dxxerror.h"
41
#include "kdefs.h"
42
#include        "object.h"
43
#include "robot.h"
44
#include "game.h"
45
#include "ai.h"
46
#include "bm.h"
47
#include "3d.h"         //      For g3_point_to_vec
48
#include        "fvi.h"
49
 
50
#include "powerup.h"
51
#include "fuelcen.h"
52
#include "hostage.h"
53
#include "medrobot.h"
54
#include "player.h"
55
#include "gameseg.h"
56
#include "cntrlcen.h"
57
 
58
#include "compiler-range_for.h"
59
#include "segiter.h"
60
 
61
#define OBJ_SCALE               (F1_0/2)
62
#define OBJ_DEL_SIZE    (F1_0/2)
63
 
64
//returns the number of the first object in a segment, skipping the player
65
static objnum_t get_first_object(fvcobjptr &vcobjptr, const unique_segment &seg)
66
{
67
        const auto id = seg.objects;
68
        if (id == object_none)
69
                return object_none;
70
        auto &o = *vcobjptr(id);
71
        if (&o == ConsoleObject)
72
                return o.next;
73
        return id;
74
}
75
 
76
//returns the number of the next object in a segment, skipping the player
77
static objnum_t get_next_object(const vmsegptr_t seg,objnum_t id)
78
{
79
        auto &Objects = LevelUniqueObjectState.Objects;
80
        auto &vcobjptr = Objects.vcptr;
81
        auto &vmobjptr = Objects.vmptr;
82
        if (id == object_none)
83
                return get_first_object(vcobjptr, seg);
84
        for (auto o = vmobjptr(id);;)
85
        {
86
                id = o->next;
87
                if (id == object_none)
88
                        return get_first_object(vcobjptr, seg);
89
                o = vmobjptr(id);
90
                if (o != ConsoleObject)
91
                        return id;
92
        }
93
}
94
 
95
 
96
//@@//  ------------------------------------------------------------------------------------------------------
97
//@@// this should be called whenever the current segment may have changed
98
//@@//  If Cur_object_seg != Cursegp, then update various variables.
99
//@@// this used to be called update_due_to_new_segment()
100
//@@void ObjectUpdateCurrent(void)
101
//@@{
102
//@@    if (Cur_object_seg != Cursegp) {
103
//@@            Cur_object_seg = Cursegp;
104
//@@            Cur_object_index = get_first_object(Cur_object_seg);
105
//@@            Update_flags |= UF_WORLD_CHANGED;
106
//@@    }
107
//@@
108
//@@}
109
 
110
namespace dsx {
111
 
112
//      ------------------------------------------------------------------------------------
113
int place_object(const vmsegptridx_t segp, const vms_vector &object_pos, short object_type, short object_id)
114
{
115
        vms_matrix seg_matrix;
116
 
117
        med_extract_matrix_from_segment(segp, seg_matrix);
118
 
119
        imobjptridx_t objnum = object_none;
120
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
121
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
122
        switch (object_type)
123
        {
124
 
125
                case OBJ_HOSTAGE:
126
                {
127
                        objnum = obj_create(OBJ_HOSTAGE, -1,
128
                                        segp,object_pos,&seg_matrix,HOSTAGE_SIZE,
129
                                        CT_NONE,MT_NONE,RT_HOSTAGE);
130
 
131
                        if ( objnum == object_none)
132
                                return 0;
133
 
134
                        const vmobjptridx_t obj = objnum;
135
 
136
                        // Fill in obj->id and other hostage info
137
                        obj->id = 0;
138
 
139
                        obj->control_type = CT_POWERUP;
140
 
141
                        obj->rtype.vclip_info.vclip_num = Hostage_vclip_num[object_id];
142
                        obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time;
143
                        obj->rtype.vclip_info.framenum = 0;
144
                        break;
145
                }
146
                case OBJ_ROBOT:
147
                {
148
                        segnum_t hide_segment;
149
                        if (Markedsegp)
150
                                hide_segment = Markedsegp;
151
                        else
152
                                hide_segment = segment_none;
153
 
154
                        objnum = robot_create(object_id, segp, object_pos,
155
                                                                &seg_matrix, Polygon_models[Robot_info[object_id].model_num].rad,
156
                                                                Robot_info[object_id].attack_type ?
157
                                                                //      robots which lunge forward to attack cannot have behavior type still.
158
                                                                ai_behavior::AIB_NORMAL :
159
                                                                ai_behavior::AIB_STILL,
160
                                                                hide_segment);
161
 
162
                        if ( objnum == object_none)
163
                                return 0;
164
 
165
                        const vmobjptridx_t obj = objnum;
166
 
167
                        //Set polygon-object-specific data 
168
 
169
                        obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
170
                        obj->rtype.pobj_info.subobj_flags = 0;
171
 
172
                        //set Physics info
173
 
174
                        obj->mtype.phys_info.mass = Robot_info[get_robot_id(obj)].mass;
175
                        obj->mtype.phys_info.drag = Robot_info[get_robot_id(obj)].drag;
176
 
177
                        obj->mtype.phys_info.flags |= (PF_LEVELLING);
178
 
179
                        obj->shields = Robot_info[get_robot_id(obj)].strength;
180
                        break;
181
                }
182
                case OBJ_POWERUP:
183
                {
184
                        objnum = obj_create(OBJ_POWERUP, object_id,
185
                                        segp, object_pos, &seg_matrix, Powerup_info[object_id].size,
186
                                        CT_POWERUP, MT_NONE, RT_POWERUP);
187
 
188
                        if ( objnum == object_none)
189
                                return 0;
190
 
191
                        const vmobjptridx_t obj = objnum;
192
 
193
                        //set powerup-specific data
194
 
195
                        obj->rtype.vclip_info.vclip_num = Powerup_info[get_powerup_id(obj)].vclip_num;
196
                        obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].play_time/Vclip[obj->rtype.vclip_info.vclip_num].num_frames;
197
                        obj->rtype.vclip_info.framenum = 0;
198
 
199
                        if (get_powerup_id(obj) == POW_VULCAN_WEAPON)
200
                                obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
201
                        else
202
                                obj->ctype.powerup_info.count = 1;
203
                        break;
204
                }
205
                case OBJ_CNTRLCEN:
206
                {
207
                        objnum = obj_create(OBJ_CNTRLCEN, object_id, segp, object_pos,
208
                                        &seg_matrix, Polygon_models[object_id].rad,
209
                                        CT_CNTRLCEN, MT_NONE, RT_POLYOBJ);
210
 
211
                        if ( objnum == object_none)
212
                                return 0;
213
 
214
                        const vmobjptridx_t obj = objnum;
215
 
216
                        //Set polygon-object-specific data 
217
                        obj->shields = 0;       // stored in Reactor_strength or calculated
218
#if defined(DXX_BUILD_DESCENT_I)
219
                        obj->rtype.pobj_info.model_num = ObjId[object_type];
220
#elif defined(DXX_BUILD_DESCENT_II)
221
                        obj->rtype.pobj_info.model_num = Reactors[object_id].model_num;
222
#endif
223
                        obj->rtype.pobj_info.subobj_flags = 0;
224
 
225
                        break;
226
                }
227
                case OBJ_PLAYER:        {
228
                        objnum = obj_create(OBJ_PLAYER, object_id, segp, object_pos,
229
                                &seg_matrix, Polygon_models[Player_ship->model_num].rad,
230
                                CT_NONE, MT_PHYSICS, RT_POLYOBJ);
231
 
232
                        if ( objnum == object_none)
233
                                return 0;
234
 
235
                        const vmobjptridx_t obj = objnum;
236
 
237
                        //Set polygon-object-specific data 
238
 
239
                        obj->rtype.pobj_info.model_num = Player_ship->model_num;
240
                        obj->rtype.pobj_info.subobj_flags = 0;
241
                        //for (i=0;i<MAX_SUBMODELS;i++)
242
                        //      vm_angvec_zero(&obj->rtype.pobj_info.anim_angles[i]);
243
 
244
                        //set Physics info
245
 
246
                        vm_vec_zero(obj->mtype.phys_info.velocity);
247
                        obj->mtype.phys_info.mass = Player_ship->mass;
248
                        obj->mtype.phys_info.drag = Player_ship->drag;
249
                        obj->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE;
250
                        obj->shields = i2f(100);
251
                        break;
252
                }
253
                default:
254
                        return 0;
255
                }
256
 
257
        Cur_object_index = objnum;
258
        //Cur_object_seg = Cursegp;
259
 
260
        Update_flags |= UF_WORLD_CHANGED;
261
 
262
        return 1;
263
}
264
 
265
//      ------------------------------------------------------------------------------------------------------
266
//      Count number of player objects, return value.
267
static int compute_num_players(void)
268
{
269
        auto &Objects = LevelUniqueObjectState.Objects;
270
        auto &vcobjptr = Objects.vcptr;
271
        int     count = 0;
272
 
273
        range_for (const auto &&objp, vcobjptr)
274
        {
275
                if (objp->type == OBJ_PLAYER)
276
                        count++;
277
        }
278
 
279
        return count;
280
 
281
}
282
 
283
int ObjectMakeCoop(void)
284
{
285
        auto &Objects = LevelUniqueObjectState.Objects;
286
        auto &vmobjptr = Objects.vmptr;
287
        Assert(Cur_object_index != object_none);
288
        Assert(Cur_object_index < MAX_OBJECTS);
289
//      Assert(Objects[Cur_object_index.type == OBJ_PLAYER);
290
 
291
        const auto &&objp = vmobjptr(Cur_object_index);
292
        if (objp->type == OBJ_PLAYER)
293
        {
294
                objp->type = OBJ_COOP;
295
                editor_status("You just made a player object COOPERATIVE");
296
        } else
297
                editor_status("This is not a player object");
298
 
299
        return 1;
300
}
301
 
302
//      ------------------------------------------------------------------------------------------------------
303
//      Place current object at center of current segment.
304
int ObjectPlaceObject(void)
305
{
306
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
307
        auto &Objects = LevelUniqueObjectState.Objects;
308
        auto &Vertices = LevelSharedVertexState.get_vertices();
309
        auto &vmobjptr = Objects.vmptr;
310
        int     old_cur_object_index;
311
        int     rval;
312
        if (Cur_object_type == OBJ_PLAYER)
313
        {
314
                int num_players = compute_num_players();
315
                Assert(num_players <= MAX_MULTI_PLAYERS);
316
                if (num_players > MAX_PLAYERS)
317
                        editor_status("You just placed a cooperative player object");
318
                if (num_players == MAX_MULTI_PLAYERS) {
319
                        editor_status_fmt("Can't place player object.  Already %i players.", MAX_MULTI_PLAYERS);
320
                        return -1;
321
                }
322
        }
323
 
324
        //update_due_to_new_segment();
325
        auto &vcvertptr = Vertices.vcptr;
326
        const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp);
327
 
328
        old_cur_object_index = Cur_object_index;
329
        rval = place_object(Cursegp, cur_object_loc, Cur_object_type, Cur_object_id);
330
 
331
        if (old_cur_object_index != Cur_object_index)
332
                vmobjptr(Cur_object_index)->rtype.pobj_info.tmap_override = -1;
333
 
334
        return rval;
335
 
336
}
337
 
338
//      ------------------------------------------------------------------------------------------------------
339
//      Place current object at center of current segment.
340
int ObjectPlaceObjectTmap(void)
341
{
342
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
343
        auto &Objects = LevelUniqueObjectState.Objects;
344
        auto &Vertices = LevelSharedVertexState.get_vertices();
345
        int     rval, old_cur_object_index;
346
        //update_due_to_new_segment();
347
        auto &vcvertptr = Vertices.vcptr;
348
        const auto cur_object_loc = compute_segment_center(vcvertptr, Cursegp);
349
 
350
        old_cur_object_index = Cur_object_index;
351
        rval = place_object(Cursegp, cur_object_loc, Cur_object_type, Cur_object_id);
352
 
353
        if ((Cur_object_index != old_cur_object_index) && (Objects[Cur_object_index].render_type == RT_POLYOBJ))
354
                Objects[Cur_object_index].rtype.pobj_info.tmap_override = CurrentTexture;
355
        else
356
                editor_status("Unable to apply current texture map to this object.");
357
 
358
        return rval;
359
}
360
 
361
//      ------------------------------------------------------------------------------------------------------
362
int ObjectSelectNextinSegment(void)
363
{
364
        auto &Objects = LevelUniqueObjectState.Objects;
365
        //update_due_to_new_segment();
366
 
367
        //Assert(Cur_object_seg == Cursegp);
368
 
369
        const vmsegptr_t objsegp = Cursegp;
370
        if (Cur_object_index == object_none) {
371
                Cur_object_index = objsegp->objects;
372
        } else {
373
                if (Objects[Cur_object_index].segnum != Cursegp)
374
                        Cur_object_index = objsegp->objects;
375
        }
376
 
377
 
378
        //Debug: make sure current object is in current segment
379
        objnum_t id;
380
        for (id=objsegp->objects;(id != Cur_object_index)  && (id != object_none);id=Objects[id].next);
381
        Assert(id == Cur_object_index);         //should have found object
382
 
383
        //      Select the next object, wrapping back to start if we are at the end of the linked list for this segment.
384
        if (id != object_none)
385
                Cur_object_index = get_next_object(objsegp,Cur_object_index);
386
 
387
        Update_flags |= UF_WORLD_CHANGED;
388
 
389
        return 1;
390
 
391
}
392
 
393
//Moves to next object in the mine, skipping the player
394
int ObjectSelectNextInMine()
395
{       int i;
396
        auto &Objects = LevelUniqueObjectState.Objects;
397
        auto &vcobjptr = Objects.vcptr;
398
        for (i=0;i<MAX_OBJECTS;i++) {
399
                Cur_object_index++;
400
                if (Cur_object_index>= MAX_OBJECTS ) Cur_object_index= 0;
401
 
402
                const auto &&objp = vcobjptr(Cur_object_index);
403
                if (objp->type != OBJ_NONE && objp != ConsoleObject)
404
                {
405
                        Cursegp = imsegptridx(objp->segnum);
406
                        med_create_new_segment_from_cursegp();
407
                        //Cur_object_seg = Cursegp;
408
                        return 1;
409
                }
410
        }
411
        Cur_object_index = object_none;
412
 
413
        Update_flags |= UF_WORLD_CHANGED;
414
 
415
        return 0;
416
}
417
 
418
//Moves to next object in the mine, skipping the player
419
int ObjectSelectPrevInMine()
420
{       int i;
421
        auto &Objects = LevelUniqueObjectState.Objects;
422
        auto &vcobjptr = Objects.vcptr;
423
        for (i=0;i<MAX_OBJECTS;i++) {
424
                if (!(Cur_object_index --))
425
                        Cur_object_index = MAX_OBJECTS-1;
426
 
427
                const auto &&objp = vcobjptr(Cur_object_index);
428
                if (objp->type != OBJ_NONE && objp != ConsoleObject)
429
                {
430
                        Cursegp = imsegptridx(objp->segnum);
431
                        med_create_new_segment_from_cursegp();
432
                        //Cur_object_seg = Cursegp;
433
                        return 1;
434
                }
435
        }
436
        Cur_object_index = object_none;
437
 
438
        Update_flags |= UF_WORLD_CHANGED;
439
 
440
        return 0;
441
}
442
 
443
//      ------------------------------------------------------------------------------------------------------
444
//      Delete current object, if it exists.
445
//      If it doesn't exist, reformat Matt's hard disk, even if he is in Boston.
446
int ObjectDelete(void)
447
{
448
        auto &Objects = LevelUniqueObjectState.Objects;
449
        auto &vmobjptridx = Objects.vmptridx;
450
 
451
        if (Cur_object_index != object_none) {
452
                auto delete_objnum = Cur_object_index;
453
                ObjectSelectNextinSegment();
454
 
455
                obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(delete_objnum));
456
 
457
                if (delete_objnum == Cur_object_index)
458
                        Cur_object_index = object_none;
459
 
460
                Update_flags |= UF_WORLD_CHANGED;
461
        }
462
 
463
        return 1;
464
}
465
 
466
//      -----------------------------------------------------------------------------------------------------------------
467
//      Object has moved to another segment, (or at least poked through).
468
//      If still in mine, that is legal, so relink into new segment.
469
//      Return value:   0 = in mine, 1 = not in mine
470
static int move_object_within_mine(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t obj, const vms_vector &newpos)
471
{
472
        range_for (const auto &&segp, Segments.vmptridx)
473
        {
474
                if (get_seg_masks(vcvertptr, obj->pos, segp, 0).centermask == 0) {
475
                        int     fate;
476
                        fvi_info        hit_info;
477
                        fvi_query fq;
478
 
479
                        //      See if the radius pokes through any wall.
480
                        fq.p0                                           = &obj->pos;
481
                        fq.startseg                             = obj->segnum;
482
                        fq.p1                                           = &newpos;
483
                        fq.rad                                  = obj->size;
484
                        fq.thisobjnum                   = object_none;
485
                        fq.ignore_obj_list.first = nullptr;
486
                        fq.flags                                        = 0;
487
 
488
                        fate = find_vector_intersection(fq, hit_info);
489
 
490
                        if (fate != HIT_WALL) {
491
                                if (segp != obj->segnum)
492
                                        obj_relink(vmobjptr, Segments.vmptr, obj, segp);
493
                                obj->pos = newpos;
494
                                return 0;
495
                        }
496
                }
497
        }
498
        return 1;
499
}
500
 
501
//      Return 0 if object is in expected segment, else return 1
502
static int verify_object_seg(fvmobjptr &vmobjptr, segment_array &Segments, fvcvertptr &vcvertptr, const vmobjptridx_t objp, const vms_vector &newpos)
503
{
504
        const auto &&result = get_seg_masks(vcvertptr, newpos, Segments.vcptr(objp->segnum), objp->size);
505
        if (result.facemask == 0)
506
                return 0;
507
        else
508
                return move_object_within_mine(vmobjptr, Segments, vcvertptr, objp, newpos);
509
}
510
 
511
namespace {
512
 
513
class extract_fvec_from_segment
514
{
515
public:
516
        static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
517
        {
518
                vms_vector v;
519
                extract_forward_vector_from_segment(vcvertptr, segp, v);
520
                return v;
521
        }
522
};
523
 
524
class extract_rvec_from_segment
525
{
526
public:
527
        static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
528
        {
529
                vms_vector v;
530
                extract_right_vector_from_segment(vcvertptr, segp, v);
531
                return v;
532
        }
533
};
534
 
535
class extract_uvec_from_segment
536
{
537
public:
538
        static vms_vector get(fvcvertptr &vcvertptr, const shared_segment &segp)
539
        {
540
                vms_vector v;
541
                extract_up_vector_from_segment(vcvertptr, segp, v);
542
                return v;
543
        }
544
};
545
 
546
static int ObjectMoveFailed()
547
{
548
        editor_status("No current object, cannot move.");
549
        return 1;
550
}
551
 
552
static int ObjectMovePos(const vmobjptridx_t obj, vms_vector &&vec, int scale)
553
{
554
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
555
        auto &Objects = LevelUniqueObjectState.Objects;
556
        auto &Vertices = LevelSharedVertexState.get_vertices();
557
        auto &vmobjptr = Objects.vmptr;
558
        vm_vec_normalize(vec);
559
        const auto &&newpos = vm_vec_add(obj->pos, vm_vec_scale(vec, scale));
560
        auto &vcvertptr = Vertices.vcptr;
561
        if (!verify_object_seg(vmobjptr, Segments, vcvertptr, obj, newpos))
562
                obj->pos = newpos;
563
        Update_flags |= UF_WORLD_CHANGED;
564
        return 1;
565
}
566
 
567
template <typename extract_type, int direction>
568
static int ObjectMove()
569
{
570
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
571
        auto &Objects = LevelUniqueObjectState.Objects;
572
        auto &Vertices = LevelSharedVertexState.get_vertices();
573
        auto &vmobjptridx = Objects.vmptridx;
574
        const auto i = Cur_object_index;
575
        if (i == object_none)
576
                return ObjectMoveFailed();
577
        const auto &&obj = vmobjptridx(i);
578
        auto &vcvertptr = Vertices.vcptr;
579
        return ObjectMovePos(obj, extract_type::get(vcvertptr, vcsegptr(obj->segnum)), direction * OBJ_SCALE);
580
}
581
 
582
}
583
 
584
//      ------------------------------------------------------------------------------------------------------
585
int     ObjectMoveForward(void)
586
{
587
        return ObjectMove<extract_fvec_from_segment, 1>();
588
}
589
 
590
//      ------------------------------------------------------------------------------------------------------
591
int     ObjectMoveBack(void)
592
{
593
        return ObjectMove<extract_fvec_from_segment, -1>();
594
}
595
 
596
//      ------------------------------------------------------------------------------------------------------
597
int     ObjectMoveLeft(void)
598
{
599
        return ObjectMove<extract_rvec_from_segment, -1>();
600
}
601
 
602
//      ------------------------------------------------------------------------------------------------------
603
int     ObjectMoveRight(void)
604
{
605
        return ObjectMove<extract_rvec_from_segment, 1>();
606
}
607
 
608
//      ------------------------------------------------------------------------------------------------------
609
int     ObjectSetDefault(void)
610
{
611
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
612
        auto &Objects = LevelUniqueObjectState.Objects;
613
        auto &Vertices = LevelSharedVertexState.get_vertices();
614
        auto &vmobjptr = Objects.vmptr;
615
        //update_due_to_new_segment();
616
 
617
        if (Cur_object_index == object_none) {
618
                editor_status("No current object, cannot move.");
619
                return 1;
620
        }
621
 
622
        const auto &&objp = vmobjptr(Cur_object_index);
623
        auto &vcvertptr = Vertices.vcptr;
624
        compute_segment_center(vcvertptr, objp->pos, vcsegptr(objp->segnum));
625
 
626
        Update_flags |= UF_WORLD_CHANGED;
627
 
628
        return 1;
629
}
630
 
631
 
632
//      ------------------------------------------------------------------------------------------------------
633
int     ObjectMoveUp(void)
634
{
635
        return ObjectMove<extract_uvec_from_segment, 1>();
636
}
637
 
638
//      ------------------------------------------------------------------------------------------------------
639
int     ObjectMoveDown(void)
640
{
641
        return ObjectMove<extract_uvec_from_segment, -1>();
642
}
643
 
644
//      ------------------------------------------------------------------------------------------------------
645
 
646
static int rotate_object(const vmobjptridx_t obj, int p, int b, int h)
647
{
648
        vms_angvec ang;
649
//      vm_extract_angles_matrix( &ang,&obj->orient);
650
 
651
//      ang.p += p;
652
//      ang.b += b;
653
//      ang.h += h;
654
 
655
        ang.p = p;
656
        ang.b = b;
657
        ang.h = h;
658
 
659
        const auto rotmat = vm_angles_2_matrix(ang);
660
        obj->orient = vm_matrix_x_matrix(obj->orient, rotmat);
661
//   vm_angles_2_matrix(&obj->orient, &ang);
662
 
663
        Update_flags |= UF_WORLD_CHANGED;
664
 
665
        return 1;
666
}
667
 
668
static void reset_object(const vmobjptridx_t obj)
669
{
670
        med_extract_matrix_from_segment(vcsegptr(obj->segnum), obj->orient);
671
}
672
 
673
int ObjectResetObject()
674
{
675
        auto &Objects = LevelUniqueObjectState.Objects;
676
        auto &vmobjptridx = Objects.vmptridx;
677
        reset_object(vmobjptridx(Cur_object_index));
678
 
679
        Update_flags |= UF_WORLD_CHANGED;
680
 
681
        return 1;
682
}
683
 
684
 
685
int ObjectFlipObject()
686
{
687
        auto &Objects = LevelUniqueObjectState.Objects;
688
        auto &vmobjptr = Objects.vmptr;
689
        const auto m = &vmobjptr(Cur_object_index)->orient;
690
 
691
        vm_vec_negate(m->uvec);
692
        vm_vec_negate(m->rvec);
693
 
694
        Update_flags |= UF_WORLD_CHANGED;
695
 
696
        return 1;
697
}
698
 
699
template <int p, int b, int h>
700
int ObjectChangeRotation()
701
{
702
        auto &Objects = LevelUniqueObjectState.Objects;
703
        auto &vmobjptridx = Objects.vmptridx;
704
        return rotate_object(vmobjptridx(Cur_object_index), p, b, h);
705
}
706
 
707
template int ObjectDecreaseBank();
708
template int ObjectIncreaseBank();
709
template int ObjectDecreasePitch();
710
template int ObjectIncreasePitch();
711
template int ObjectDecreaseHeading();
712
template int ObjectIncreaseHeading();
713
 
714
template int ObjectDecreaseBankBig();
715
template int ObjectIncreaseBankBig();
716
template int ObjectDecreasePitchBig();
717
template int ObjectIncreasePitchBig();
718
template int ObjectDecreaseHeadingBig();
719
template int ObjectIncreaseHeadingBig();
720
 
721
//      -----------------------------------------------------------------------------------------------------
722
//      Move object around based on clicks in 2d screen.
723
//      Slide an object parallel to the 2d screen, to a point on a vector.
724
//      The vector is defined by a point on the 2d screen and the eye.
725
 
726
//      V       =       vector from eye to 2d screen point.
727
//      E       =       eye
728
//      F       =       forward vector from eye
729
//      O       =       3-space location of object
730
 
731
//      D       =       depth of object given forward vector F
732
//              =       (OE dot norm(F))
733
 
734
//      Must solve intersection of:
735
//              E + tV          ( equation of vector from eye through point on 2d screen)
736
//              Fs + D          ( equation of plane parallel to 2d screen, at depth D)
737
//              =       Fx(Ex + tVx) + Fy(Ey + tVy) + Fz(Ez + tVz) + D = 0
738
//
739
//                            FxEx + FyEy + FzEz - D
740
//                      t = - ----------------------
741
//                                        VxFx + VyFy + VzFz
742
 
743
static void move_object_to_position(const vmobjptridx_t objp, const vms_vector &newpos)
744
{
745
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
746
        auto &Objects = LevelUniqueObjectState.Objects;
747
        auto &Vertices = LevelSharedVertexState.get_vertices();
748
        auto &vmobjptr = Objects.vmptr;
749
        auto &vcvertptr = Vertices.vcptr;
750
        if (get_seg_masks(vcvertptr, newpos, vcsegptr(objp->segnum), objp->size).facemask == 0)
751
        {
752
                objp->pos = newpos;
753
        } else {
754
                if (verify_object_seg(vmobjptr, Segments, vcvertptr, objp, newpos)) {
755
                        int             fate;
756
                        object  temp_viewer_obj;
757
                        fvi_query fq;
758
                        fvi_info        hit_info;
759
 
760
                        temp_viewer_obj = *Viewer;
761
                        auto viewer_segnum = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, *Viewer);
762
                        temp_viewer_obj.segnum = viewer_segnum;
763
 
764
                        //      If the viewer is outside the mine, get him in the mine!
765
                        if (viewer_segnum == segment_none) {
766
                                editor_status("Unable to move object, viewer not in mine.  Aborting");
767
                                return;
768
#if 0
769
                                vms_vector      last_outside_pos;
770
                                //      While outside mine, move towards object
771
                                count = 0;
772
                                while (viewer_segnum == segment_none) {
773
                                        vms_vector      temp_vec;
774
 
775
                                        last_outside_pos = temp_viewer_obj.pos;
776
 
777
                                        vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, newpos);
778
                                        temp_viewer_obj.pos = temp_vec;
779
                                        viewer_segnum = find_object_seg(&temp_viewer_obj);
780
                                        temp_viewer_obj.segnum = viewer_segnum;
781
 
782
                                        if (count > 5) {
783
                                                editor_status("Unable to move object, can't get viewer in mine.  Aborting");
784
                                                return;
785
                                        }
786
                                }
787
 
788
                                count = 0;
789
                                //      While inside mine, move away from object.
790
                                while (viewer_segnum != segment_none) {
791
 
792
                                        vms_vector      temp_vec;
793
 
794
                                        vm_vec_avg(&temp_vec, &temp_viewer_obj.pos, &last_outside_pos);
795
                                        temp_viewer_obj.pos = temp_vec;
796
                                        update_object_seg(&temp_viewer_obj);
797
                                        viewer_segnum = find_object_seg(&temp_viewer_obj);
798
                                        temp_viewer_obj.segnum = viewer_segnum;
799
 
800
                                        if (count > 5) {
801
                                                editor_status("Unable to move object, can't get viewer back out of mine.  Aborting");
802
                                                return;
803
                                        }
804
                                }
805
#endif
806
                        }
807
 
808
                        fq.p0                                           = &temp_viewer_obj.pos;
809
                        fq.startseg                             = temp_viewer_obj.segnum;
810
                        fq.p1                                           = &newpos;
811
                        fq.rad                                  = temp_viewer_obj.size;
812
                        fq.thisobjnum                   = object_none;
813
                        fq.ignore_obj_list.first = nullptr;
814
                        fq.flags                                        = 0;
815
 
816
                        fate = find_vector_intersection(fq, hit_info);
817
                        if (fate == HIT_WALL) {
818
 
819
                                objp->pos = hit_info.hit_pnt;
820
                                const auto &&segp = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, objp);
821
                                if (segp != segment_none)
822
                                        obj_relink(vmobjptr, vmsegptr, objp, segp);
823
                        } else {
824
                                editor_status("Attempted to move object out of mine.  Object not moved.");
825
                        }
826
                }
827
        }
828
 
829
        Update_flags |= UF_WORLD_CHANGED;
830
}
831
 
832
static void move_object_to_vector(const vms_vector &vec_through_screen, fix delta_distance)
833
{
834
        auto &Objects = LevelUniqueObjectState.Objects;
835
        auto &vmobjptridx = Objects.vmptridx;
836
        const auto &&objp = vmobjptridx(Cur_object_index);
837
        const auto result = vm_vec_scale_add(Viewer->pos, vec_through_screen, vm_vec_dist(Viewer->pos, objp->pos) + delta_distance);
838
        move_object_to_position(objp, result);
839
}
840
 
841
}
842
 
843
static void move_object_to_mouse_click_delta(fix delta_distance)
844
{
845
        short                   xcrd,ycrd;
846
        vms_vector      vec_through_screen;
847
 
848
        if (Cur_object_index == object_none) {
849
                editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
850
                return;
851
        }
852
 
853
        xcrd = GameViewBox->b1_drag_x1;
854
        ycrd = GameViewBox->b1_drag_y1;
855
 
856
        med_point_2_vec(&_canv_editor_game, vec_through_screen, xcrd, ycrd);
857
 
858
        move_object_to_vector(vec_through_screen, delta_distance);
859
 
860
}
861
 
862
void move_object_to_mouse_click(void)
863
{
864
        move_object_to_mouse_click_delta(0);
865
}
866
 
867
namespace dsx {
868
 
869
int     ObjectMoveNearer(void)
870
{
871
        auto &Objects = LevelUniqueObjectState.Objects;
872
        auto &vcobjptr = Objects.vcptr;
873
        if (Cur_object_index == object_none) {
874
                editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
875
                return 1;
876
        }
877
 
878
//      move_object_to_mouse_click_delta(-4*F1_0);              //      Move four units closer to eye
879
 
880
        const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos));
881
        move_object_to_vector(result, -4*F1_0);
882
 
883
        return 1;      
884
}
885
 
886
int     ObjectMoveFurther(void)
887
{
888
        auto &Objects = LevelUniqueObjectState.Objects;
889
        auto &vcobjptr = Objects.vcptr;
890
        if (Cur_object_index == object_none) {
891
                editor_status("Cur_object_index == -1, cannot move that peculiar object...aborting!");
892
                return 1;
893
        }
894
 
895
//      move_object_to_mouse_click_delta(+4*F1_0);              //      Move four units further from eye
896
 
897
        const auto &&result = vm_vec_normalized(vm_vec_sub(vcobjptr(Cur_object_index)->pos, Viewer->pos));
898
        move_object_to_vector(result, 4*F1_0);
899
 
900
        return 1;      
901
}
902
 
903
}