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