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