Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

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