1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * object system definitions
23  *
24  */
25 
26 #pragma once
27 
28 #include <type_traits>
29 #include "dsx-ns.h"
30 #ifdef dsx
31 
32 #include "pstypes.h"
33 #include "vecmat.h"
34 #include "aistruct.h"
35 #include "polyobj.h"
36 
37 #include <cassert>
38 #include <cstdint>
39 #include "dxxsconf.h"
40 #include "valptridx.h"
41 #include "objnum.h"
42 #include "fwd-segment.h"
43 #include <vector>
44 #include <stdexcept>
45 #include "fwd-object.h"
46 #include "fwd-weapon.h"
47 #include "fwd-player.h"
48 #include "powerup.h"
49 #include "compiler-poison.h"
50 #include "physics_info.h"
51 #include "player-flags.h"
52 #if defined(DXX_BUILD_DESCENT_II)
53 #include "escort.h"
54 #endif
55 #include <array>
56 #include <utility>
57 
58 namespace dcx {
59 
60 // Object types
61 enum object_type_t : uint8_t
62 {
63 	OBJ_NONE	= 255, // unused object
64 	OBJ_WALL	= 0,   // A wall... not really an object, but used for collisions
65 	OBJ_FIREBALL	= 1,   // a fireball, part of an explosion
66 	OBJ_ROBOT	= 2,   // an evil enemy
67 	OBJ_HOSTAGE	= 3,   // a hostage you need to rescue
68 	OBJ_PLAYER	= 4,   // the player on the console
69 	OBJ_WEAPON	= 5,   // a laser, missile, etc
70 	OBJ_CAMERA	= 6,   // a camera to slew around with
71 	OBJ_POWERUP	= 7,   // a powerup you can pick up
72 	OBJ_DEBRIS	= 8,   // a piece of robot
73 	OBJ_CNTRLCEN	= 9,   // the control center
74 	OBJ_CLUTTER	= 11,  // misc objects
75 	OBJ_GHOST	= 12,  // what the player turns into when dead
76 	OBJ_LIGHT	= 13,  // a light source, & not much else
77 	OBJ_COOP	= 14,  // a cooperative player object.
78 	OBJ_MARKER	= 15,  // a map marker
79 };
80 
81 enum render_type_t : uint8_t
82 {
83 	RT_NONE = 0,   // does not render
84 	RT_POLYOBJ = 1,   // a polygon model
85 	RT_FIREBALL = 2,   // a fireball
86 	RT_LASER = 3,   // a laser
87 	RT_HOSTAGE = 4,   // a hostage
88 	RT_POWERUP = 5,   // a powerup
89 	RT_MORPH = 6,   // a robot being morphed
90 	RT_WEAPON_VCLIP = 7,   // a weapon that renders as a vclip
91 };
92 
valid_render_type(const uint8_t r)93 static inline bool valid_render_type(const uint8_t r)
94 {
95 	switch (r)
96 	{
97 		case RT_NONE:
98 		case RT_POLYOBJ:
99 		case RT_FIREBALL:
100 		case RT_LASER:
101 		case RT_HOSTAGE:
102 		case RT_POWERUP:
103 		case RT_MORPH:
104 		case RT_WEAPON_VCLIP:
105 			return true;
106 		default:
107 			return false;
108 	}
109 }
110 
111 }
112 
113 namespace dsx {
114 
115 /*
116  * STRUCTURES
117  */
118 
119 struct reactor_static {
120 	/* Location of the gun on the reactor object */
121 	std::array<vms_vector, MAX_CONTROLCEN_GUNS>	gun_pos,
122 	/* Orientation of the gun on the reactor object */
123 		gun_dir;
124 };
125 
126 struct player_info
127 {
128 	fix     energy;                 // Amount of energy remaining.
129 	fix     homing_object_dist;     // Distance of nearest homing object.
130 	fix Fusion_charge;
131 #if defined(DXX_BUILD_DESCENT_II)
132 	fix Omega_charge;
133 	fix Omega_recharge_delay;
134 #endif
135 	player_flags powerup_flags;
136 	objnum_t   killer_objnum;          // Who killed me.... (-1 if no one)
137 	uint16_t vulcan_ammo;
138 #if defined(DXX_BUILD_DESCENT_I)
139 	using primary_weapon_flag_type = uint8_t;
140 #elif defined(DXX_BUILD_DESCENT_II)
141 	using primary_weapon_flag_type = uint16_t;
142 #endif
143 	primary_weapon_flag_type primary_weapon_flags;
144 	bool Player_eggs_dropped;
145 	bool FakingInvul;
146 	bool lavafall_hiss_playing;
147 	uint8_t missile_gun;
148 	player_selected_weapon<primary_weapon_index_t> Primary_weapon;
149 	player_selected_weapon<secondary_weapon_index_t> Secondary_weapon;
150 	enum laser_level laser_level;
151 	std::array<uint8_t, MAX_SECONDARY_WEAPONS>  secondary_ammo; // How much ammo of each type.
152 	uint8_t Spreadfire_toggle;
153 #if defined(DXX_BUILD_DESCENT_II)
154 	uint8_t Primary_last_was_super;
155 	uint8_t Secondary_last_was_super;
156 	uint8_t Helix_orientation;
157 #endif
158 	int16_t net_killed_total;		// Number of times killed total
159 	int16_t net_kills_total;		// Number of net kills total
160 	int16_t KillGoalCount;			// Num of players killed this level
161 	union {
162 		struct {
163 			int score;				// Current score.
164 			int last_score;			// Score at beginning of current level.
165 			uint16_t hostages_rescued_total; // Total number of hostages rescued.
166 			uint8_t hostages_on_board;
167 		} mission;
168 		struct {
169 			uint8_t orbs;
170 		} hoard;
171 	};
172 	enum
173 	{
174 		max_hoard_orbs = 12,
175 	};
176 	fix64   cloak_time;             // Time cloaked
177 	fix64   invulnerable_time;      // Time invulnerable
178 	fix64 Next_flare_fire_time;
179 	fix64 Next_laser_fire_time;
180 	fix64 Next_missile_fire_time;
181 	fix64 Last_bumped_local_player;
182 	fix64 Auto_fire_fusion_cannon_time;
183 };
184 
185 }
186 
187 namespace dcx {
188 
189 // A compressed form for sending crucial data
190 struct shortpos : prohibit_void_ptr<shortpos>
191 {
192 	std::array<int8_t, 9> bytemat;
193 	int16_t xo, yo, zo;
194 	segnum_t segment;
195 	int16_t velx, vely, velz;
196 };
197 
198 // Another compressed form for object position, velocity, orientation and rotvel using quaternion
199 struct quaternionpos : prohibit_void_ptr<quaternionpos>
200 {
201 	using packed_size = std::integral_constant<std::size_t, sizeof(vms_quaternion) + sizeof(segnum_t) + (sizeof(vms_vector) * 3)>;
202 	vms_quaternion orient;
203 	vms_vector pos;
204 	segnum_t segment;
205 	vms_vector vel;
206 	vms_vector rotvel;
207 };
208 
209 // stuctures for different kinds of simulation
210 
211 struct laser_parent
212 {
213 	int16_t parent_type = {};        // The type of the parent of this object
214 	objnum_t parent_num = {};         // The object's parent's number
215 	object_signature_t parent_signature = object_signature_t{0};   // The object's parent's signature...
216 };
217 
218 struct laser_info : prohibit_void_ptr<laser_info>, laser_parent
219 {
220 	fix64 creation_time = 0;      // Absolute time of creation.
221 	/* hitobj_pos specifies the next position to which a value should be
222 	 * written.  That position may have a defined value if the array has
223 	 * wrapped, but should be treated as write-only in the general case.
224 	 *
225 	 * hitobj_count tells how many elements in hitobj_values[] are
226 	 * valid.  Its valid values are [0, hitobj_values.size()].  When
227 	 * hitobj_count == hitobj_values.size(), hitobj_pos wraps around and
228 	 * begins erasing the oldest elements first.
229 	 */
230 	uint8_t hitobj_pos = 0, hitobj_count = 0;
231 	std::array<objnum_t, 83> hitobj_values = {};
232 	objnum_t track_goal = 0;         // Object this object is tracking.
233 	fix multiplier = 0;         // Power if this is a fusion bolt (or other super weapon to be added).
234 	uint_fast8_t test_set_hitobj(const vcobjidx_t o);
235 	uint_fast8_t test_hitobj(const vcobjidx_t o) const;
236 	icobjidx_t get_last_hitobj() const;
clear_hitobjlaser_info237 	void clear_hitobj()
238 	{
239 		hitobj_pos = hitobj_count = 0;
240 	}
reset_hitobjlaser_info241 	void reset_hitobj(const icobjidx_t o)
242 	{
243 		if (o == object_none)
244 		{
245 			/* Adding object_none to the array is harmless, since
246 			 * get_last_hitobj can return object_none for empty arrays.
247 			 * However, by filtering it here (which is called only when
248 			 * loading data into new objects), test_hitobj (which is
249 			 * called every time the object strikes a potential target)
250 			 * will not need to read a slot that is guaranteed not to
251 			 * match.
252 			 */
253 			clear_hitobj();
254 			return;
255 		}
256 		/* Assume caller already poisoned the unused array elements */
257 		hitobj_pos = hitobj_count = 1;
258 		hitobj_values[0] = o;
259 	}
260 };
261 
262 }
263 
264 namespace dcx {
265 
266 // Same as above but structure Savegames/Multiplayer objects expect
267 struct laser_info_rw
268 {
269 	short   parent_type;        // The type of the parent of this object
270 	short   parent_num;         // The object's parent's number
271 	int     parent_signature;   // The object's parent's signature...
272 	fix     creation_time;      // Absolute time of creation.
273 	short   last_hitobj;        // For persistent weapons (survive object collision), object it most recently hit.
274 	short   track_goal;         // Object this object is tracking.
275 	fix     multiplier;         // Power if this is a fusion bolt (or other super weapon to be added).
276 } __pack__;
277 
278 struct explosion_info : prohibit_void_ptr<explosion_info>
279 {
280     fix     spawn_time;         // when lifeleft is < this, spawn another
281     fix     delete_time;        // when to delete object
282     objnum_t   delete_objnum;      // and what object to delete
283     objnum_t   attach_parent;      // explosion is attached to this object
284     objnum_t   prev_attach;        // previous explosion in attach list
285     objnum_t   next_attach;        // next explosion in attach list
286 };
287 
288 struct explosion_info_rw
289 {
290     fix     spawn_time;         // when lifeleft is < this, spawn another
291     fix     delete_time;        // when to delete object
292     short   delete_objnum;      // and what object to delete
293     short   attach_parent;      // explosion is attached to this object
294     short   prev_attach;        // previous explosion in attach list
295     short   next_attach;        // next explosion in attach list
296 } __pack__;
297 
298 struct light_info : prohibit_void_ptr<light_info>
299 {
300     fix     intensity;          // how bright the light is
301 };
302 
303 struct light_info_rw
304 {
305     fix     intensity;          // how bright the light is
306 } __pack__;
307 
308 struct powerup_info : prohibit_void_ptr<powerup_info>
309 {
310 	int     count;          // how many/much we pick up (vulcan cannon only?)
311 	int     flags;          // spat by player?
312 	fix64   creation_time;  // Absolute time of creation.
313 };
314 
315 }
316 
317 namespace dsx {
318 
319 struct powerup_info_rw
320 {
321 	int     count;          // how many/much we pick up (vulcan cannon only?)
322 #if defined(DXX_BUILD_DESCENT_II)
323 // Same as above but structure Savegames/Multiplayer objects expect
324 	fix     creation_time;  // Absolute time of creation.
325 	int     flags;          // spat by player?
326 #endif
327 } __pack__;
328 
329 }
330 
331 namespace dcx {
332 
333 struct vclip_info : prohibit_void_ptr<vclip_info>
334 {
335 	int     vclip_num;
336 	fix     frametime;
337 	uint8_t framenum;
338 };
339 
340 struct vclip_info_rw
341 {
342 	int     vclip_num;
343 	fix     frametime;
344 	sbyte   framenum;
345 } __pack__;
346 
347 // structures for different kinds of rendering
348 
349 struct polyobj_info : prohibit_void_ptr<polyobj_info>
350 {
351 	int     model_num = 0;          // which polygon model
352 	std::array<vms_angvec, MAX_SUBMODELS> anim_angles{}; // angles for each subobject
353 	int     subobj_flags = 0;       // specify which subobjs to draw
354 	int     tmap_override = 0;      // if this is not -1, map all face to this
355 	int     alt_textures = 0;       // if not -1, use these textures instead
356 };
357 
358 struct polyobj_info_rw
359 {
360 	int     model_num;          // which polygon model
361 	vms_angvec anim_angles[MAX_SUBMODELS]; // angles for each subobject
362 	int     subobj_flags;       // specify which subobjs to draw
363 	int     tmap_override;      // if this is not -1, map all face to this
364 	int     alt_textures;       // if not -1, use these textures instead
365 } __pack__;
366 
367 struct object_base
368 {
369 	enum class control_type : uint8_t;
370 	enum class movement_type : uint8_t;
371 	object_signature_t signature;
372 	object_type_t   type;           // what type of object this is... robot, weapon, hostage, powerup, fireball
373 	ubyte   id;             // which form of object...which powerup, robot, etc.
374 	objnum_t   next,prev;      // id of next and previous connected object in Objects, -1 = no connection
375 	enum control_type control_source;   // how this object is controlled
376 	enum movement_type movement_source; // how this object moves
377 	render_type_t render_type;    // how this object renders
378 	ubyte   flags;          // misc flags
379 	segnum_t   segnum;         // segment number containing object
380 	objnum_t   attached_obj;   // number of attached fireball object
381 	vms_vector pos;         // absolute x,y,z coordinate of center of object
382 	vms_matrix orient;      // orientation of object in world
383 	fix     size;           // 3d size of object - for collision detection
384 	fix     shields;        // Starts at maximum, when <0, object dies..
385 	sbyte   contains_type;  // Type of object this object contains (eg, spider contains powerup)
386 	sbyte   contains_id;    // ID of object this object contains (eg, id = blue type = key)
387 	sbyte   contains_count; // number of objects of type:id this object contains
388 	sbyte   matcen_creator; // Materialization center that created this object, high bit set if matcen-created
389 	fix     lifeleft;       // how long until goes away, or 7fff if immortal
390 	// -- Removed, MK, 10/16/95, using lifeleft instead: int     lightlevel;
391 
392 	// movement info, determined by MOVEMENT_TYPE
393 	union movement_info {
394 		physics_info phys_info; // a physics object
395 		vms_vector   spin_rate; // for spinning objects
movement_info()396 		constexpr movement_info() :
397 			phys_info{}
398 		{
399 			static_assert(sizeof(phys_info) == sizeof(*this), "insufficient initialization");
400 		}
401 	} mtype;
402 
403 	// render info, determined by RENDER_TYPE
404 	union render_info {
405 		struct polyobj_info    pobj_info;      // polygon model
406 		struct vclip_info      vclip_info;     // vclip
render_info()407 		constexpr render_info() :
408 			pobj_info{}
409 		{
410 			static_assert(sizeof(pobj_info) == sizeof(*this), "insufficient initialization");
411 		}
412 	} rtype;
413 };
414 
415 // Control types - what tells this object what do do
416 enum class object_base::control_type : uint8_t
417 {
418 	None	=	0,		// doesn't move (or change movement)
419 	ai	=	1,			// driven by AI
420 	explosion	=	2,	// explosion sequencer
421 	flying	=	4,		// the player is flying
422 	slew	=	5,		// slewing
423 	flythrough	=	6,	// the flythrough system
424 	weapon	=	9,		// laser, etc.
425 	repaircen	=	10,	// under the control of the repair center
426 	morph	=	11,		// this object is being morphed
427 	debris	=	12,	// this is a piece of debris
428 	powerup	=	13,	// animating powerup blob
429 	light	=	14,		// doesn't actually do anything
430 	remote	=	15,	// controlled by another net player
431 	cntrlcen	=	16,	// the control center/main reactor
432 };
433 
434 enum class object_base::movement_type : uint8_t
435 {
436 	None = 0,   // doesn't move
437 	physics = 1,   // moves by physics
438 	spinning = 3,   // this object doesn't move, just sits and spins
439 };
440 
441 }
442 
443 namespace dsx {
444 
445 #if defined(DXX_BUILD_DESCENT_II)
446 struct laser_info : public ::dcx::laser_info
447 {
448 	fix64 last_afterburner_time = 0;	//	Time at which this object last created afterburner blobs.
449 };
450 #endif
451 
452 struct object : public ::dcx::object_base
453 {
454 	// control info, determined by CONTROL_TYPE
455 	union control_info {
control_info()456 		constexpr control_info() :
457 			ai_info{}
458 		{
459 			static_assert(sizeof(ai_info) == sizeof(*this), "insufficient initialization");
460 		}
461 		struct laser_info      laser_info;
462 		struct explosion_info  expl_info;      // NOTE: debris uses this also
463 		struct light_info      light_info;     // why put this here?  Didn't know what else to do with it.
464 		struct powerup_info    powerup_info;
465 		struct ai_static       ai_info;
466 		struct reactor_static  reactor_info;
467 		struct player_info     player_info;
468 	} ctype;
469 };
470 
471 }
472 
473 namespace dcx {
474 
475 // Same as above but structure Savegames/Multiplayer objects expect
476 struct object_rw
477 {
478 	int     signature;      // Every object ever has a unique signature...
479 	ubyte   type;           // what type of object this is... robot, weapon, hostage, powerup, fireball
480 	ubyte   id;             // which form of object...which powerup, robot, etc.
481 	short   next,prev;      // id of next and previous connected object in Objects, -1 = no connection
482 	ubyte   control_source;   // how this object is controlled
483 	ubyte   movement_source;  // how this object moves
484 	ubyte   render_type;    // how this object renders
485 	ubyte   flags;          // misc flags
486 	short   segnum;         // segment number containing object
487 	short   attached_obj;   // number of attached fireball object
488 	vms_vector pos;         // absolute x,y,z coordinate of center of object
489 	vms_matrix orient;      // orientation of object in world
490 	fix     size;           // 3d size of object - for collision detection
491 	fix     shields;        // Starts at maximum, when <0, object dies..
492 	vms_vector last_pos;    // where object was last frame
493 	sbyte   contains_type;  // Type of object this object contains (eg, spider contains powerup)
494 	sbyte   contains_id;    // ID of object this object contains (eg, id = blue type = key)
495 	sbyte   contains_count; // number of objects of type:id this object contains
496 	sbyte   matcen_creator; // Materialization center that created this object, high bit set if matcen-created
497 	fix     lifeleft;       // how long until goes away, or 7fff if immortal
498 	// -- Removed, MK, 10/16/95, using lifeleft instead: int     lightlevel;
499 
500 	// movement info, determined by MOVEMENT_TYPE
501 	union {
502 		physics_info_rw phys_info; // a physics object
503 		vms_vector   spin_rate; // for spinning objects
504 	} __pack__ mtype ;
505 
506 	// control info, determined by CONTROL_TYPE
507 	union {
508 		laser_info_rw   laser_info;
509 		explosion_info_rw  expl_info;      // NOTE: debris uses this also
510 		ai_static_rw    ai_info;
511 		light_info_rw      light_info;     // why put this here?  Didn't know what else to do with it.
512 		powerup_info_rw powerup_info;
513 	} __pack__ ctype ;
514 
515 	// render info, determined by RENDER_TYPE
516 	union {
517 		polyobj_info_rw    pobj_info;      // polygon model
518 		vclip_info_rw      vclip_info;     // vclip
519 	} __pack__ rtype;
520 } __pack__;
521 
522 struct obj_position
523 {
524 	vms_vector  pos;        // absolute x,y,z coordinate of center of object
525 	vms_matrix  orient;     // orientation of object in world
526 	segnum_t       segnum;     // segment number containing object
527 };
528 
529 }
530 
531 #define set_object_type(O,T)	\
532 	( DXX_BEGIN_COMPOUND_STATEMENT {	\
533 		object_base &dxx_object_type_ref = (O);	\
534 		const uint8_t &dxx_object_type_value = (T);	\
535 		assert(	\
536 			dxx_object_type_value == OBJ_NONE ||	\
537 			dxx_object_type_value == OBJ_FIREBALL ||	\
538 			dxx_object_type_value == OBJ_ROBOT ||	\
539 			dxx_object_type_value == OBJ_HOSTAGE ||	\
540 			dxx_object_type_value == OBJ_PLAYER ||	\
541 			dxx_object_type_value == OBJ_WEAPON ||	\
542 			dxx_object_type_value == OBJ_CAMERA ||	\
543 			dxx_object_type_value == OBJ_POWERUP ||	\
544 			dxx_object_type_value == OBJ_DEBRIS ||	\
545 			dxx_object_type_value == OBJ_CNTRLCEN ||	\
546 			dxx_object_type_value == OBJ_CLUTTER ||	\
547 			dxx_object_type_value == OBJ_GHOST ||	\
548 			dxx_object_type_value == OBJ_LIGHT ||	\
549 			dxx_object_type_value == OBJ_COOP ||	\
550 			dxx_object_type_value == OBJ_MARKER	\
551 		);	\
552 		dxx_object_type_ref.type = static_cast<object_type_t>(dxx_object_type_value);	\
553 	} DXX_END_COMPOUND_STATEMENT )
554 
555 namespace dsx {
556 
557 template <typename T, std::size_t... N>
init_object_number_array(std::index_sequence<N...>)558 constexpr std::array<T, sizeof...(N)> init_object_number_array(std::index_sequence<N...>)
559 {
560 	return {{((void)N, object_none)...}};
561 }
562 
563 template <typename T, std::size_t N>
564 struct object_number_array : std::array<T, N>
565 {
object_number_arrayobject_number_array566 	constexpr object_number_array() :
567 		std::array<T, N>(init_object_number_array<T>(std::make_index_sequence<N>()))
568 	{
569 	}
570 };
571 
572 }
573 
574 namespace dcx {
575 
576 unsigned laser_parent_is_matching_signature(const laser_parent &l, const object_base &o);
577 
578 struct d_level_unique_control_center_state
579 {
580 	uint8_t Control_center_destroyed;
581 	uint8_t Control_center_been_hit;
582 	uint8_t Control_center_present;
583 	player_visibility_state Control_center_player_been_seen;
584 	objnum_t Dead_controlcen_object_num;
585 	int Countdown_seconds_left;
586 	fix Countdown_timer;
587 	fix Frametime_until_next_fire;
588 	/* If the player is not dead, this stays 0.  If the player is dead,
589 	 * this accumulates FrameTime, up until it saturates.  When it
590 	 * saturates, the reactor stops firing.
591 	 *
592 	 * FIXME: The original game defined this in terms of "the player",
593 	 * but reactors are present in multiplayer games.
594 	 */
595 	fix Frametime_since_player_died;
596 	int Total_countdown_time;		//in whole seconds
597 };
598 
599 struct d_level_unique_boss_state
600 {
601 	fix64 Last_gate_time;
602 	fix64 Boss_cloak_start_time;
603 	fix64 Last_teleport_time;
604 	fix64 Boss_dying_start_time;
605 	int8_t Boss_hit_this_frame;
606 	int8_t Boss_dying;
607 	int8_t Boss_dying_sound_playing;
608 };
609 
610 }
611 
612 #define Highest_object_index (Objects.get_count() - 1)
613 
614 namespace dsx {
615 
616 // initialize a new object.  adds to the list for the given segment
617 // returns the object number
618 imobjptridx_t obj_create(object_type_t type, unsigned id, vmsegptridx_t segnum, const vms_vector &pos, const vms_matrix *orient, fix size, enum object::control_type ctype, enum object::movement_type mtype, render_type_t rtype);
619 
620 #if defined(DXX_BUILD_DESCENT_II)
621 
622 /* game_marker_index values are assigned as the object.id field for
623  * marker objects, and are used as an index into most marker-related
624  * arrays.  These values are scoped to a single level.  Each player
625  * marker has a unique value for game_marker_index, computed based on
626  * the player's ID and the player_marker_index active for that player
627  * when the marker was created.
628  */
629 enum class game_marker_index : uint8_t
630 {
631 	GuidebotDeathSite = 10,
632 	None = UINT8_MAX
633 };
634 
635 /* player_marker_index are per-player marker indexes.  Each player has
636  * their own private number space.  Valid values range from [0,
637  * maxdrop), where maxdrop depends on the game type (single player,
638  * competitive multiplayer, or cooperative multiplayer).
639  */
640 enum class player_marker_index : uint8_t
641 {
642 	_0,
643 	None = UINT8_MAX
644 };
645 
646 static inline game_marker_index &operator++(game_marker_index &i)
647 {
648 	auto u = static_cast<unsigned>(i);
649 	++ u;
650 	i = static_cast<game_marker_index>(u);
651 	return i;
652 }
653 
654 static inline player_marker_index &operator++(player_marker_index &i)
655 {
656 	auto u = static_cast<unsigned>(i);
657 	++ u;
658 	i = static_cast<player_marker_index>(u);
659 	return i;
660 }
661 
662 struct d_unique_buddy_state
663 {
664 	enum class Escort_goal_reachability : uint8_t
665 	{
666 		unreachable,
667 		reachable,
668 	};
669 	icobjidx_t Buddy_objnum = object_none;
670 	icobjidx_t Escort_goal_objidx = object_none;
671 	Escort_goal_reachability Escort_goal_reachable = Escort_goal_reachability::unreachable;
672 	uint8_t Buddy_allowed_to_talk;
673 	uint8_t Buddy_messages_suppressed;
674 	uint8_t Buddy_gave_hint_count;
675 	game_marker_index Looking_for_marker;
676 	int Last_buddy_key;
677 	int Last_buddy_polish_path_tick;
678 	escort_goal_t Escort_goal_object;
679 	escort_goal_t Escort_special_goal;
680 	fix64 Buddy_sorry_time;
681 	fix64 Buddy_last_seen_player;
682 	fix64 Buddy_last_missile_time;
683 	fix64 Last_time_buddy_gave_hint;
684 	fix64 Last_come_back_message_time;
685 	fix64 Escort_last_path_created;
686 	fix64 Buddy_last_player_path_created;
687 	fix64 Last_buddy_message_time;
688 };
689 
690 struct d_level_unique_control_center_state :
691 	::dcx::d_level_unique_control_center_state
692 {
693 	fix64 Last_time_cc_vis_check;
694 };
695 
696 class d_guided_missile_indices : object_number_array<imobjidx_t, MAX_PLAYERS>
697 {
698 	template <typename R, typename F>
699 		R get_player_active_guided_missile_tmpl(F &fvcobj, unsigned pnum) const;
700 	static bool debug_check_current_object(const object_base &);
701 public:
702 	imobjidx_t get_player_active_guided_missile(unsigned pnum) const;
703 	imobjptr_t get_player_active_guided_missile(fvmobjptr &vmobjptr, unsigned pnum) const;
704 	imobjptridx_t get_player_active_guided_missile(fvmobjptridx &vmobjptridx, unsigned pnum) const;
705 	void set_player_active_guided_missile(vmobjidx_t, unsigned pnum);
706 	void clear_player_active_guided_missile(unsigned pnum);
707 };
708 
709 struct d_level_unique_boss_state : ::dcx::d_level_unique_boss_state
710 {
711 	fix64 Boss_hit_time;
712 };
713 
714 const player &get_player_controlling_guidebot(const d_unique_buddy_state & /* reserved for future use */, const valptridx<player>::array_managed_type &Players);
715 #endif
716 
717 unsigned laser_parent_is_player(fvcobjptr &, const laser_parent &, const object_base &);
718 unsigned laser_parent_is_object(fvcobjptr &, const laser_parent &, const object_base &);
719 unsigned laser_parent_is_object(const laser_parent &, vcobjptridx_t);
720 unsigned laser_parent_object_exists(fvcobjptr &, const laser_parent &);
721 
get_powerup_id(const object_base & o)722 static inline powerup_type_t get_powerup_id(const object_base &o)
723 {
724 	return static_cast<powerup_type_t>(o.id);
725 }
726 
get_weapon_id(const object_base & o)727 static inline weapon_id_type get_weapon_id(const object_base &o)
728 {
729 	return static_cast<weapon_id_type>(o.id);
730 }
731 
732 #if defined(DXX_BUILD_DESCENT_II)
get_marker_id(const object_base & o)733 static inline game_marker_index get_marker_id(const object_base &o)
734 {
735 	return game_marker_index{o.id};
736 }
737 #endif
738 
739 void set_powerup_id(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip, object_base &o, powerup_type_t id);
740 
set_weapon_id(object_base & o,weapon_id_type id)741 static inline void set_weapon_id(object_base &o, weapon_id_type id)
742 {
743 	o.id = static_cast<uint8_t>(id);
744 }
745 
746 }
747 
748 namespace dcx {
749 
get_player_id(const object_base & o)750 static inline unsigned get_player_id(const object_base &o)
751 {
752 	return o.id;
753 }
754 
get_reactor_id(const object_base & o)755 static inline uint8_t get_reactor_id(const object_base &o)
756 {
757 	return o.id;
758 }
759 
get_fireball_id(const object_base & o)760 static inline uint8_t get_fireball_id(const object_base &o)
761 {
762 	return o.id;
763 }
764 
get_robot_id(const object_base & o)765 static inline uint8_t get_robot_id(const object_base &o)
766 {
767 	return o.id;
768 }
769 
set_player_id(object_base & o,const uint8_t id)770 static inline void set_player_id(object_base &o, const uint8_t id)
771 {
772 	o.id = id;
773 }
774 
set_reactor_id(object_base & o,const uint8_t id)775 static inline void set_reactor_id(object_base &o, const uint8_t id)
776 {
777 	o.id = id;
778 }
779 
set_robot_id(object_base & o,const uint8_t id)780 static inline void set_robot_id(object_base &o, const uint8_t id)
781 {
782 	o.id = id;
783 }
784 
785 void check_warn_object_type(const object_base &, object_type_t, const char *file, unsigned line);
786 #define get_player_id(O)	(check_warn_object_type(O, OBJ_PLAYER, __FILE__, __LINE__), get_player_id(O))
787 #define get_powerup_id(O)	(check_warn_object_type(O, OBJ_POWERUP, __FILE__, __LINE__), get_powerup_id(O))
788 #define get_reactor_id(O)	(check_warn_object_type(O, OBJ_CNTRLCEN, __FILE__, __LINE__), get_reactor_id(O))
789 #define get_ghost_id(O)	(check_warn_object_type(O, OBJ_GHOST, __FILE__, __LINE__), (get_player_id)(O))
790 #define get_fireball_id(O)	(check_warn_object_type(O, OBJ_FIREBALL, __FILE__, __LINE__), get_fireball_id(O))
791 #define get_robot_id(O)	(check_warn_object_type(O, OBJ_ROBOT, __FILE__, __LINE__), get_robot_id(O))
792 #define get_weapon_id(O)	(check_warn_object_type(O, OBJ_WEAPON, __FILE__, __LINE__), get_weapon_id(O))
793 #if defined(DXX_BUILD_DESCENT_II)
794 #define get_marker_id(O)	(check_warn_object_type(O, OBJ_MARKER, __FILE__, __LINE__), get_marker_id(O))
795 #endif
796 #define set_player_id(O,I)	(check_warn_object_type(O, OBJ_PLAYER, __FILE__, __LINE__), set_player_id(O, I))
797 #define set_reactor_id(O,I)	(check_warn_object_type(O, OBJ_CNTRLCEN, __FILE__, __LINE__), set_reactor_id(O, I))
798 #define set_robot_id(O,I)	(check_warn_object_type(O, OBJ_ROBOT, __FILE__, __LINE__), set_robot_id(O, I))
799 #define set_weapon_id(O,I)	(check_warn_object_type(O, OBJ_WEAPON, __FILE__, __LINE__), set_weapon_id(O, I))
800 #ifdef DXX_CONSTANT_TRUE
801 #define check_warn_object_type(O,T,F,L)	\
802 	( DXX_BEGIN_COMPOUND_STATEMENT {	\
803 		const object_base &dxx_check_warn_o = (O);	\
804 		const auto dxx_check_warn_actual_type = dxx_check_warn_o.type;	\
805 		const auto dxx_check_warn_expected_type = (T);	\
806 		/* If the type is always right, omit the runtime check. */	\
807 		DXX_CONSTANT_TRUE(dxx_check_warn_actual_type == dxx_check_warn_expected_type) || (	\
808 			/* If the type is always wrong, force a compile-time error. */	\
809 			DXX_CONSTANT_TRUE(dxx_check_warn_actual_type != dxx_check_warn_expected_type)	\
810 			? DXX_ALWAYS_ERROR_FUNCTION(dxx_error_object_type_mismatch, "object type mismatch")	\
811 			: (	\
812 				check_warn_object_type(dxx_check_warn_o, dxx_check_warn_expected_type, F, L)	\
813 			)	\
814 		, 0);	\
815 	} DXX_END_COMPOUND_STATEMENT )
816 #endif
817 
818 }
819 
820 #endif
821