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 rendering
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <cstdlib>
  28. #include <stdio.h>
  29.  
  30. #include "inferno.h"
  31. #include "game.h"
  32. #include "gr.h"
  33. #include "bm.h"
  34. #include "3d.h"
  35. #include "segment.h"
  36. #include "texmap.h"
  37. #include "laser.h"
  38. #include "key.h"
  39. #include "gameseg.h"
  40. #include "textures.h"
  41. #include "object.h"
  42. #include "controls.h"
  43. #include "physics.h"
  44. #include "slew.h"
  45. #include "render.h"
  46. #include "wall.h"
  47. #include "vclip.h"
  48. #include "robot.h"
  49. #include "interp.h"
  50. #include "fireball.h"
  51. #include "laser.h"
  52. #include "dxxerror.h"
  53. #include "ai.h"
  54. #include "hostage.h"
  55. #include "morph.h"
  56. #include "cntrlcen.h"
  57. #include "powerup.h"
  58. #include "fuelcen.h"
  59. #include "endlevel.h"
  60. #include "hudmsg.h"
  61. #include "sounds.h"
  62. #include "collide.h"
  63. #include "lighting.h"
  64. #include "newdemo.h"
  65. #include "player.h"
  66. #include "weapon.h"
  67. #include "newmenu.h"
  68. #include "gauges.h"
  69. #include "multi.h"
  70. #include "menu.h"
  71. #include "args.h"
  72. #include "text.h"
  73. #include "piggy.h"
  74. #include "switch.h"
  75. #include "gameseq.h"
  76. #include "playsave.h"
  77. #include "timer.h"
  78. #if DXX_USE_EDITOR
  79. #include "editor/editor.h"
  80. #endif
  81.  
  82. #include "compiler-range_for.h"
  83. #include "d_range.h"
  84. #include "partial_range.h"
  85. #include <utility>
  86.  
  87. using std::min;
  88. using std::max;
  89.  
  90. namespace dsx {
  91. static void obj_detach_all(object_array &Objects, object_base &parent);
  92. static void obj_detach_one(object_array &Objects, object &sub);
  93.  
  94. static int is_proximity_bomb_or_any_smart_mine(const weapon_id_type id)
  95. {
  96.         const auto r = is_proximity_bomb_or_player_smart_mine(id);
  97. #if defined(DXX_BUILD_DESCENT_II)
  98.         if (r)
  99.                 return r;
  100.         // superprox dropped by robots have their own ID not considered by is_proximity_bomb_or_player_smart_mine() and since that function is used in many other places, I didn't feel safe to add this weapon type in it
  101.         if (id == weapon_id_type::ROBOT_SUPERPROX_ID)
  102.                 return 1;
  103. #endif
  104.         return r;
  105. }
  106.  
  107. /*
  108.  *  Global variables
  109.  */
  110.  
  111. object *ConsoleObject;                                  //the object that is the player
  112. }
  113.  
  114. namespace dcx {
  115.  
  116. //Data for objects
  117.  
  118. // -- Object stuff
  119.  
  120. //info on the various types of objects
  121. }
  122.  
  123. namespace dsx {
  124. #ifndef RELEASE
  125. //set viewer object to next object in array
  126. void object_goto_next_viewer()
  127. {
  128.         auto &Objects = LevelUniqueObjectState.Objects;
  129.         auto &vcobjptr = Objects.vcptr;
  130.         auto &vcobjptridx = Objects.vcptridx;
  131.         auto &vmobjptr = Objects.vmptr;
  132.         objnum_t start_obj;
  133.         start_obj = vcobjptridx(Viewer);                //get viewer object number
  134.        
  135.         range_for (const auto &&i, vcobjptr)
  136.         {
  137.                 (void)i;
  138.                 start_obj++;
  139.                 if (start_obj > Highest_object_index ) start_obj = 0;
  140.  
  141.                 auto &objp = *vmobjptr(start_obj);
  142.                 if (objp.type != OBJ_NONE)
  143.                 {
  144.                         Viewer = &objp;
  145.                         return;
  146.                 }
  147.         }
  148.  
  149.         Error( "Could not find a viewer object!" );
  150.  
  151. }
  152. #endif
  153.  
  154. imobjptridx_t obj_find_first_of_type(fvmobjptridx &vmobjptridx, const object_type_t type)
  155. {
  156.         range_for (const auto &&i, vmobjptridx)
  157.         {
  158.                 if (i->type==type)
  159.                         return i;
  160.         }
  161.         return object_none;
  162. }
  163.  
  164. }
  165.  
  166. namespace dcx {
  167.  
  168. icobjidx_t laser_info::get_last_hitobj() const
  169. {
  170.         if (!hitobj_count)
  171.                 /* If no elements, return object_none */
  172.                 return object_none;
  173.         /* Return the most recently written element.  `hitobj_pos`
  174.          * indicates the element to write next, so return
  175.          * hitobj_values[hitobj_pos - 1].  When hitobj_pos == 0, the
  176.          * most recently written element is at the end of the array, not
  177.          * before the beginning of the array.
  178.          */
  179.         if (!hitobj_pos)
  180.                 return hitobj_values.back();
  181.         return hitobj_values[hitobj_pos - 1];
  182. }
  183.  
  184. //draw an object that has one bitmap & doesn't rotate
  185. void draw_object_blob(grs_canvas &canvas, const object_base &obj, const bitmap_index bmi)
  186. {
  187.         auto &bm = GameBitmaps[bmi.index];
  188.         PIGGY_PAGE_IN( bmi );
  189.  
  190.         const auto osize = obj.size;
  191.         // draw these with slight offset to viewer preventing too much ugly clipping
  192.         auto pos = obj.pos;
  193.         if (obj.type == OBJ_FIREBALL && get_fireball_id(obj) == VCLIP_VOLATILE_WALL_HIT)
  194.         {
  195.                 vms_vector offs_vec;
  196.                 vm_vec_normalized_dir_quick(offs_vec, Viewer->pos, pos);
  197.                 vm_vec_scale_add2(pos,offs_vec,F1_0);
  198.         }
  199.  
  200.         using wh = std::pair<fix, fix>;
  201.         const auto bm_w = bm.bm_w;
  202.         const auto bm_h = bm.bm_h;
  203.         const auto p = (bm_w > bm_h)
  204.                 ? wh(osize, fixmuldiv(osize, bm_h, bm_w))
  205.                 : wh(fixmuldiv(osize, bm_w, bm_h), osize);
  206.         g3_draw_bitmap(canvas, pos, p.first, p.second, bm);
  207. }
  208.  
  209. }
  210.  
  211. namespace dsx {
  212.  
  213. //draw an object that is a texture-mapped rod
  214. void draw_object_tmap_rod(grs_canvas &canvas, const d_level_unique_light_state *const LevelUniqueLightState, const vcobjptridx_t obj, const bitmap_index bitmapi)
  215. {
  216.         g3s_lrgb light;
  217.         PIGGY_PAGE_IN(bitmapi);
  218.  
  219.         auto &bitmap = GameBitmaps[bitmapi.index];
  220.  
  221.         const auto delta = vm_vec_copy_scale(obj->orient.uvec,obj->size);
  222.  
  223.         const auto top_v = vm_vec_add(obj->pos,delta);
  224.         const auto bot_v = vm_vec_sub(obj->pos,delta);
  225.  
  226.         const auto top_p = g3_rotate_point(top_v);
  227.         const auto bot_p = g3_rotate_point(bot_v);
  228.  
  229.         if (LevelUniqueLightState)
  230.         {
  231.                 light = compute_object_light(*LevelUniqueLightState, obj);
  232.         }
  233.         else
  234.         {
  235.                 light.r = light.g = light.b = f1_0;
  236.         }
  237.         g3_draw_rod_tmap(canvas, bitmap, bot_p, obj->size, top_p, obj->size, light);
  238. }
  239.  
  240. //used for robot engine glow
  241. #define MAX_VELOCITY i2f(50)
  242.  
  243. //what darkening level to use when cloaked
  244. #define CLOAKED_FADE_LEVEL              28
  245.  
  246. #define CLOAK_FADEIN_DURATION_PLAYER    F2_0
  247. #define CLOAK_FADEOUT_DURATION_PLAYER   F2_0
  248.  
  249. #define CLOAK_FADEIN_DURATION_ROBOT     F1_0
  250. #define CLOAK_FADEOUT_DURATION_ROBOT    F1_0
  251.  
  252. //do special cloaked render
  253. static void draw_cloaked_object(grs_canvas &canvas, const object_base &obj, const g3s_lrgb light, glow_values_t glow, const fix64 cloak_start_time, const fix total_cloaked_time, const fix Cloak_fadein_duration, const fix Cloak_fadeout_duration)
  254. {
  255.         fix cloak_delta_time;
  256.         fix light_scale=F1_0;
  257.         int cloak_value=0;
  258.         int fading=0;           //if true, fading, else cloaking
  259.  
  260.         cloak_delta_time = GameTime64 - cloak_start_time;
  261.  
  262.         if (cloak_delta_time < Cloak_fadein_duration/2) {
  263.  
  264. #if defined(DXX_BUILD_DESCENT_I)
  265.                 light_scale = Cloak_fadein_duration/2 - cloak_delta_time;
  266. #elif defined(DXX_BUILD_DESCENT_II)
  267.                 light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
  268. #endif
  269.                 fading = 1;
  270.  
  271.         }
  272.         else if (cloak_delta_time < Cloak_fadein_duration) {
  273.  
  274. #if defined(DXX_BUILD_DESCENT_I)
  275.                 cloak_value = f2i((cloak_delta_time - Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
  276. #elif defined(DXX_BUILD_DESCENT_II)
  277.                 cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
  278. #endif
  279.  
  280.         } else if (GameTime64 < (cloak_start_time + total_cloaked_time) -Cloak_fadeout_duration) {
  281.                 static int cloak_delta=0,cloak_dir=1;
  282.                 static fix cloak_timer=0;
  283.  
  284.                 //note, if more than one cloaked object is visible at once, the
  285.                 //pulse rate will change!
  286.  
  287.                 cloak_timer -= FrameTime;
  288.                 while (cloak_timer < 0) {
  289.  
  290.                         cloak_timer += Cloak_fadeout_duration/12;
  291.  
  292.                         cloak_delta += cloak_dir;
  293.  
  294.                         if (cloak_delta==0 || cloak_delta==4)
  295.                                 cloak_dir = -cloak_dir;
  296.                 }
  297.  
  298.                 cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
  299.        
  300.         } else if (GameTime64 < (cloak_start_time + total_cloaked_time) -Cloak_fadeout_duration/2) {
  301.  
  302. #if defined(DXX_BUILD_DESCENT_I)
  303.                 cloak_value = f2i((total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time) * CLOAKED_FADE_LEVEL);
  304. #elif defined(DXX_BUILD_DESCENT_II)
  305.                 cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
  306. #endif
  307.  
  308.         } else {
  309.  
  310. #if defined(DXX_BUILD_DESCENT_I)
  311.                 light_scale = Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time);
  312. #elif defined(DXX_BUILD_DESCENT_II)
  313.                 light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
  314. #endif
  315.                 fading = 1;
  316.         }
  317.  
  318.         alternate_textures alt_textures;
  319. #if defined(DXX_BUILD_DESCENT_II)
  320.         if (fading)
  321. #endif
  322.         {
  323.                 const unsigned ati = static_cast<unsigned>(obj.rtype.pobj_info.alt_textures) - 1;
  324.                 if (ati < multi_player_textures.size())
  325.                 {
  326.                         alt_textures = multi_player_textures[ati];
  327.                 }
  328.         }
  329.  
  330.         if (fading) {
  331.                 g3s_lrgb new_light;
  332.  
  333.                 new_light.r = fixmul(light.r,light_scale);
  334.                 new_light.g = fixmul(light.g,light_scale);
  335.                 new_light.b = fixmul(light.b,light_scale);
  336.                 glow[0] = fixmul(glow[0],light_scale);
  337.                 draw_polygon_model(canvas, obj.pos,
  338.                                    obj.orient,
  339.                                    obj.rtype.pobj_info.anim_angles,
  340.                                    obj.rtype.pobj_info.model_num, obj.rtype.pobj_info.subobj_flags,
  341.                                    new_light,
  342.                                    &glow,
  343.                                    alt_textures );
  344.         }
  345.         else {
  346.                 gr_settransblend(canvas, cloak_value, gr_blend::normal);
  347.                 g3_set_special_render(draw_tmap_flat);          //use special flat drawer
  348.                 draw_polygon_model(canvas, obj.pos,
  349.                                    obj.orient,
  350.                                    obj.rtype.pobj_info.anim_angles,
  351.                                    obj.rtype.pobj_info.model_num, obj.rtype.pobj_info.subobj_flags,
  352.                                    light,
  353.                                    &glow,
  354.                                    alt_textures );
  355.                 g3_set_special_render(draw_tmap);
  356.                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
  357.         }
  358.  
  359. }
  360.  
  361. //draw an object which renders as a polygon model
  362. static void draw_polygon_object(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vcobjptridx_t obj)
  363. {
  364.         auto &BossUniqueState = LevelUniqueObjectState.BossState;
  365.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  366.         g3s_lrgb light;
  367.         glow_values_t engine_glow_value;
  368.         engine_glow_value[0] = 0;
  369. #if defined(DXX_BUILD_DESCENT_II)
  370.         engine_glow_value[1] = -1;              //element 0 is for engine glow, 1 for headlight
  371. #endif
  372.  
  373.         //      If option set for bright players in netgame, brighten them!
  374.         light = unlikely(Netgame.BrightPlayers && (Game_mode & GM_MULTI) && obj->type == OBJ_PLAYER)
  375.                 ? g3s_lrgb{F1_0 * 2, F1_0 * 2, F1_0 * 2}
  376.                 : compute_object_light(LevelUniqueLightState, obj);
  377.  
  378. #if defined(DXX_BUILD_DESCENT_II)
  379.         //make robots brighter according to robot glow field
  380.         if (obj->type == OBJ_ROBOT)
  381.         {
  382.                 const auto glow = Robot_info[get_robot_id(obj)].glow<<12;
  383.                 light.r += glow; //convert 4:4 to 16:16
  384.                 light.g += glow; //convert 4:4 to 16:16
  385.                 light.b += glow; //convert 4:4 to 16:16
  386.         }
  387.  
  388.         if ((obj->type == OBJ_WEAPON &&
  389.                         get_weapon_id(obj) == weapon_id_type::FLARE_ID) ||
  390.                 obj->type == OBJ_MARKER
  391.                 )
  392.                 {
  393.                         light.r += F1_0*2;
  394.                         light.g += F1_0*2;
  395.                         light.b += F1_0*2;
  396.                 }
  397. #endif
  398.  
  399.         push_interpolation_method imsave(1, true);
  400.  
  401.         //set engine glow value
  402.         engine_glow_value[0] = f1_0/5;
  403.         if (obj->movement_type == MT_PHYSICS) {
  404.  
  405.                 if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && get_player_id(obj)==Player_num) {
  406.                         fix thrust_mag = vm_vec_mag_quick(obj->mtype.phys_info.thrust);
  407.                         engine_glow_value[0] += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
  408.                 }
  409.                 else {
  410.                         fix speed = vm_vec_mag_quick(obj->mtype.phys_info.velocity);
  411. #if defined(DXX_BUILD_DESCENT_I)
  412.                         engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*4)/5;
  413. #elif defined(DXX_BUILD_DESCENT_II)
  414.                         engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
  415. #endif
  416.                 }
  417.         }
  418.  
  419. #if defined(DXX_BUILD_DESCENT_II)
  420.         //set value for player headlight
  421.         if (obj->type == OBJ_PLAYER) {
  422.                 auto &player_flags = obj->ctype.player_info.powerup_flags;
  423.                 if (player_flags & PLAYER_FLAGS_HEADLIGHT && !Endlevel_sequence)
  424.                         if (player_flags & PLAYER_FLAGS_HEADLIGHT_ON)
  425.                                 engine_glow_value[1] = -2;              //draw white!
  426.                         else
  427.                                 engine_glow_value[1] = -1;              //draw normal color (grey)
  428.                 else
  429.                         engine_glow_value[1] = -3;                      //don't draw
  430.         }
  431. #endif
  432.  
  433.         if (obj->rtype.pobj_info.tmap_override != -1) {
  434.                 std::array<bitmap_index, 12> bm_ptrs;
  435.  
  436.                 //fill whole array, in case simple model needs more
  437.                 bm_ptrs.fill(Textures[obj->rtype.pobj_info.tmap_override]);
  438.                 draw_polygon_model(canvas, obj->pos,
  439.                                    obj->orient,
  440.                                    obj->rtype.pobj_info.anim_angles,
  441.                                    obj->rtype.pobj_info.model_num,
  442.                                    obj->rtype.pobj_info.subobj_flags,
  443.                                    light,
  444.                                    &engine_glow_value,
  445.                                    bm_ptrs);
  446.         }
  447.         else {
  448.                 std::pair<fix64, fix> cloak_duration;
  449.                 std::pair<fix, fix> cloak_fade;
  450.                 if (obj->type==OBJ_PLAYER && (obj->ctype.player_info.powerup_flags & PLAYER_FLAGS_CLOAKED))
  451.                 {
  452.                         auto &cloak_time = obj->ctype.player_info.cloak_time;
  453.                         cloak_duration = {cloak_time, CLOAK_TIME_MAX};
  454.                         cloak_fade = {CLOAK_FADEIN_DURATION_PLAYER, CLOAK_FADEOUT_DURATION_PLAYER};
  455.                 }
  456.                 else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
  457.                         if (Robot_info[get_robot_id(obj)].boss_flag)
  458.                                 cloak_duration = {BossUniqueState.Boss_cloak_start_time, Boss_cloak_duration};
  459.                         else
  460.                                 cloak_duration = {GameTime64-F1_0*10, F1_0 * 20};
  461.                         cloak_fade = {CLOAK_FADEIN_DURATION_ROBOT, CLOAK_FADEOUT_DURATION_ROBOT};
  462.                 } else {
  463.                         alternate_textures alt_textures;
  464.                         const unsigned ati = static_cast<unsigned>(obj->rtype.pobj_info.alt_textures) - 1;
  465.                         if (ati < multi_player_textures.size())
  466.                                 alt_textures = multi_player_textures[ati];
  467.  
  468. #if defined(DXX_BUILD_DESCENT_II)
  469.                         if (obj->type == OBJ_ROBOT)
  470.                         {
  471.                         //      Snipers get bright when they fire.
  472.                                 if (obj->ctype.ai_info.ail.next_fire < F1_0/8) {
  473.                                 if (obj->ctype.ai_info.behavior == ai_behavior::AIB_SNIPE)
  474.                                 {
  475.                                         light.r = 2*light.r + F1_0;
  476.                                         light.g = 2*light.g + F1_0;
  477.                                         light.b = 2*light.b + F1_0;
  478.                                 }
  479.                         }
  480.                         }
  481. #endif
  482.  
  483.                         const auto is_weapon_with_inner_model = (obj->type == OBJ_WEAPON && Weapon_info[get_weapon_id(obj)].model_num_inner > -1);
  484.                         bool draw_simple_model;
  485.                         if (is_weapon_with_inner_model)
  486.                         {
  487.                                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::additive_a);
  488.                                 draw_simple_model = static_cast<fix>(vm_vec_dist_quick(Viewer->pos, obj->pos)) < Simple_model_threshhold_scale * F1_0*2;
  489.                                 if (draw_simple_model)
  490.                                         draw_polygon_model(canvas, obj->pos,
  491.                                                            obj->orient,
  492.                                                            obj->rtype.pobj_info.anim_angles,
  493.                                                            Weapon_info[get_weapon_id(obj)].model_num_inner,
  494.                                                            obj->rtype.pobj_info.subobj_flags,
  495.                                                            light,
  496.                                                            &engine_glow_value,
  497.                                                            alt_textures);
  498.                         }
  499.                        
  500.                         draw_polygon_model(canvas, obj->pos,
  501.                                            obj->orient,
  502.                                            obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
  503.                                            obj->rtype.pobj_info.subobj_flags,
  504.                                            light,
  505.                                            &engine_glow_value,
  506.                                            alt_textures);
  507.  
  508.                         if (is_weapon_with_inner_model)
  509.                         {
  510. #if !DXX_USE_OGL // in software rendering must draw inner model last
  511.                                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::additive_a);
  512.                                 if (draw_simple_model)
  513.                                         draw_polygon_model(canvas, obj->pos,
  514.                                                            obj->orient,
  515.                                                            obj->rtype.pobj_info.anim_angles,
  516.                                                            Weapon_info[obj->id].model_num_inner,
  517.                                                            obj->rtype.pobj_info.subobj_flags,
  518.                                                            light,
  519.                                                            &engine_glow_value,
  520.                                                            alt_textures);
  521. #endif
  522.                                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
  523.                         }
  524.                         return;
  525.                 }
  526.                 draw_cloaked_object(canvas, obj, light, engine_glow_value, cloak_duration.first, cloak_duration.second, cloak_fade.first, cloak_fade.second);
  527.         }
  528. }
  529.  
  530. }
  531.  
  532. //------------------------------------------------------------------------------
  533. // These variables are used to keep a list of the 3 closest robots to the viewer.
  534. // The code works like this: Every time render object is called with a polygon model,
  535. // it finds the distance of that robot to the viewer.  If this distance if within 10
  536. // segments of the viewer, it does the following: If there aren't already 3 robots in
  537. // the closet-robots list, it just sticks that object into the list along with its distance.
  538. // If the list already contains 3 robots, then it finds the robot in that list that is
  539. // farthest from the viewer. If that object is farther than the object currently being
  540. // rendered, then the new object takes over that far object's slot.  *Then* after all
  541. // objects are rendered, object_render_targets is called an it draws a target on top
  542. // of all the objects.
  543.  
  544. //091494: #define MAX_CLOSE_ROBOTS 3
  545. //--unused-- static int Object_draw_lock_boxes = 0;
  546. //091494: static int Object_num_close = 0;
  547. //091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
  548. //091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
  549.  
  550. //091494: set_close_objects(object *obj)
  551. //091494: {
  552. //091494:       fix dist;
  553. //091494:
  554. //091494:       if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) ) 
  555. //091494:               return;
  556. //091494:
  557. //091494:       // The following code keeps a list of the 10 closest robots to the
  558. //091494:       // viewer.  See comments in front of this function for how this works.
  559. //091494:       dist = vm_vec_dist( &obj->pos, &Viewer->pos );
  560. //091494:       if ( dist < i2f(20*10) )        {                              
  561. //091494:               if ( Object_num_close < MAX_CLOSE_ROBOTS )      {
  562. //091494:                       Object_close_ones[Object_num_close] = obj;
  563. //091494:                       Object_close_distance[Object_num_close] = dist;
  564. //091494:                       Object_num_close++;
  565. //091494:               } else {
  566. //091494:                       int i, farthest_robot;
  567. //091494:                       fix farthest_distance;
  568. //091494:                       // Find the farthest robot in the list
  569. //091494:                       farthest_robot = 0;
  570. //091494:                       farthest_distance = Object_close_distance[0];
  571. //091494:                       for (i=1; i<Object_num_close; i++ )     {
  572. //091494:                               if ( Object_close_distance[i] > farthest_distance )     {
  573. //091494:                                       farthest_distance = Object_close_distance[i];
  574. //091494:                                       farthest_robot = i;
  575. //091494:                               }
  576. //091494:                       }
  577. //091494:                       // If this object is closer to the viewer than
  578. //091494:                       // the farthest in the list, replace the farthest with this object.
  579. //091494:                       if ( farthest_distance > dist ) {
  580. //091494:                               Object_close_ones[farthest_robot] = obj;
  581. //091494:                               Object_close_distance[farthest_robot] = dist;
  582. //091494:                       }
  583. //091494:               }
  584. //091494:       }
  585. //091494: }
  586.  
  587. namespace dcx {
  588. objnum_t        Player_fired_laser_this_frame=object_none;
  589.  
  590. static bool predicate_debris(const object_base &o)
  591. {
  592.         return o.type == OBJ_DEBRIS;
  593. }
  594.  
  595. static bool predicate_flare(const object_base &o)
  596. {
  597.         return (o.type == OBJ_WEAPON) && (get_weapon_id(o) == weapon_id_type::FLARE_ID);
  598. }
  599.  
  600. static bool predicate_nonflare_weapon(const object_base &o)
  601. {
  602.         return (o.type == OBJ_WEAPON) && (get_weapon_id(o) != weapon_id_type::FLARE_ID);
  603. }
  604.  
  605. }
  606.  
  607.  
  608.  
  609. namespace dsx {
  610.  
  611. static bool predicate_fireball(const object &o)
  612. {
  613.         return o.type == OBJ_FIREBALL && o.ctype.expl_info.delete_objnum == object_none;
  614. }
  615.  
  616. // -----------------------------------------------------------------------------
  617. //this routine checks to see if an robot rendered near the middle of
  618. //the screen, and if so and the player had fired, "warns" the robot
  619. static void set_robot_location_info(object &objp)
  620. {
  621.         auto &Objects = LevelUniqueObjectState.Objects;
  622.         auto &vcobjptr = Objects.vcptr;
  623.         if (Player_fired_laser_this_frame != object_none) {
  624.                 const auto &&temp = g3_rotate_point(objp.pos);
  625.                 if (temp.p3_codes & CC_BEHIND)          //robot behind the screen
  626.                         return;
  627.  
  628.                 //the code below to check for object near the center of the screen
  629.                 //completely ignores z, which may not be good
  630.  
  631.                 if ((abs(temp.p3_x) < F1_0*4) && (abs(temp.p3_y) < F1_0*4)) {
  632.                         objp.ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
  633.                         objp.ctype.ai_info.danger_laser_signature = vcobjptr(Player_fired_laser_this_frame)->signature;
  634.                 }
  635.         }
  636. }
  637.  
  638. //      ------------------------------------------------------------------------------------------------------------------
  639. void create_small_fireball_on_object(const vmobjptridx_t objp, fix size_scale, int sound_flag)
  640. {
  641.         auto &Objects = LevelUniqueObjectState.Objects;
  642.         fix                     size;
  643.         vms_vector      pos;
  644.  
  645.         pos = objp->pos;
  646.         auto rand_vec = make_random_vector();
  647.  
  648.         vm_vec_scale(rand_vec, objp->size/2);
  649.  
  650.         vm_vec_add2(pos, rand_vec);
  651.  
  652. #if defined(DXX_BUILD_DESCENT_I)
  653.         size = fixmul(size_scale, F1_0 + d_rand()*4);
  654. #elif defined(DXX_BUILD_DESCENT_II)
  655.         size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
  656. #endif
  657.  
  658.         const auto &&segnum = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, pos, Segments.vmptridx(objp->segnum));
  659.         if (segnum != segment_none) {
  660.                 auto expl_obj = object_create_explosion(segnum, pos, size, VCLIP_SMALL_EXPLOSION);
  661.                 if (!expl_obj)
  662.                         return;
  663.                 obj_attach(Objects, objp, expl_obj);
  664.                 if (d_rand() < 8192) {
  665.                         fix     vol = F1_0/2;
  666.                         if (objp->type == OBJ_ROBOT)
  667.                                 vol *= 2;
  668.                         if (sound_flag)
  669.                                 digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp, 0, vol, sound_stack::allow_stacking);
  670.                 }
  671.         }
  672. }
  673.  
  674. // -- mk, 02/05/95 -- #define   VCLIP_INVULNERABILITY_EFFECT    VCLIP_SMALL_EXPLOSION
  675. // -- mk, 02/05/95 --
  676. // -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
  677. // -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
  678. // -- mk, 02/05/95 -- {
  679. // -- mk, 02/05/95 --   if (d_rand() < FrameTime*8) {
  680. // -- mk, 02/05/95 --           create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
  681. // -- mk, 02/05/95 --   }
  682. // -- mk, 02/05/95 -- }
  683.  
  684. // -----------------------------------------------------------------------------
  685. //      Render an object.  Calls one of several routines based on type
  686. void render_object(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj)
  687. {
  688.         if (unlikely(obj == Viewer))
  689.                 return;
  690.         if (unlikely(obj->type==OBJ_NONE))
  691.         {
  692.                 Int3();
  693.                 return;
  694.         }
  695.  
  696. #if !DXX_USE_OGL
  697.         const auto mld_save = std::exchange(Max_linear_depth, Max_linear_depth_objects);
  698. #endif
  699.  
  700.         bool alpha = false;
  701.         switch (obj->render_type)
  702.         {
  703.                 case RT_NONE:
  704.                         break; //doesn't render, like the player
  705.  
  706.                 case RT_POLYOBJ:
  707. #if defined(DXX_BUILD_DESCENT_II)
  708.                         if ( PlayerCfg.AlphaBlendMarkers && obj->type == OBJ_MARKER ) // set nice transparency/blending for certrain objects
  709.                         {
  710.                                 alpha = true;
  711.                                 gr_settransblend(canvas, 10, gr_blend::additive_a);
  712.                         }
  713. #endif
  714.                         draw_polygon_object(canvas, LevelUniqueLightState, obj);
  715.  
  716.                         if (obj->type == OBJ_ROBOT) //"warn" robot if being shot at
  717.                                 set_robot_location_info(obj);
  718.                         break;
  719.  
  720.                 case RT_MORPH:
  721.                         draw_morph_object(canvas, LevelUniqueLightState, obj);
  722.                         break;
  723.  
  724.                 case RT_FIREBALL:
  725.                         if (PlayerCfg.AlphaBlendFireballs) // set nice transparency/blending for certrain objects
  726.                         {
  727.                                 alpha = true;
  728.                                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::additive_c);
  729.                         }
  730.  
  731.                         draw_fireball(Vclip, canvas, obj);
  732.                         break;
  733.  
  734.                 case RT_WEAPON_VCLIP:
  735.                         if (PlayerCfg.AlphaBlendWeapons && (!is_proximity_bomb_or_any_smart_mine(get_weapon_id(obj))
  736.                 )) // set nice transparency/blending for certain objects
  737.                         {
  738.                                 alpha = true;
  739.                                 gr_settransblend(canvas, 7, gr_blend::additive_a);
  740.                         }
  741.  
  742.                         draw_weapon_vclip(Vclip, Weapon_info, canvas, obj);
  743.                         break;
  744.  
  745.                 case RT_HOSTAGE:
  746.                         draw_hostage(Vclip, canvas, LevelUniqueLightState, obj);
  747.                         break;
  748.  
  749.                 case RT_POWERUP:
  750.                         if (PlayerCfg.AlphaBlendPowerups) // set nice transparency/blending for certrain objects
  751.                                 switch ( get_powerup_id(obj) )
  752.                                 {
  753.                                         case POW_EXTRA_LIFE:
  754.                                         case POW_ENERGY:
  755.                                         case POW_SHIELD_BOOST:
  756.                                         case POW_CLOAK:
  757.                                         case POW_INVULNERABILITY:
  758. #if defined(DXX_BUILD_DESCENT_II)
  759.                                         case POW_HOARD_ORB:
  760. #endif
  761.                                                 alpha = true;
  762.                                                 gr_settransblend(canvas, 7, gr_blend::additive_a);
  763.                                                 break;
  764.                                         case POW_LASER:
  765.                                         case POW_KEY_BLUE:
  766.                                         case POW_KEY_RED:
  767.                                         case POW_KEY_GOLD:
  768.                                         case POW_MISSILE_1:
  769.                                         case POW_MISSILE_4:
  770.                                         case POW_QUAD_FIRE:
  771.                                         case POW_VULCAN_WEAPON:
  772.                                         case POW_SPREADFIRE_WEAPON:
  773.                                         case POW_PLASMA_WEAPON:
  774.                                         case POW_FUSION_WEAPON:
  775.                                         case POW_PROXIMITY_WEAPON:
  776.                                         case POW_HOMING_AMMO_1:
  777.                                         case POW_HOMING_AMMO_4:
  778.                                         case POW_SMARTBOMB_WEAPON:
  779.                                         case POW_MEGA_WEAPON:
  780.                                         case POW_VULCAN_AMMO:
  781.                                         case POW_TURBO:
  782.                                         case POW_MEGAWOW:
  783. #if defined(DXX_BUILD_DESCENT_II)
  784.                                         case POW_FULL_MAP:
  785.                                         case POW_HEADLIGHT:
  786.                                         case POW_GAUSS_WEAPON:
  787.                                         case POW_HELIX_WEAPON:
  788.                                         case POW_PHOENIX_WEAPON:
  789.                                         case POW_OMEGA_WEAPON:
  790.                                         case POW_SUPER_LASER:
  791.                                         case POW_CONVERTER:
  792.                                         case POW_AMMO_RACK:
  793.                                         case POW_AFTERBURNER:
  794.                                         case POW_SMISSILE1_1:
  795.                                         case POW_SMISSILE1_4:
  796.                                         case POW_GUIDED_MISSILE_1:
  797.                                         case POW_GUIDED_MISSILE_4:
  798.                                         case POW_SMART_MINE:
  799.                                         case POW_MERCURY_MISSILE_1:
  800.                                         case POW_MERCURY_MISSILE_4:
  801.                                         case POW_EARTHSHAKER_MISSILE:
  802.                                         case POW_FLAG_BLUE:
  803.                                         case POW_FLAG_RED:
  804. #endif
  805.                                                 break;
  806.                                 }
  807.  
  808.                         draw_powerup(Vclip, canvas, obj);
  809.                         break;
  810.  
  811.                 case RT_LASER:
  812.                         if (PlayerCfg.AlphaBlendLasers) // set nice transparency/blending for certrain objects
  813.                         {
  814.                                 alpha = true;
  815.                                 gr_settransblend(canvas, 7, gr_blend::additive_a);
  816.                         }
  817.  
  818.                         Laser_render(canvas, obj);
  819.                         break;
  820.  
  821.                 default:
  822.                         Error("Unknown render_type <%d>",obj->render_type);
  823.         }
  824.  
  825.         if (alpha)
  826.                 gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal); // revert any transparency/blending setting back to normal
  827.  
  828.         if ( obj->render_type != RT_NONE && Newdemo_state == ND_STATE_RECORDING )
  829.                 newdemo_record_render_object(obj);
  830. #if !DXX_USE_OGL
  831.         Max_linear_depth = mld_save;
  832. #endif
  833. }
  834.  
  835. void reset_player_object()
  836. {
  837.         //Init physics
  838.  
  839.         vm_vec_zero(ConsoleObject->mtype.phys_info.velocity);
  840.         vm_vec_zero(ConsoleObject->mtype.phys_info.thrust);
  841.         vm_vec_zero(ConsoleObject->mtype.phys_info.rotvel);
  842.         vm_vec_zero(ConsoleObject->mtype.phys_info.rotthrust);
  843.         ConsoleObject->mtype.phys_info.turnroll = 0;
  844.         ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
  845.         ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
  846.         ConsoleObject->mtype.phys_info.flags = PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
  847.  
  848.         //Init render info
  849.  
  850.         ConsoleObject->render_type = RT_POLYOBJ;
  851.         ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num;              //what model is this?
  852.         ConsoleObject->rtype.pobj_info.subobj_flags = 0;                //zero the flags
  853.         ConsoleObject->rtype.pobj_info.tmap_override = -1;              //no tmap override!
  854.         ConsoleObject->rtype.pobj_info.anim_angles = {};
  855.  
  856.         // Clear misc
  857.  
  858.         ConsoleObject->flags = 0;
  859. }
  860.  
  861.  
  862. //make object0 the player, setting all relevant fields
  863. void init_player_object()
  864. {
  865.         auto &Objects = LevelUniqueObjectState.Objects;
  866.         auto &vmobjptr = Objects.vmptr;
  867.         const auto &&console = vmobjptr(ConsoleObject);
  868.         console->type = OBJ_PLAYER;
  869.         set_player_id(console, 0);                                      //no sub-types for player
  870.         console->signature = object_signature_t{0};
  871.         auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  872.         console->size = Polygon_models[Player_ship->model_num].rad;
  873.         console->control_type = CT_SLEW;                        //default is player slewing
  874.         console->movement_type = MT_PHYSICS;            //change this sometime
  875.         console->lifeleft = IMMORTAL_TIME;
  876.         console->attached_obj = object_none;
  877.         reset_player_object();
  878. }
  879.  
  880. //sets up the free list & init player & whatever else
  881. void init_objects()
  882. {
  883.         auto &Objects = LevelUniqueObjectState.Objects;
  884.         auto &vmobjptr = Objects.vmptr;
  885.         for (objnum_t i = 0; i< MAX_OBJECTS; ++i)
  886.         {
  887.                 LevelUniqueObjectState.free_obj_list[i] = i;
  888.                 auto &obj = *vmobjptr(i);
  889.                 DXX_POISON_VAR(obj, 0xfd);
  890.                 obj.type = OBJ_NONE;
  891.         }
  892.  
  893.         range_for (unique_segment &j, Segments)
  894.                 j.objects = object_none;
  895.  
  896.         Viewer = ConsoleObject = &Objects.front();
  897.  
  898.         init_player_object();
  899.         obj_link_unchecked(Objects.vmptr, Objects.vmptridx(ConsoleObject), Segments.vmptridx(segment_first));   //put in the world in segment 0
  900.         LevelUniqueObjectState.num_objects = 1;                                         //just the player
  901.         Objects.set_count(1);
  902. }
  903.  
  904. //after calling init_object(), the network code has grabbed specific
  905. //object slots without allocating them.  Go though the objects & build
  906. //the free list, then set the apporpriate globals
  907. void special_reset_objects(d_level_unique_object_state &LevelUniqueObjectState)
  908. {
  909.         unsigned num_objects = MAX_OBJECTS;
  910.  
  911.         auto &Objects = LevelUniqueObjectState.get_objects();
  912.         Objects.set_count(1);
  913.         assert(Objects.front().type != OBJ_NONE);               //0 should be used
  914.  
  915.         DXX_POISON_VAR(LevelUniqueObjectState.free_obj_list, 0xfd);
  916.         for (objnum_t i = MAX_OBJECTS; i--;)
  917.                 if (Objects.vcptr(i)->type == OBJ_NONE)
  918.                         LevelUniqueObjectState.free_obj_list[--num_objects] = i;
  919.                 else
  920.                         if (i > Highest_object_index)
  921.                                 Objects.set_count(i + 1);
  922.         LevelUniqueObjectState.num_objects = num_objects;
  923. }
  924.  
  925. //link the object into the list for its segment
  926. void obj_link(fvmobjptr &vmobjptr, const vmobjptridx_t obj, const vmsegptridx_t segnum)
  927. {
  928.         assert(obj->segnum == segment_none);
  929.         assert(obj->next == object_none);
  930.         assert(obj->prev == object_none);
  931.         obj_link_unchecked(vmobjptr, obj, segnum);
  932. }
  933.  
  934. void obj_link_unchecked(fvmobjptr &vmobjptr, const vmobjptridx_t obj, const vmsegptridx_t segnum)
  935. {
  936.         obj->segnum = segnum;
  937.        
  938.         obj->next = segnum->objects;
  939.         obj->prev = object_none;
  940.  
  941.         segnum->objects = obj;
  942.  
  943.         if (obj->next != object_none)
  944.                 vmobjptr(obj->next)->prev = obj;
  945. }
  946.  
  947. void obj_unlink(fvmobjptr &vmobjptr, fvmsegptr &vmsegptr, object_base &obj)
  948. {
  949.         const auto next = obj.next;
  950.         /* It is a bug elsewhere if vmsegptr ever fails here.  However, it is
  951.          * expensive to check, so only force verification in debug builds.
  952.          *
  953.          * In debug builds, always compute it, for the side effect of
  954.          * validating the segment number.
  955.          *
  956.          * In release builds, compute it when it is needed.
  957.          */
  958. #ifndef NDEBUG
  959.         const auto &&segp = vmsegptr(obj.segnum);
  960. #endif
  961.         ((obj.prev == object_none)
  962.                 ? (
  963. #ifdef NDEBUG
  964.                         vmsegptr(obj.segnum)
  965. #else
  966.                         segp
  967. #endif
  968.                 )->objects
  969.                 : vmobjptr(obj.prev)->next) = next;
  970.  
  971.         obj.segnum = segment_none;
  972.  
  973.         if (next != object_none)
  974.                 vmobjptr(next)->prev = obj.prev;
  975.         DXX_POISON_VAR(obj.next, 0xfa);
  976.         DXX_POISON_VAR(obj.prev, 0xfa);
  977. }
  978.  
  979. //returns the number of a free object, updating Highest_object_index.
  980. //Generally, obj_create() should be called to get an object, since it
  981. //fills in important fields and does the linking.
  982. //returns -1 if no free objects
  983. imobjptridx_t obj_allocate(d_level_unique_object_state &LevelUniqueObjectState)
  984. {
  985.         auto &Objects = LevelUniqueObjectState.Objects;
  986.         if (LevelUniqueObjectState.num_objects >= Objects.size())
  987.                 return object_none;
  988.  
  989.         const auto objnum = LevelUniqueObjectState.free_obj_list[LevelUniqueObjectState.num_objects++];
  990.         if (objnum >= Objects.get_count())
  991.         {
  992.                 Objects.set_count(objnum + 1);
  993.         }
  994.         const auto &&r = Objects.vmptridx(objnum);
  995.         assert(r->type == OBJ_NONE);
  996.         return r;
  997. }
  998.  
  999. //frees up an object.  Generally, obj_delete() should be called to get
  1000. //rid of an object.  This function deallocates the object entry after
  1001. //the object has been unlinked
  1002. static void obj_free(d_level_unique_object_state &LevelUniqueObjectState, const vmobjidx_t objnum)
  1003. {
  1004.         const auto num_objects = -- LevelUniqueObjectState.num_objects;
  1005.         assert(num_objects < LevelUniqueObjectState.free_obj_list.size());
  1006.         LevelUniqueObjectState.free_obj_list[num_objects] = objnum;
  1007.         auto &Objects = LevelUniqueObjectState.get_objects();
  1008.  
  1009.         objnum_t o = objnum;
  1010.         if (o == Highest_object_index)
  1011.         {
  1012.                 for (;;)
  1013.                 {
  1014.                         --o;
  1015.                         if (Objects.vcptr(o)->type != OBJ_NONE)
  1016.                                 break;
  1017.                         if (o == 0)
  1018.                                 break;
  1019.                 }
  1020.                 Objects.set_count(o + 1);
  1021.         }
  1022. }
  1023.  
  1024. //-----------------------------------------------------------------------------
  1025. //      Scan the object list, freeing down to num_used objects
  1026. //      Returns number of slots freed.
  1027. static void free_object_slots(uint_fast32_t num_used)
  1028. {
  1029.         auto &Objects = LevelUniqueObjectState.Objects;
  1030.         auto &vmobjptr = Objects.vmptr;
  1031.         std::array<object *, MAX_OBJECTS>       obj_list;
  1032.         unsigned        num_already_free, num_to_free, olind = 0;
  1033.  
  1034.         num_already_free = MAX_OBJECTS - Highest_object_index - 1;
  1035.  
  1036.         if (MAX_OBJECTS - num_already_free < num_used)
  1037.                 return;
  1038.  
  1039.         range_for (const auto &&objp, vmobjptr)
  1040.         {
  1041.                 if (objp->flags & OF_SHOULD_BE_DEAD)
  1042.                 {
  1043.                         num_already_free++;
  1044.                         if (MAX_OBJECTS - num_already_free < num_used)
  1045.                                 return;
  1046.                 } else
  1047.                         switch (objp->type)
  1048.                         {
  1049.                                 case OBJ_NONE:
  1050.                                         num_already_free++;
  1051.                                         if (MAX_OBJECTS - num_already_free < num_used)
  1052.                                                 return;
  1053.                                         break;
  1054.                                 case OBJ_WALL:
  1055.                                         Int3();         //      This is curious.  What is an object that is a wall?
  1056.                                         break;
  1057.                                 case OBJ_FIREBALL:
  1058.                                 case OBJ_WEAPON:
  1059.                                 case OBJ_DEBRIS:
  1060.                                         obj_list[olind++] = objp;
  1061.                                         break;
  1062.                                 case OBJ_ROBOT:
  1063.                                 case OBJ_HOSTAGE:
  1064.                                 case OBJ_PLAYER:
  1065.                                 case OBJ_CNTRLCEN:
  1066.                                 case OBJ_CLUTTER:
  1067.                                 case OBJ_GHOST:
  1068.                                 case OBJ_LIGHT:
  1069.                                 case OBJ_CAMERA:
  1070.                                 case OBJ_POWERUP:
  1071.                                 case OBJ_COOP:
  1072.                                 case OBJ_MARKER:
  1073.                                         break;
  1074.                         }
  1075.  
  1076.         }
  1077.  
  1078.         num_to_free = MAX_OBJECTS - num_used - num_already_free;
  1079.  
  1080.         if (num_to_free > olind) {
  1081.                 num_to_free = olind;
  1082.         }
  1083.  
  1084.         // Capture before num_to_free modified
  1085.         const auto &&r = partial_const_range(obj_list, num_to_free);
  1086.         auto l = [&vmobjptr, &r, &num_to_free](const auto predicate) -> bool {
  1087.                 range_for (const auto i, r)
  1088.                 {
  1089.                         auto &o = *vmobjptr(i);
  1090.                         if (predicate(o))
  1091.                         {
  1092.                                 o.flags |= OF_SHOULD_BE_DEAD;
  1093.                                 if (!-- num_to_free)
  1094.                                         return true;
  1095.                         }
  1096.                 }
  1097.                 return false;
  1098.         };
  1099.  
  1100.         if (l(predicate_debris))
  1101.                 return;
  1102.  
  1103.         if (l(predicate_fireball))
  1104.                 return;
  1105.  
  1106.         if (l(predicate_flare))
  1107.                 return;
  1108.  
  1109.         if (l(predicate_nonflare_weapon))
  1110.                 return;
  1111. }
  1112.  
  1113. //-----------------------------------------------------------------------------
  1114. //initialize a new object.  adds to the list for the given segment
  1115. //note that segnum is really just a suggestion, since this routine actually
  1116. //searches for the correct segment
  1117. //returns the object number
  1118. imobjptridx_t obj_create(const object_type_t type, const unsigned id, vmsegptridx_t segnum, const vms_vector &pos, const vms_matrix *const orient, const fix size, const unsigned ctype, const movement_type_t mtype, const render_type_t rtype)
  1119. {
  1120.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1121.         auto &Objects = LevelUniqueObjectState.Objects;
  1122.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1123.         // Some consistency checking. FIXME: Add more debug output here to probably trace all possible occurances back.
  1124.         Assert(ctype <= CT_CNTRLCEN);
  1125.  
  1126.         if (type == OBJ_DEBRIS && LevelUniqueObjectState.Debris_object_count >= Max_debris_objects && !PERSISTENT_DEBRIS)
  1127.                 return object_none;
  1128.  
  1129.         auto &vcvertptr = Vertices.vcptr;
  1130.         if (get_seg_masks(vcvertptr, pos, segnum, 0).centermask != 0)
  1131.         {
  1132.                 const auto &&p = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, pos, segnum);
  1133.                 if (p == segment_none) {
  1134.                         return object_none;             //don't create this object
  1135.                 }
  1136.                 segnum = p;
  1137.         }
  1138.  
  1139.         // Find next free object
  1140.         const auto &&obj = obj_allocate(LevelUniqueObjectState);
  1141.  
  1142.         if (obj == object_none)         //no free objects
  1143.                 return object_none;
  1144.  
  1145.         // Zero out object structure to keep weird bugs from happening
  1146.         // in uninitialized fields.
  1147.         const auto signature = obj->signature;
  1148.         /* Test the version in the object structure, not the local copy.
  1149.          * This produces a more useful diagnostic from Valgrind if the
  1150.          * test reports a problem.
  1151.          */
  1152.         DXX_CHECK_VAR_IS_DEFINED(obj->signature);
  1153.         *obj = {};
  1154.         // Tell Valgrind to warn on any uninitialized fields.
  1155.         DXX_POISON_VAR(*obj, 0xfd);
  1156.  
  1157.         obj->signature = object_signature_t(signature.get() + 1);
  1158.         obj->type                               = type;
  1159.         obj->id                                 = id;
  1160.         obj->pos                                = pos;
  1161.         obj->size                               = size;
  1162.         obj->flags                              = 0;
  1163.         //@@if (orient != NULL)
  1164.         //@@    obj->orient                     = *orient;
  1165.  
  1166.         obj->orient                             = orient?*orient:vmd_identity_matrix;
  1167.  
  1168.         obj->control_type                       = ctype;
  1169.         set_object_movement_type(*obj, mtype);
  1170.         obj->render_type                        = rtype;
  1171.         obj->contains_count                     = 0;
  1172.         obj->matcen_creator                     = 0;
  1173.         obj->lifeleft                           = IMMORTAL_TIME;                //assume immortal
  1174.         obj->attached_obj                       = object_none;
  1175.  
  1176.         if (obj->control_type == CT_POWERUP)
  1177.         {
  1178.                 obj->ctype.powerup_info.count = 1;
  1179.                 obj->ctype.powerup_info.flags = 0;
  1180.                 obj->ctype.powerup_info.creation_time = GameTime64;
  1181.         }
  1182.  
  1183.         // Init physics info for this object
  1184.         if (obj->movement_type == MT_PHYSICS) {
  1185.                 obj->mtype.phys_info = {};
  1186.         }
  1187.  
  1188.         if (obj->render_type == RT_POLYOBJ)
  1189.         {
  1190.                 obj->rtype.pobj_info.subobj_flags = 0;
  1191.                 obj->rtype.pobj_info.tmap_override = -1;
  1192.                 obj->rtype.pobj_info.alt_textures = 0;
  1193.         }
  1194.  
  1195.         obj->shields                            = 20*F1_0;
  1196.  
  1197.         {
  1198.                 const auto &&p = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, pos, segnum);         //find correct segment
  1199.                 // Previously this was only an assert check.  Now it is also
  1200.                 // checked at runtime.
  1201.                 segnum = p;
  1202.         }
  1203.  
  1204.         obj_link_unchecked(Objects.vmptr, obj, segnum);
  1205.  
  1206.         //      Set (or not) persistent bit in phys_info.
  1207.         if (obj->type == OBJ_WEAPON) {
  1208.                 Assert(obj->control_type == CT_WEAPON);
  1209.                 obj->mtype.phys_info.flags |= (Weapon_info[get_weapon_id(obj)].persistent*PF_PERSISTENT);
  1210.                 obj->ctype.laser_info.creation_time = GameTime64;
  1211.                 obj->ctype.laser_info.clear_hitobj();
  1212.                 obj->ctype.laser_info.multiplier = F1_0;
  1213. #if defined(DXX_BUILD_DESCENT_II)
  1214.                 obj->ctype.laser_info.last_afterburner_time = 0;
  1215. #endif
  1216.         }
  1217.  
  1218.         if (obj->control_type == CT_EXPLOSION)
  1219.                 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = object_none;
  1220.  
  1221.         if (obj->type == OBJ_DEBRIS)
  1222.                 ++ LevelUniqueObjectState.Debris_object_count;
  1223.  
  1224.         return obj;
  1225. }
  1226.  
  1227. //create a copy of an object. returns new object number
  1228. imobjptridx_t obj_create_copy(const object &srcobj, const vmsegptridx_t newsegnum)
  1229. {
  1230.         auto &Objects = LevelUniqueObjectState.Objects;
  1231.         // Find next free object
  1232.         const auto &&obj = obj_allocate(LevelUniqueObjectState);
  1233.  
  1234.         if (obj == object_none)
  1235.                 return object_none;
  1236.  
  1237.         const auto signature = obj->signature;
  1238.         *obj = srcobj;
  1239.  
  1240.         obj_link_unchecked(Objects.vmptr, obj, newsegnum);
  1241.         obj->signature = object_signature_t(signature.get() + 1);
  1242.  
  1243.         //we probably should initialize sub-structures here
  1244.  
  1245.         return obj;
  1246. }
  1247.  
  1248. //remove object from the world
  1249. void obj_delete(d_level_unique_object_state &LevelUniqueObjectState, segment_array &Segments, const vmobjptridx_t obj)
  1250. {
  1251.         auto &Objects = LevelUniqueObjectState.get_objects();
  1252.         Assert(obj->type != OBJ_NONE);
  1253.         Assert(obj != ConsoleObject);
  1254.  
  1255. #if defined(DXX_BUILD_DESCENT_II)
  1256.         if (obj->type==OBJ_WEAPON && get_weapon_id(obj)==weapon_id_type::GUIDEDMISS_ID && obj->ctype.laser_info.parent_type==OBJ_PLAYER)
  1257.         {
  1258.                 const auto pnum = get_player_id(Objects.vcptr(obj->ctype.laser_info.parent_num));
  1259.                 const auto &&gimobj = LevelUniqueObjectState.Guided_missile.get_player_active_guided_missile(Objects.vmptridx, pnum);
  1260.                 if (gimobj == obj)
  1261.                 {
  1262.                         LevelUniqueObjectState.Guided_missile.clear_player_active_guided_missile(pnum);
  1263.                         if (pnum == Player_num)
  1264.                         {
  1265.                                 if (!PlayerCfg.GuidedInBigWindow)
  1266.                                         do_cockpit_window_view(1, WBU_STATIC);
  1267.                                 if (Newdemo_state == ND_STATE_RECORDING)
  1268.                                         newdemo_record_guided_end();
  1269.                         }
  1270.                 }
  1271.         }
  1272. #endif
  1273.  
  1274.         if (obj == Viewer)              //deleting the viewer?
  1275.                 Viewer = ConsoleObject;                                         //..make the player the viewer
  1276.  
  1277.         if (obj->flags & OF_ATTACHED)           //detach this from object
  1278.                 obj_detach_one(Objects, obj);
  1279.  
  1280.         if (obj->attached_obj != object_none)           //detach all objects from this
  1281.                 obj_detach_all(Objects, obj);
  1282.  
  1283.         if (obj->type == OBJ_DEBRIS)
  1284.                 -- LevelUniqueObjectState.Debris_object_count;
  1285.  
  1286.         if (obj->movement_type == MT_PHYSICS && (obj->mtype.phys_info.flags & PF_STICK))
  1287.                 LevelUniqueStuckObjectState.remove_stuck_object(obj);
  1288.         obj_unlink(Objects.vmptr, Segments.vmptr, obj);
  1289.         const auto signature = obj->signature;
  1290.         DXX_POISON_VAR(*obj, 0xfa);
  1291.         obj->type = OBJ_NONE;           //unused!
  1292.         /* Preserve signature across the poison value.  When the object slot
  1293.          * is reused, the allocator will need the old signature so that the
  1294.          * new one can be derived from it.  No other sites should read it
  1295.          * until that happens.
  1296.          */
  1297.         obj->signature = signature;
  1298.         obj_free(LevelUniqueObjectState, obj);
  1299. }
  1300.  
  1301. #define DEATH_SEQUENCE_LENGTH                   (F1_0*5)
  1302. #define DEATH_SEQUENCE_EXPLODE_TIME     (F1_0*2)
  1303.  
  1304. object  *Dead_player_camera = NULL;     //      Object index of object watching deader.
  1305. static const object *Viewer_save;
  1306. }
  1307. namespace dcx {
  1308. player_dead_state Player_dead_state = player_dead_state::no;                    //      If !0, then player is dead, but game continues so he can watch.
  1309. static int Player_flags_save;
  1310. static fix Camera_to_player_dist_goal = F1_0*4;
  1311. static uint8_t Control_type_save;
  1312. static render_type_t Render_type_save;
  1313.  
  1314. unsigned laser_parent_is_matching_signature(const laser_parent &l, const object_base &o)
  1315. {
  1316.         if (l.parent_type != o.type)
  1317.                 return 0;
  1318.         return l.parent_signature == o.signature;
  1319. }
  1320.  
  1321. }
  1322.  
  1323. namespace dsx {
  1324. //      ------------------------------------------------------------------------------------------------------------------
  1325. void dead_player_end(void)
  1326. {
  1327.         auto &Objects = LevelUniqueObjectState.Objects;
  1328.         auto &vmobjptridx = Objects.vmptridx;
  1329.         if (Player_dead_state == player_dead_state::no)
  1330.                 return;
  1331.  
  1332.         if (Newdemo_state == ND_STATE_RECORDING)
  1333.                 newdemo_record_restore_cockpit();
  1334.  
  1335.         Player_dead_state = player_dead_state::no;
  1336.         obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(Dead_player_camera));
  1337.         Dead_player_camera = NULL;
  1338.         select_cockpit(PlayerCfg.CockpitMode[0]);
  1339.         Viewer = Viewer_save;
  1340.         ConsoleObject->type = OBJ_PLAYER;
  1341.         ConsoleObject->flags = Player_flags_save;
  1342.  
  1343.         Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
  1344.  
  1345.         ConsoleObject->control_type = Control_type_save;
  1346.         ConsoleObject->render_type = Render_type_save;
  1347.         auto &player_info = ConsoleObject->ctype.player_info;
  1348.         player_info.powerup_flags &= ~PLAYER_FLAGS_INVULNERABLE;
  1349.         player_info.Player_eggs_dropped = false;
  1350. }
  1351.  
  1352. //      ------------------------------------------------------------------------------------------------------------------
  1353. //      Camera is less than size of player away from
  1354. static void set_camera_pos(vms_vector &camera_pos, const vcobjptridx_t objp)
  1355. {
  1356.         int     count = 0;
  1357.         fix     camera_player_dist;
  1358.         fix     far_scale;
  1359.  
  1360.         camera_player_dist = vm_vec_dist_quick(camera_pos, objp->pos);
  1361.  
  1362.         if (camera_player_dist < Camera_to_player_dist_goal) {
  1363.                 //      Camera is too close to player object, so move it away.
  1364.                 fvi_query       fq;
  1365.                 fvi_info                hit_data;
  1366.  
  1367.                 auto player_camera_vec = vm_vec_sub(camera_pos, objp->pos);
  1368.                 if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
  1369.                         player_camera_vec.x += F1_0/16;
  1370.  
  1371.                 hit_data.hit_type = HIT_WALL;
  1372.                 far_scale = F1_0;
  1373.  
  1374.                 while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
  1375.                         vm_vec_normalize_quick(player_camera_vec);
  1376.                         vm_vec_scale(player_camera_vec, Camera_to_player_dist_goal);
  1377.  
  1378.                         fq.p0 = &objp->pos;
  1379.                         const auto closer_p1 = vm_vec_add(objp->pos, player_camera_vec);                //      This is the actual point we want to put the camera at.
  1380.                         vm_vec_scale(player_camera_vec, far_scale);                                             //      ...but find a point 50% further away...
  1381.                         const auto local_p1 = vm_vec_add(objp->pos, player_camera_vec);         //      ...so we won't have to do as many cuts.
  1382.  
  1383.                         fq.p1 = &local_p1;
  1384.                         fq.startseg = objp->segnum;
  1385.                         fq.rad = 0;
  1386.                         fq.thisobjnum = objp;
  1387.                         fq.ignore_obj_list.first = nullptr;
  1388.                         fq.flags = 0;
  1389.                         find_vector_intersection(fq, hit_data);
  1390.  
  1391.                         if (hit_data.hit_type == HIT_NONE) {
  1392.                                 camera_pos = closer_p1;
  1393.                         } else {
  1394.                                 make_random_vector(player_camera_vec);
  1395.                                 far_scale = 3*F1_0/2;
  1396.                         }
  1397.                 }
  1398.         }
  1399. }
  1400.  
  1401. //      ------------------------------------------------------------------------------------------------------------------
  1402. window_event_result dead_player_frame()
  1403. {
  1404.         auto &Objects = LevelUniqueObjectState.Objects;
  1405.         auto &vcobjptridx = Objects.vcptridx;
  1406.         auto &vmobjptr = Objects.vmptr;
  1407.         auto &vmobjptridx = Objects.vmptridx;
  1408.         static fix      time_dead = 0;
  1409.  
  1410.         if (Player_dead_state != player_dead_state::no)
  1411.         {
  1412.                 time_dead += FrameTime;
  1413.  
  1414.                 //      If unable to create camera at time of death, create now.
  1415.                 if (Dead_player_camera == Viewer_save) {
  1416.                         const auto &player = get_local_plrobj();
  1417.                         const auto &&objnum = obj_create(OBJ_CAMERA, 0, vmsegptridx(player.segnum), player.pos, &player.orient, 0, CT_NONE, MT_NONE, RT_NONE);
  1418.  
  1419.                         if (objnum != object_none)
  1420.                                 Viewer = Dead_player_camera = objnum;
  1421.                         else {
  1422.                                 Int3();
  1423.                         }
  1424.                 }              
  1425.  
  1426.                 ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
  1427.                 ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
  1428.                 ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
  1429.  
  1430.                 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
  1431.  
  1432.                 set_camera_pos(Dead_player_camera->pos, vcobjptridx(ConsoleObject));
  1433.  
  1434.                 // the following line uncommented by WraithX, 4-12-00
  1435.                 if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
  1436.                 {
  1437.                         const auto fvec = vm_vec_sub(ConsoleObject->pos, Dead_player_camera->pos);
  1438.                         vm_vector_2_matrix(Dead_player_camera->orient, fvec, nullptr, nullptr);
  1439.                         Dead_player_camera->mtype.phys_info = ConsoleObject->mtype.phys_info;
  1440.  
  1441.                         // the following "if" added by WraithX to get rid of camera "wiggle"
  1442.                         Dead_player_camera->mtype.phys_info.flags &= ~PF_WIGGLE;
  1443.                         // end "if" added by WraithX, 4/13/00
  1444.  
  1445.                 // the following line uncommented by WraithX, 4-12-00
  1446.                 }
  1447.                 else
  1448.                 {
  1449.                         // the following line uncommented by WraithX, 4-11-00
  1450.                         Dead_player_camera->movement_type = MT_PHYSICS;
  1451.                         //Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
  1452.                 // the following line uncommented by WraithX, 4-12-00
  1453.                 }
  1454.                 // end addition by WX
  1455.  
  1456.                 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
  1457.                         if (Player_dead_state != player_dead_state::exploded)
  1458.                         {
  1459.                                 auto &player_info = get_local_plrobj().ctype.player_info;
  1460.                                 const auto hostages_lost = std::exchange(player_info.mission.hostages_on_board, 0);
  1461.  
  1462.                                 if (hostages_lost > 1)
  1463.                                         HUD_init_message(HM_DEFAULT, TXT_SHIP_DESTROYED_2, hostages_lost);
  1464.                                 else
  1465.                                         HUD_init_message_literal(HM_DEFAULT, hostages_lost == 1 ? TXT_SHIP_DESTROYED_1 : TXT_SHIP_DESTROYED_0);
  1466.  
  1467.                                 Player_dead_state = player_dead_state::exploded;
  1468.                                
  1469.                                 const auto cobjp = vmobjptridx(ConsoleObject);
  1470.                                 drop_player_eggs(cobjp);
  1471.                                 player_info.Player_eggs_dropped = true;
  1472.                                 if (Game_mode & GM_MULTI)
  1473.                                 {
  1474.                                         multi_send_player_deres(deres_explode);
  1475.                                 }
  1476.  
  1477.                                 explode_badass_player(cobjp);
  1478.  
  1479.                                 //is this next line needed, given the badass call above?
  1480.                                 explode_object(cobjp,0);
  1481.                                 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD;             //don't really kill player
  1482.                                 ConsoleObject->render_type = RT_NONE;                           //..just make him disappear
  1483.                                 ConsoleObject->type = OBJ_GHOST;                                                //..and kill intersections
  1484. #if defined(DXX_BUILD_DESCENT_II)
  1485.                                 player_info.powerup_flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
  1486. #endif
  1487.                         }
  1488.                 } else {
  1489.                         if (d_rand() < FrameTime*4) {
  1490.                                 if (Game_mode & GM_MULTI)
  1491.                                         multi_send_create_explosion(Player_num);
  1492.                                 create_small_fireball_on_object(vmobjptridx(ConsoleObject), F1_0, 1);
  1493.                         }
  1494.                 }
  1495.  
  1496.  
  1497.                 if (GameViewUniqueState.Death_sequence_aborted)
  1498.                 {
  1499.                         auto &player_info = get_local_plrobj().ctype.player_info;
  1500.                         if (!player_info.Player_eggs_dropped) {
  1501.                                 player_info.Player_eggs_dropped = true;
  1502.                                 drop_player_eggs(vmobjptridx(ConsoleObject));
  1503.                                 if (Game_mode & GM_MULTI)
  1504.                                 {
  1505.                                         multi_send_player_deres(deres_explode);
  1506.                                 }
  1507.                         }
  1508.  
  1509.                         return DoPlayerDead();          //kill_player();
  1510.                 }
  1511.         }
  1512.         else
  1513.                 time_dead = 0;
  1514.  
  1515.         return window_event_result::handled;
  1516. }
  1517.  
  1518. //      ------------------------------------------------------------------------------------------------------------------
  1519. static void start_player_death_sequence(object &player)
  1520. {
  1521.         auto &Objects = LevelUniqueObjectState.Objects;
  1522. #if defined(DXX_BUILD_DESCENT_II)
  1523.         auto &vmobjptr = Objects.vmptr;
  1524. #endif
  1525.         auto &vmobjptridx = Objects.vmptridx;
  1526.         assert(&player == ConsoleObject);
  1527.         if (Player_dead_state != player_dead_state::no ||
  1528.                 Dead_player_camera != NULL ||
  1529.                 ((Game_mode & GM_MULTI) && (get_local_player().connected != CONNECT_PLAYING)))
  1530.                 return;
  1531.  
  1532.         //Assert(Dead_player_camera == NULL);
  1533.  
  1534.         reset_rear_view();
  1535.  
  1536.         if (!(Game_mode & GM_MULTI))
  1537.                 HUD_clear_messages();
  1538.  
  1539.         GameViewUniqueState.Death_sequence_aborted = 0;
  1540.  
  1541.         if (Game_mode & GM_MULTI)
  1542.         {
  1543. #if defined(DXX_BUILD_DESCENT_II)
  1544.                 // If Hoard, increase number of orbs by 1. Only if you haven't killed yourself. This prevents cheating
  1545.                 if (game_mode_hoard())
  1546.                 {
  1547.                         auto &player_info = player.ctype.player_info;
  1548.                         auto &proximity = player_info.hoard.orbs;
  1549.                         if (proximity < player_info.max_hoard_orbs)
  1550.                         {
  1551.                                 const auto is_bad_kill = [&vmobjptr]{
  1552.                                         auto &lplr = get_local_player();
  1553.                                         auto &lplrobj = get_local_plrobj();
  1554.                                         const auto killer_objnum = lplrobj.ctype.player_info.killer_objnum;
  1555.                                         if (killer_objnum == lplr.objnum)
  1556.                                                 /* Self kill */
  1557.                                                 return true;
  1558.                                         if (killer_objnum == object_none)
  1559.                                                 /* Non-player kill */
  1560.                                                 return true;
  1561.                                         const auto &&killer_objp = vmobjptr(killer_objnum);
  1562.                                         if (killer_objp->type != OBJ_PLAYER)
  1563.                                                 return true;
  1564.                                         if (!(Game_mode & GM_TEAM))
  1565.                                                 return false;
  1566.                                         return get_team(Player_num) == get_team(get_player_id(killer_objp));
  1567.                                 };
  1568.                                 if (!is_bad_kill())
  1569.                                         ++ proximity;
  1570.                         }
  1571.                 }
  1572. #endif
  1573.                 multi_send_kill(vmobjptridx(get_local_player().objnum));
  1574.         }
  1575.        
  1576.         PaletteRedAdd = 40;
  1577.         Player_dead_state = player_dead_state::yes;
  1578.  
  1579.         vm_vec_zero(player.mtype.phys_info.rotthrust);
  1580.         vm_vec_zero(player.mtype.phys_info.thrust);
  1581.  
  1582.         const auto &&objnum = obj_create(OBJ_CAMERA, 0, vmsegptridx(player.segnum), player.pos, &player.orient, 0, CT_NONE, MT_NONE, RT_NONE);
  1583.         Viewer_save = Viewer;
  1584.         if (objnum != object_none)
  1585.                 Viewer = Dead_player_camera = objnum;
  1586.         else {
  1587.                 Int3();
  1588.                 Dead_player_camera = ConsoleObject;
  1589.         }
  1590.  
  1591.         select_cockpit(CM_LETTERBOX);
  1592.         if (Newdemo_state == ND_STATE_RECORDING)
  1593.                 newdemo_record_letterbox();
  1594.  
  1595.         Player_flags_save = player.flags;
  1596.         Control_type_save = player.control_type;
  1597.         Render_type_save = player.render_type;
  1598.  
  1599.         player.flags &= ~OF_SHOULD_BE_DEAD;
  1600. //      Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
  1601.         player.control_type = CT_NONE;
  1602.  
  1603.         PALETTE_FLASH_SET(0,0,0);
  1604. }
  1605.  
  1606. //      ------------------------------------------------------------------------------------------------------------------
  1607. static void obj_delete_all_that_should_be_dead()
  1608. {
  1609.         auto &Objects = LevelUniqueObjectState.Objects;
  1610.         auto &vmobjptridx = Objects.vmptridx;
  1611.         objnum_t                local_dead_player_object=object_none;
  1612.  
  1613.         // Move all objects
  1614.         range_for (const auto &&objp, vmobjptridx)
  1615.         {
  1616.                 if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
  1617.                         Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
  1618.                         if (objp->type==OBJ_PLAYER) {
  1619.                                 if ( get_player_id(objp) == Player_num ) {
  1620.                                         if (local_dead_player_object == object_none) {
  1621.                                                 start_player_death_sequence(objp);
  1622.                                                 local_dead_player_object = objp;
  1623.                                         } else
  1624.                                                 Int3(); //      Contact Mike: Illegal, killed player twice in this frame!
  1625.                                                                         // Ok to continue, won't start death sequence again!
  1626.                                         // kill_player();
  1627.                                 }
  1628.                         } else {                                       
  1629.                                 obj_delete(LevelUniqueObjectState, Segments, objp);
  1630.                         }
  1631.                 }
  1632.         }
  1633. }
  1634.  
  1635. //when an object has moved into a new segment, this function unlinks it
  1636. //from its old segment, and links it into the new segment
  1637. void obj_relink(fvmobjptr &vmobjptr, fvmsegptr &vmsegptr, const vmobjptridx_t objnum, const vmsegptridx_t newsegnum)
  1638. {
  1639.         obj_unlink(vmobjptr, vmsegptr, objnum);
  1640.         obj_link_unchecked(vmobjptr, objnum, newsegnum);
  1641. }
  1642.  
  1643. // for getting out of messed up linking situations (i.e. caused by demo playback)
  1644. void obj_relink_all(void)
  1645. {
  1646.         auto &Objects = LevelUniqueObjectState.Objects;
  1647.         range_for (const auto &&segp, vmsegptr)
  1648.         {
  1649.                 segp->objects = object_none;
  1650.         }
  1651.        
  1652.         range_for (const auto &&obj, Objects.vmptridx)
  1653.         {
  1654.                 if (obj->type != OBJ_NONE)
  1655.                 {
  1656.                         auto segnum = obj->segnum;
  1657.                         if (segnum > Highest_segment_index)
  1658.                                 segnum = segment_first;
  1659.                         obj_link_unchecked(Objects.vmptr, obj, Segments.vmptridx(segnum));
  1660.                 }
  1661.         }
  1662. }
  1663.  
  1664. //process a continuously-spinning object
  1665. static void spin_object(object_base &obj)
  1666. {
  1667.         vms_angvec rotangs;
  1668.         assert(obj.movement_type == MT_SPINNING);
  1669.  
  1670.         const fix frametime = FrameTime;
  1671.         rotangs.p = fixmul(obj.mtype.spin_rate.x, frametime);
  1672.         rotangs.h = fixmul(obj.mtype.spin_rate.y, frametime);
  1673.         rotangs.b = fixmul(obj.mtype.spin_rate.z, frametime);
  1674.  
  1675.         const auto &&rotmat = vm_angles_2_matrix(rotangs);
  1676.         obj.orient = vm_matrix_x_matrix(obj.orient, rotmat);
  1677.         check_and_fix_matrix(obj.orient);
  1678. }
  1679.  
  1680. #if defined(DXX_BUILD_DESCENT_II)
  1681. imobjidx_t d_guided_missile_indices::get_player_active_guided_missile(const unsigned pnum) const
  1682. {
  1683.         return operator[](pnum);
  1684. }
  1685.  
  1686. /* Place debug checks out of line so that they are shared among the
  1687.  * template instantiations.
  1688.  */
  1689. bool d_guided_missile_indices::debug_check_current_object(const object_base &obj)
  1690. {
  1691.         assert(obj.type == OBJ_WEAPON);
  1692.         const auto gmid = get_weapon_id(obj);
  1693.         if (obj.type != OBJ_WEAPON)
  1694.                 return false;
  1695.         assert(gmid == weapon_id_type::GUIDEDMISS_ID);
  1696.         if (gmid != weapon_id_type::GUIDEDMISS_ID)
  1697.                 return false;
  1698.         return true;
  1699. }
  1700.  
  1701. template <typename R, typename F>
  1702. R d_guided_missile_indices::get_player_active_guided_missile_tmpl(F &fobjptr, const unsigned pnum) const
  1703. {
  1704.         const auto gmidx = get_player_active_guided_missile(pnum);
  1705.         if (gmidx == object_none)
  1706.                 return object_none;
  1707.         auto &&gmobj = fobjptr(gmidx);
  1708.         if (!debug_check_current_object(gmobj))
  1709.                 return object_none;
  1710.         return gmobj;
  1711. }
  1712.  
  1713. imobjptr_t d_guided_missile_indices::get_player_active_guided_missile(fvmobjptr &vmobjptr, const unsigned pnum) const
  1714. {
  1715.         return this->template get_player_active_guided_missile_tmpl<imobjptr_t>(vmobjptr, pnum);
  1716. }
  1717.  
  1718. imobjptridx_t d_guided_missile_indices::get_player_active_guided_missile(fvmobjptridx &vmobjptridx, const unsigned pnum) const
  1719. {
  1720.         return this->template get_player_active_guided_missile_tmpl<imobjptridx_t>(vmobjptridx, pnum);
  1721. }
  1722.  
  1723. void d_guided_missile_indices::set_player_active_guided_missile(const vmobjidx_t obji, const unsigned pnum)
  1724. {
  1725.         auto &i = operator[](pnum);
  1726.         i = obji;
  1727. }
  1728.  
  1729. void d_guided_missile_indices::clear_player_active_guided_missile(const unsigned pnum)
  1730. {
  1731.         auto &i = operator[](pnum);
  1732.         i = object_none;
  1733. }
  1734.  
  1735. int Drop_afterburner_blob_flag;         //ugly hack
  1736. //see if wall is volatile, and if so, cause damage to player
  1737. //returns true if player is in lava
  1738. #endif
  1739.  
  1740. //--------------------------------------------------------------------
  1741. //move an object for the current frame
  1742. static window_event_result object_move_one(const vmobjptridx_t obj)
  1743. {
  1744. #if defined(DXX_BUILD_DESCENT_II)
  1745.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1746.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1747. #endif
  1748.         auto &Objects = LevelUniqueObjectState.Objects;
  1749.         auto &vmobjptr = Objects.vmptr;
  1750.         const auto previous_segment = obj->segnum;
  1751.         auto result = window_event_result::handled;
  1752.  
  1753.         const auto obj_previous_position = obj->pos;                    // Save the current position
  1754.  
  1755.         if ((obj->type==OBJ_PLAYER) && (Player_num==get_player_id(obj)))        {
  1756.                 const auto &&segp = vmsegptr(obj->segnum);
  1757. #if defined(DXX_BUILD_DESCENT_II)
  1758.                 if (game_mode_capture_flag())
  1759.                         fuelcen_check_for_goal(obj, segp);
  1760.                 else if (game_mode_hoard())
  1761.                         fuelcen_check_for_hoard_goal(obj, segp);
  1762. #endif
  1763.  
  1764.                 auto &player_info = obj->ctype.player_info;
  1765.                 auto &energy = player_info.energy;
  1766.                 const fix fuel = fuelcen_give_fuel(segp, INITIAL_ENERGY - energy);
  1767.                 if (fuel > 0 )  {
  1768.                         energy += fuel;
  1769.                 }
  1770. #if defined(DXX_BUILD_DESCENT_II)
  1771.                 auto &pl_shields = get_local_plrobj().shields;
  1772.                 const fix shields = repaircen_give_shields(segp, INITIAL_SHIELDS - pl_shields);
  1773.                 if (shields > 0) {
  1774.                         pl_shields += shields;
  1775.                 }
  1776. #endif
  1777.         }
  1778.  
  1779.         {
  1780.                 auto lifeleft = obj->lifeleft;
  1781.                 if (lifeleft != IMMORTAL_TIME) //if not immortal...
  1782.                 {
  1783.                         lifeleft -= FrameTime; //...inevitable countdown towards death
  1784. #if defined(DXX_BUILD_DESCENT_II)
  1785.                         if (obj->type == OBJ_MARKER)
  1786.                         {
  1787.                                 if (lifeleft < F1_0*1000)
  1788.                                         lifeleft += F1_0; // Make sure this object doesn't go out.
  1789.                         }
  1790. #endif
  1791.                         obj->lifeleft = lifeleft;
  1792.                 }
  1793.         }
  1794. #if defined(DXX_BUILD_DESCENT_II)
  1795.         Drop_afterburner_blob_flag = 0;
  1796. #endif
  1797.  
  1798.         switch (obj->control_type) {
  1799.  
  1800.                 case CT_NONE: break;
  1801.  
  1802.                 case CT_FLYING:
  1803.  
  1804.                         read_flying_controls( obj );
  1805.  
  1806.                         break;
  1807.  
  1808.                 case CT_REPAIRCEN:
  1809.                         Int3();
  1810.                         // -- hey! these are no longer supported!! -- do_repair_sequence(obj);
  1811.                         break;
  1812.  
  1813.                 case CT_POWERUP:
  1814.                         do_powerup_frame(Vclip, obj);
  1815.                         break;
  1816.        
  1817.                 case CT_MORPH:                  //morph implies AI
  1818.                         do_morph_frame(obj);
  1819.                         //NOTE: FALLS INTO AI HERE!!!!
  1820.                         DXX_BOOST_FALLTHROUGH;
  1821.  
  1822.                 case CT_AI:
  1823.                         //NOTE LINK TO CT_MORPH ABOVE!!!
  1824.                         if (Game_suspended & SUSP_ROBOTS) return window_event_result::ignored;
  1825.                         do_ai_frame(obj);
  1826.                         break;
  1827.  
  1828.                 case CT_WEAPON:         Laser_do_weapon_sequence(obj); break;
  1829.                 case CT_EXPLOSION:      do_explosion_sequence(obj); break;
  1830.  
  1831.                 case CT_SLEW:
  1832. #ifdef RELEASE
  1833.                         obj->control_type = CT_NONE;
  1834.                         con_printf(CON_URGENT, DXX_STRINGIZE_FL(__FILE__, __LINE__, "BUG: object %hu has control type CT_SLEW, sig/type/id = %i/%i/%i"), static_cast<objnum_t>(obj), obj->signature.get(), obj->type, obj->id);
  1835. #else
  1836.                         if ( keyd_pressed[KEY_PAD5] ) slew_stop();
  1837.                         if ( keyd_pressed[KEY_NUMLOCK] )                {
  1838.                                 slew_reset_orient();
  1839.                         }
  1840.                         slew_frame(0 );         // Does velocity addition for us.
  1841. #endif
  1842.                         break;
  1843.  
  1844. //              case CT_FLYTHROUGH:
  1845. //                      do_flythrough(obj,0);                   // HACK:do_flythrough should operate on an object!!!!
  1846. //                      //check_object_seg(obj);
  1847. //                      return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
  1848. //                      break;
  1849.  
  1850.                 case CT_DEBRIS: do_debris_frame(obj); break;
  1851.  
  1852.                 case CT_LIGHT: break;           //doesn't do anything
  1853.  
  1854.                 case CT_REMOTE: break;          //doesn't do anything
  1855.  
  1856.                 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
  1857.  
  1858.                 default:
  1859.  
  1860.                         Error("Unknown control type %d in object %hu, sig/type/id = %i/%i/%i",obj->control_type, static_cast<objnum_t>(obj), obj->signature.get(), obj->type, obj->id);
  1861.  
  1862.                         break;
  1863.  
  1864.         }
  1865.  
  1866.         if (obj->lifeleft < 0 ) {               // We died of old age
  1867.                 obj->flags |= OF_SHOULD_BE_DEAD;
  1868.                 if ( obj->type==OBJ_WEAPON && Weapon_info[get_weapon_id(obj)].damage_radius )
  1869.                         explode_badass_weapon(obj, obj->pos);
  1870. #if defined(DXX_BUILD_DESCENT_II)
  1871.                 else if ( obj->type==OBJ_ROBOT) //make robots explode
  1872.                         explode_object(obj,0);
  1873. #endif
  1874.         }
  1875.  
  1876.         if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
  1877.                 return window_event_result::ignored;         // object has been deleted
  1878.  
  1879.         bool prepare_seglist = false;
  1880.         phys_visited_seglist phys_visited_segs;
  1881.         switch (obj->movement_type) {
  1882.  
  1883.                 case MT_NONE:                   break;                          //this doesn't move
  1884.  
  1885.                 case MT_PHYSICS:        //move by physics
  1886.                         result = do_physics_sim(obj, obj_previous_position, obj->type == OBJ_PLAYER ? (prepare_seglist = true, phys_visited_segs.nsegs = 0, &phys_visited_segs) : nullptr);
  1887.                         break;
  1888.  
  1889.                 case MT_SPINNING:               spin_object(obj); break;
  1890.  
  1891.         }
  1892.  
  1893. #if defined(DXX_BUILD_DESCENT_II)
  1894.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1895.         auto &vcwallptr = Walls.vcptr;
  1896. #endif
  1897.         //      If player and moved to another segment, see if hit any triggers.
  1898.         // also check in player under a lavafall
  1899.         if (prepare_seglist)
  1900.         {
  1901.                 if (previous_segment != obj->segnum && phys_visited_segs.nsegs > 1)
  1902.                 {
  1903.                         auto seg0 = vmsegptridx(phys_visited_segs.seglist[0]);
  1904. #if defined(DXX_BUILD_DESCENT_II)
  1905.                         int     old_level = Current_level_num;
  1906. #endif
  1907.                         range_for (const auto i, partial_const_range(phys_visited_segs.seglist, 1u, phys_visited_segs.nsegs))
  1908.                         {
  1909.                                 const auto &&seg1 = seg0.absolute_sibling(i);
  1910.                                 const auto connect_side = find_connect_side(seg1, seg0);
  1911.                                 if (connect_side != side_none)
  1912.                                 {
  1913.                                         result = check_trigger(seg0, connect_side, get_local_plrobj(), obj, 0);
  1914. #if defined(DXX_BUILD_DESCENT_II)
  1915.                                 //maybe we've gone on to the next level.  if so, bail!
  1916.                                 if (Current_level_num != old_level)
  1917.                                         return window_event_result::ignored;
  1918. #endif
  1919.                                 }
  1920.                                 seg0 = seg1;
  1921.                         }
  1922.                 }
  1923. #if defined(DXX_BUILD_DESCENT_II)
  1924.                 {
  1925.                         bool under_lavafall = false;
  1926.  
  1927.                         auto &playing = obj->ctype.player_info.lavafall_hiss_playing;
  1928.                         const auto &&segp = vcsegptr(obj->segnum);
  1929.                         auto &vcvertptr = Vertices.vcptr;
  1930.                         if (const auto sidemask = get_seg_masks(vcvertptr, obj->pos, segp, obj->size).sidemask)
  1931.                         {
  1932.                                 range_for (const unsigned sidenum, xrange(MAX_SIDES_PER_SEGMENT))
  1933.                                 {
  1934.                                         if (!(sidemask & (1 << sidenum)))
  1935.                                                 continue;
  1936.                                         const auto wall_num = segp->shared_segment::sides[sidenum].wall_num;
  1937.                                         if (wall_num != wall_none && vcwallptr(wall_num)->type == WALL_ILLUSION)
  1938.                                         {
  1939.                                                 const auto type = check_volatile_wall(obj, segp->unique_segment::sides[sidenum]);
  1940.                                                 if (type != volatile_wall_result::none)
  1941.                                                 {
  1942.                                                         under_lavafall = 1;
  1943.                                                         if (!playing)
  1944.                                                         {
  1945.                                                                 playing = 1;
  1946.                                                                 const auto sound = (type == volatile_wall_result::lava) ? SOUND_LAVAFALL_HISS : SOUND_SHIP_IN_WATERFALL;
  1947.                                                                 digi_link_sound_to_object3(sound, obj, 1, F1_0, sound_stack::allow_stacking, vm_distance{i2f(256)}, -1, -1);
  1948.                                                                 break;
  1949.                                                         }
  1950.                                                 }
  1951.                                         }
  1952.                                 }
  1953.                         }
  1954.        
  1955.                         if (!under_lavafall && playing)
  1956.                         {
  1957.                                 playing = 0;
  1958.                                 digi_kill_sound_linked_to_object( obj);
  1959.                         }
  1960.                 }
  1961. #endif
  1962.         }
  1963.  
  1964. #if defined(DXX_BUILD_DESCENT_II)
  1965.         //see if guided missile has flown through exit trigger
  1966.         if (obj == LevelUniqueObjectState.Guided_missile.get_player_active_guided_missile(Player_num))
  1967.         {
  1968.                 if (previous_segment != obj->segnum) {
  1969.                         const auto &&psegp = vcsegptr(previous_segment);
  1970.                         const auto &&connect_side = find_connect_side(vcsegptridx(obj->segnum), psegp);
  1971.                         if (connect_side != side_none)
  1972.                         {
  1973.                                 const auto wall_num = psegp->shared_segment::sides[connect_side].wall_num;
  1974.                                 if ( wall_num != wall_none ) {
  1975.                                         auto trigger_num = vcwallptr(wall_num)->trigger;
  1976.                                         if (trigger_num != trigger_none)
  1977.                                         {
  1978.                                                 auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
  1979.                                                 auto &vctrgptr = Triggers.vcptr;
  1980.                                                 const auto &&t = vctrgptr(trigger_num);
  1981.                                                 if (t->type == trigger_action::normal_exit)
  1982.                                                         obj->lifeleft = 0;
  1983.                                         }
  1984.                                 }
  1985.                         }
  1986.                 }
  1987.         }
  1988.  
  1989.         if (Drop_afterburner_blob_flag) {
  1990.                 Assert(obj==ConsoleObject);
  1991.                 drop_afterburner_blobs(obj, 2, i2f(5)/2, -1);   //      -1 means use default lifetime
  1992.                 if (Game_mode & GM_MULTI)
  1993.                         multi_send_drop_blobs(Player_num);
  1994.                 Drop_afterburner_blob_flag = 0;
  1995.         }
  1996.  
  1997.         if ((obj->type == OBJ_WEAPON) && (Weapon_info[get_weapon_id(obj)].afterburner_size)) {
  1998.                 fix     vel = vm_vec_mag_quick(obj->mtype.phys_info.velocity);
  1999.                 fix     delay, lifetime;
  2000.  
  2001.                 if (vel > F1_0*200)
  2002.                         delay = F1_0/16;
  2003.                 else if (vel > F1_0*40)
  2004.                         delay = fixdiv(F1_0*13,vel);
  2005.                 else
  2006.                         delay = F1_0/4;
  2007.  
  2008.                 lifetime = (delay * 3)/2;
  2009.                 if (!(Game_mode & GM_MULTI)) {
  2010.                         delay /= 2;
  2011.                         lifetime *= 2;
  2012.                 }
  2013.  
  2014.                 assert(obj->control_type == CT_WEAPON);
  2015.                 if ((obj->ctype.laser_info.last_afterburner_time + delay < GameTime64) || (obj->ctype.laser_info.last_afterburner_time > GameTime64)) {
  2016.                         drop_afterburner_blobs(obj, 1, i2f(Weapon_info[get_weapon_id(obj)].afterburner_size)/16, lifetime);
  2017.                         obj->ctype.laser_info.last_afterburner_time = GameTime64;
  2018.                 }
  2019.         }
  2020.  
  2021. #endif
  2022.         return result;
  2023. }
  2024.  
  2025. //--------------------------------------------------------------------
  2026. //move all objects for the current frame
  2027. static window_event_result object_move_all()
  2028. {
  2029.         auto &Objects = LevelUniqueObjectState.Objects;
  2030.         auto &vmobjptridx = Objects.vmptridx;
  2031.         auto result = window_event_result::ignored;
  2032.  
  2033.         if (Highest_object_index > MAX_USED_OBJECTS)
  2034.                 free_object_slots(MAX_USED_OBJECTS);            //      Free all possible object slots.
  2035.  
  2036.         obj_delete_all_that_should_be_dead();
  2037.  
  2038.         if (PlayerCfg.AutoLeveling)
  2039.                 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
  2040.         else
  2041.                 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
  2042.  
  2043.         // Move all objects
  2044.         range_for (const auto &&objp, vmobjptridx)
  2045.         {
  2046.                 if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) )   {
  2047.                         result = std::max(object_move_one( objp ), result);
  2048.                 }
  2049.         }
  2050.  
  2051. //      check_duplicate_objects();
  2052. //      remove_incorrect_objects();
  2053.  
  2054.         return result;
  2055. }
  2056.  
  2057. window_event_result game_move_all_objects()
  2058. {
  2059.         LevelUniqueObjectState.last_console_player_position = ConsoleObject->pos;
  2060.         return object_move_all();
  2061. }
  2062.  
  2063. window_event_result endlevel_move_all_objects()
  2064. {
  2065.         return object_move_all();
  2066. }
  2067.  
  2068. //--unused-- // -----------------------------------------------------------
  2069. //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
  2070. //--unused-- int find_last_obj(int i)
  2071. //--unused-- {
  2072. //--unused--    for (i=MAX_OBJECTS;--i>=0;)
  2073. //--unused--            if (Objects[i].type != OBJ_NONE) break;
  2074. //--unused--
  2075. //--unused--    return i;
  2076. //--unused--
  2077. //--unused-- }
  2078.  
  2079.  
  2080. //make object array non-sparse
  2081. void compress_objects(void)
  2082. {
  2083.         auto &Objects = LevelUniqueObjectState.Objects;
  2084.         auto &vmobjptr = Objects.vmptr;
  2085.         auto &vmobjptridx = Objects.vmptridx;
  2086.         //last_i = find_last_obj(MAX_OBJECTS);
  2087.  
  2088.         //      Note: It's proper to do < (rather than <=) Highest_object_index here because we
  2089.         //      are just removing gaps, and the last object can't be a gap.
  2090.         for (objnum_t start_i=0;start_i<Highest_object_index;start_i++)
  2091.         {
  2092.                 const auto &&start_objp = vmobjptridx(start_i);
  2093.                 if (start_objp->type == OBJ_NONE) {
  2094.                         auto highest = Highest_object_index;
  2095.                         const auto &&h = vmobjptr(static_cast<objnum_t>(highest));
  2096.                         auto segnum_copy = h->segnum;
  2097.  
  2098.                         obj_unlink(Objects.vmptr, Segments.vmptr, h);
  2099.  
  2100.                         *start_objp = *h;
  2101.  
  2102. #if DXX_USE_EDITOR
  2103.                         if (Cur_object_index == Highest_object_index)
  2104.                                 Cur_object_index = start_i;
  2105.                         #endif
  2106.  
  2107.                         h->type = OBJ_NONE;
  2108.  
  2109.                         obj_link(Objects.vmptr, start_objp, vmsegptridx(segnum_copy));
  2110.  
  2111.                         while (vmobjptr(static_cast<objnum_t>(--highest))->type == OBJ_NONE)
  2112.                         {
  2113.                         }
  2114.                         Objects.set_count(highest + 1);
  2115.  
  2116.                         //last_i = find_last_obj(last_i);
  2117.                        
  2118.                 }
  2119.         }
  2120.         reset_objects(LevelUniqueObjectState, LevelUniqueObjectState.num_objects);
  2121. }
  2122.  
  2123. //called after load.  Takes number of objects,  and objects should be
  2124. //compressed.  resets free list, marks unused objects as unused
  2125. void reset_objects(d_level_unique_object_state &LevelUniqueObjectState, const unsigned n_objs)
  2126. {
  2127.         LevelUniqueObjectState.Debris_object_count = 0;
  2128.         LevelUniqueObjectState.num_objects = n_objs;
  2129.         assert(LevelUniqueObjectState.num_objects > 0);
  2130.         auto &Objects = LevelUniqueObjectState.get_objects();
  2131.         assert(LevelUniqueObjectState.num_objects < Objects.size());
  2132.         Objects.set_count(n_objs);
  2133.  
  2134.         for (objnum_t i = n_objs; i < MAX_OBJECTS; ++i)
  2135.         {
  2136.                 LevelUniqueObjectState.free_obj_list[i] = i;
  2137.                 auto &obj = *Objects.vmptr(i);
  2138.                 DXX_POISON_VAR(obj, 0xfd);
  2139.                 obj.type = OBJ_NONE;
  2140.                 obj.signature = object_signature_t{0};
  2141.         }
  2142. }
  2143.  
  2144. //Tries to find a segment for an object, using find_point_seg()
  2145. imsegptridx_t find_object_seg(const d_level_shared_segment_state &LevelSharedSegmentState, d_level_unique_segment_state &LevelUniqueSegmentState, const object_base &obj)
  2146. {
  2147.         auto &Segments = LevelUniqueSegmentState.get_segments();
  2148.         return find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj.pos, Segments.vmptridx(obj.segnum));
  2149. }
  2150.  
  2151.  
  2152. //If an object is in a segment, set its segnum field and make sure it's
  2153. //properly linked.  If not in any segment, returns 0, else 1.
  2154. //callers should generally use find_vector_intersection()
  2155. int update_object_seg(fvmobjptr &vmobjptr, const d_level_shared_segment_state &LevelSharedSegmentState, d_level_unique_segment_state &LevelUniqueSegmentState, const vmobjptridx_t obj)
  2156. {
  2157.         const auto &&newseg = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj);
  2158.         if (newseg == segment_none)
  2159.                 return 0;
  2160.  
  2161.         if ( newseg != obj->segnum )
  2162.                 obj_relink(vmobjptr, LevelUniqueSegmentState.get_segments().vmptr, obj, newseg);
  2163.  
  2164.         return 1;
  2165. }
  2166.  
  2167. unsigned laser_parent_is_player(fvcobjptr &vcobjptr, const laser_parent &l, const object_base &o)
  2168. {
  2169.         /* Player objects are never recycled, so skip the signature check.
  2170.          */
  2171.         if (l.parent_type != OBJ_PLAYER)
  2172.                 return 0;
  2173.         /* As a special case, let the player be recognized even if he died
  2174.          * before the weapon hit the target.
  2175.          */
  2176.         if (o.type != OBJ_PLAYER && o.type != OBJ_GHOST)
  2177.                 return 0;
  2178.         auto &parent_object = *vcobjptr(l.parent_num);
  2179.         return (&parent_object == &o);
  2180. }
  2181.  
  2182. unsigned laser_parent_is_object(fvcobjptr &vcobjptr, const laser_parent &l, const object_base &o)
  2183. {
  2184.         auto &parent_object = *vcobjptr(l.parent_num);
  2185.         if (&parent_object != &o)
  2186.                 return 0;
  2187.         return laser_parent_is_matching_signature(l, o);
  2188. }
  2189.  
  2190. unsigned laser_parent_is_object(const laser_parent &l, const vcobjptridx_t o)
  2191. {
  2192.         if (l.parent_num != o.get_unchecked_index())
  2193.                 return 0;
  2194.         return laser_parent_is_matching_signature(l, *o);
  2195. }
  2196.  
  2197. unsigned laser_parent_object_exists(fvcobjptr &vcobjptr, const laser_parent &l)
  2198. {
  2199.         return laser_parent_is_matching_signature(l, *vcobjptr(l.parent_num));
  2200. }
  2201.  
  2202. void set_powerup_id(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip, object_base &o, powerup_type_t id)
  2203. {
  2204.         o.id = id;
  2205.         o.size = Powerup_info[id].size;
  2206.         const auto vclip_num = Powerup_info[id].vclip_num;
  2207.         o.rtype.vclip_info.vclip_num = vclip_num;
  2208.         o.rtype.vclip_info.frametime = Vclip[vclip_num].frame_time;
  2209. }
  2210.  
  2211. //go through all objects and make sure they have the correct segment numbers
  2212. void fix_object_segs()
  2213. {
  2214.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  2215.         auto &Objects = LevelUniqueObjectState.Objects;
  2216.         auto &Vertices = LevelSharedVertexState.get_vertices();
  2217.         auto &vmobjptr = Objects.vmptr;
  2218.         auto &vmobjptridx = Objects.vmptridx;
  2219.         auto &vcvertptr = Vertices.vcptr;
  2220.         range_for (const auto &&o, vmobjptridx)
  2221.         {
  2222.                 if (o->type != OBJ_NONE)
  2223.                 {
  2224.                         const auto oldsegnum = o->segnum;
  2225.                         if (update_object_seg(vmobjptr, LevelSharedSegmentState, LevelUniqueSegmentState, o) == 0)
  2226.                         {
  2227.                                 const auto pos = o->pos;
  2228.                                 const auto segnum = o->segnum;
  2229.                                 compute_segment_center(vcvertptr, o->pos, vcsegptr(segnum));
  2230.                                 con_printf(CON_URGENT, "Object %hu claims segment %hu, but has position {%i,%i,%i}; moving to %hu:{%i,%i,%i}", o.get_unchecked_index(), oldsegnum, pos.x, pos.y, pos.z, segnum, o->pos.x, o->pos.y, o->pos.z);
  2231.                         }
  2232.                 }
  2233.         }
  2234. }
  2235.  
  2236.  
  2237. //--unused-- void object_use_new_object_list( object * new_list )
  2238. //--unused-- {
  2239. //--unused--    int i, segnum;
  2240. //--unused--    object *obj;
  2241. //--unused--
  2242. //--unused--    // First, unlink all the old objects for the segments array
  2243. //--unused--    for (segnum=0; segnum <= Highest_segment_index; segnum++) {
  2244. //--unused--            Segments[segnum].objects = -1;
  2245. //--unused--    }
  2246. //--unused--    // Then, erase all the objects
  2247. //--unused--    reset_objects(1);
  2248. //--unused--
  2249. //--unused--    // Fill in the object array
  2250. //--unused--    memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
  2251. //--unused--
  2252. //--unused--    Highest_object_index=-1;
  2253. //--unused--
  2254. //--unused--    // Relink 'em
  2255. //--unused--    for (i=0; i<MAX_OBJECTS; i++ )  {
  2256. //--unused--            obj = &Objects[i];
  2257. //--unused--            if ( obj->type != OBJ_NONE )    {
  2258. //--unused--                    num_objects++;
  2259. //--unused--                    Highest_object_index = i;
  2260. //--unused--                    segnum = obj->segnum;
  2261. //--unused--                    obj->next = obj->prev = obj->segnum = -1;
  2262. //--unused--                    obj_link(i,segnum);
  2263. //--unused--            } else {
  2264. //--unused--                    obj->next = obj->prev = obj->segnum = -1;
  2265. //--unused--            }
  2266. //--unused--    }
  2267. //--unused--    
  2268. //--unused-- }
  2269.  
  2270. #if defined(DXX_BUILD_DESCENT_I)
  2271. #define object_is_clearable_weapon(W,a,b)       object_is_clearable_weapon(a,b)
  2272. #endif
  2273. static unsigned object_is_clearable_weapon(const weapon_info_array &Weapon_info, const object_base obj, const unsigned clear_all)
  2274. {
  2275.         if (!(obj.type == OBJ_WEAPON))
  2276.                 return 0;
  2277.         const auto weapon_id = get_weapon_id(obj);
  2278. #if defined(DXX_BUILD_DESCENT_II)
  2279.         if (Weapon_info[weapon_id].flags & WIF_PLACABLE)
  2280.                 return 0;
  2281. #endif
  2282.         if (clear_all)
  2283.                 return clear_all;
  2284.         return !is_proximity_bomb_or_player_smart_mine(weapon_id);
  2285. }
  2286.  
  2287. //delete objects, such as weapons & explosions, that shouldn't stay between levels
  2288. //      Changed by MK on 10/15/94, don't remove proximity bombs.
  2289. //if clear_all is set, clear even proximity bombs
  2290. void clear_transient_objects(int clear_all)
  2291. {
  2292.         auto &Objects = LevelUniqueObjectState.Objects;
  2293.         auto &vmobjptridx = Objects.vmptridx;
  2294.         range_for (const auto &&obj, vmobjptridx)
  2295.         {
  2296.                 if (object_is_clearable_weapon(Weapon_info, obj, clear_all) ||
  2297.                          obj->type == OBJ_FIREBALL ||
  2298.                          obj->type == OBJ_DEBRIS ||
  2299.                          (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
  2300.                         obj_delete(LevelUniqueObjectState, Segments, obj);
  2301.                 }
  2302.         }
  2303. }
  2304.  
  2305. //attaches an object, such as a fireball, to another object, such as a robot
  2306. void obj_attach(object_array &Objects, const vmobjptridx_t parent, const vmobjptridx_t sub)
  2307. {
  2308.         Assert(sub->type == OBJ_FIREBALL);
  2309.         Assert(sub->control_type == CT_EXPLOSION);
  2310.  
  2311.         Assert(sub->ctype.expl_info.next_attach==object_none);
  2312.         Assert(sub->ctype.expl_info.prev_attach==object_none);
  2313.  
  2314.         assert(parent->attached_obj == object_none || Objects.vcptr(parent->attached_obj)->ctype.expl_info.prev_attach == object_none);
  2315.  
  2316.         sub->ctype.expl_info.next_attach = parent->attached_obj;
  2317.  
  2318.         if (sub->ctype.expl_info.next_attach != object_none)
  2319.                 Objects.vmptr(sub->ctype.expl_info.next_attach)->ctype.expl_info.prev_attach = sub;
  2320.  
  2321.         parent->attached_obj = sub;
  2322.  
  2323.         sub->ctype.expl_info.attach_parent = parent;
  2324.         sub->flags |= OF_ATTACHED;
  2325.  
  2326.         Assert(sub->ctype.expl_info.next_attach != sub);
  2327.         Assert(sub->ctype.expl_info.prev_attach != sub);
  2328. }
  2329.  
  2330. //dettaches one object
  2331. void obj_detach_one(object_array &Objects, object &sub)
  2332. {
  2333.         Assert(sub.flags & OF_ATTACHED);
  2334.         Assert(sub.ctype.expl_info.attach_parent != object_none);
  2335.  
  2336.         const auto &&parent_objp = Objects.vcptr(sub.ctype.expl_info.attach_parent);
  2337.         if (parent_objp->type == OBJ_NONE || parent_objp->attached_obj == object_none)
  2338.         {
  2339.                 sub.flags &= ~OF_ATTACHED;
  2340.                 return;
  2341.         }
  2342.  
  2343.         if (sub.ctype.expl_info.next_attach != object_none)
  2344.         {
  2345.                 auto &a = Objects.vmptr(sub.ctype.expl_info.next_attach)->ctype.expl_info.prev_attach;
  2346.                 assert(Objects.vcptr(a) == &sub);
  2347.                 a = sub.ctype.expl_info.prev_attach;
  2348.         }
  2349.  
  2350.         const auto use_prev_attach = (sub.ctype.expl_info.prev_attach != object_none);
  2351.         auto &o = *Objects.vmptr(use_prev_attach ? std::exchange(sub.ctype.expl_info.prev_attach, object_none) : sub.ctype.expl_info.attach_parent);
  2352.         auto &update_attach = use_prev_attach ? o.ctype.expl_info.next_attach : o.attached_obj;
  2353.         assert(Objects.vcptr(update_attach) == &sub);
  2354.         update_attach = sub.ctype.expl_info.next_attach;
  2355.  
  2356.         sub.ctype.expl_info.next_attach = object_none;
  2357.         sub.flags &= ~OF_ATTACHED;
  2358.  
  2359. }
  2360.  
  2361. //dettaches all objects from this object
  2362. static void obj_detach_all(object_array &Objects, object_base &parent)
  2363. {
  2364.         while (parent.attached_obj != object_none)
  2365.                 obj_detach_one(Objects, Objects.vmptr(parent.attached_obj));
  2366. }
  2367.  
  2368. #if defined(DXX_BUILD_DESCENT_II)
  2369. //creates a marker object in the world.  returns the object number
  2370. imobjptridx_t drop_marker_object(const vms_vector &pos, const vmsegptridx_t segnum, const vms_matrix &orient, const game_marker_index marker_num)
  2371. {
  2372.         Assert(Marker_model_num != -1);
  2373.         auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  2374.         const movement_type_t movement_type =
  2375.                 ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP) && Netgame.Allow_marker_view)
  2376.                 ? MT_NONE
  2377.                 : MT_SPINNING;
  2378.         const auto &&obj = obj_create(OBJ_MARKER, static_cast<unsigned>(marker_num), segnum, pos, &orient, Polygon_models[Marker_model_num].rad, CT_NONE, movement_type, RT_POLYOBJ);
  2379.         if (obj != object_none) {
  2380.                 auto &o = *obj;
  2381.                 o.rtype.pobj_info.model_num = Marker_model_num;
  2382.  
  2383.                 if (movement_type == MT_SPINNING)
  2384.                 {
  2385.                 constexpr fix scale = F1_0 / 2;
  2386.                 const auto oi = obj.get_unchecked_index();
  2387.                 auto &spin_vec = o.mtype.spin_rate;
  2388.                 spin_vec = {};
  2389.                 if (oi & 1)
  2390.                         vm_vec_scale_add2(spin_vec, o.orient.fvec, (oi & 8) ? scale : -scale);
  2391.                 if (oi & 2)
  2392.                         vm_vec_scale_add2(spin_vec, o.orient.uvec, (oi & 16) ? scale : -scale);
  2393.                 if (oi & 4)
  2394.                         vm_vec_scale_add2(spin_vec, o.orient.rvec, (oi & 32) ? scale : -scale);
  2395.                 }
  2396.  
  2397.                 //      MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
  2398.                 o.lifeleft = IMMORTAL_TIME - 1;
  2399.         }
  2400.         return obj;    
  2401. }
  2402.  
  2403. //      *viewer is a viewer, probably a missile.
  2404. //      wake up all robots that were rendered last frame subject to some constraints.
  2405. void wake_up_rendered_objects(const object &viewer, window_rendered_data &window)
  2406. {
  2407.         auto &Objects = LevelUniqueObjectState.Objects;
  2408.         auto &vmobjptr = Objects.vmptr;
  2409.         //      Make sure that we are processing current data.
  2410.         if (timer_query() != window.time) {
  2411.                 return;
  2412.         }
  2413.  
  2414.         Ai_last_missile_camera = &viewer;
  2415.  
  2416.         range_for (const auto objnum, window.rendered_robots)
  2417.         {
  2418.                 int     fcval = d_tick_count & 3;
  2419.                 if ((objnum & 3) == fcval) {
  2420.                         const auto &&objp = vmobjptr(objnum);
  2421.        
  2422.                         if (objp->type == OBJ_ROBOT) {
  2423.                                 if (vm_vec_dist_quick(viewer.pos, objp->pos) < F1_0*100)
  2424.                                 {
  2425.                                         ai_local                *ailp = &objp->ctype.ai_info.ail;
  2426.                                         if (ailp->player_awareness_type == player_awareness_type_t::PA_NONE) {
  2427.                                                 objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
  2428.                                                 ailp->player_awareness_type = player_awareness_type_t::PA_WEAPON_ROBOT_COLLISION;
  2429.                                                 ailp->player_awareness_time = F1_0*3;
  2430.                                                 ailp->previous_visibility = player_visibility_state::visible_and_in_field_of_view;
  2431.                                         }
  2432.                                 }
  2433.                         }
  2434.                 }
  2435.         }
  2436. }
  2437. #endif
  2438.  
  2439. // Swap endianess of given object_rw if swap == 1
  2440. void object_rw_swap(object_rw *obj, int swap)
  2441. {
  2442.         if (!swap)
  2443.                 return;
  2444.  
  2445.         obj->signature     = SWAPINT(obj->signature);
  2446.         obj->next          = SWAPSHORT(obj->next);
  2447.         obj->prev          = SWAPSHORT(obj->prev);
  2448.         obj->segnum        = SWAPSHORT(obj->segnum);
  2449.         obj->attached_obj  = SWAPSHORT(obj->attached_obj);
  2450.         obj->pos.x         = SWAPINT(obj->pos.x);
  2451.         obj->pos.y         = SWAPINT(obj->pos.y);
  2452.         obj->pos.z         = SWAPINT(obj->pos.z);
  2453.         obj->orient.rvec.x = SWAPINT(obj->orient.rvec.x);
  2454.         obj->orient.rvec.y = SWAPINT(obj->orient.rvec.y);
  2455.         obj->orient.rvec.z = SWAPINT(obj->orient.rvec.z);
  2456.         obj->orient.fvec.x = SWAPINT(obj->orient.fvec.x);
  2457.         obj->orient.fvec.y = SWAPINT(obj->orient.fvec.y);
  2458.         obj->orient.fvec.z = SWAPINT(obj->orient.fvec.z);
  2459.         obj->orient.uvec.x = SWAPINT(obj->orient.uvec.x);
  2460.         obj->orient.uvec.y = SWAPINT(obj->orient.uvec.y);
  2461.         obj->orient.uvec.z = SWAPINT(obj->orient.uvec.z);
  2462.         obj->size          = SWAPINT(obj->size);
  2463.         obj->shields       = SWAPINT(obj->shields);
  2464.         obj->last_pos.x    = SWAPINT(obj->last_pos.x);
  2465.         obj->last_pos.y    = SWAPINT(obj->last_pos.y);
  2466.         obj->last_pos.z    = SWAPINT(obj->last_pos.z);
  2467.         obj->lifeleft      = SWAPINT(obj->lifeleft);
  2468.        
  2469.         switch (obj->movement_type)
  2470.         {
  2471.                 case MT_PHYSICS:
  2472.                         obj->mtype.phys_info.velocity.x  = SWAPINT(obj->mtype.phys_info.velocity.x);
  2473.                         obj->mtype.phys_info.velocity.y  = SWAPINT(obj->mtype.phys_info.velocity.y);
  2474.                         obj->mtype.phys_info.velocity.z  = SWAPINT(obj->mtype.phys_info.velocity.z);
  2475.                         obj->mtype.phys_info.thrust.x    = SWAPINT(obj->mtype.phys_info.thrust.x);
  2476.                         obj->mtype.phys_info.thrust.y    = SWAPINT(obj->mtype.phys_info.thrust.y);
  2477.                         obj->mtype.phys_info.thrust.z    = SWAPINT(obj->mtype.phys_info.thrust.z);
  2478.                         obj->mtype.phys_info.mass        = SWAPINT(obj->mtype.phys_info.mass);
  2479.                         obj->mtype.phys_info.drag        = SWAPINT(obj->mtype.phys_info.drag);
  2480.                         obj->mtype.phys_info.rotvel.x    = SWAPINT(obj->mtype.phys_info.rotvel.x);
  2481.                         obj->mtype.phys_info.rotvel.y    = SWAPINT(obj->mtype.phys_info.rotvel.y);
  2482.                         obj->mtype.phys_info.rotvel.z    = SWAPINT(obj->mtype.phys_info.rotvel.z);
  2483.                         obj->mtype.phys_info.rotthrust.x = SWAPINT(obj->mtype.phys_info.rotthrust.x);
  2484.                         obj->mtype.phys_info.rotthrust.y = SWAPINT(obj->mtype.phys_info.rotthrust.y);
  2485.                         obj->mtype.phys_info.rotthrust.z = SWAPINT(obj->mtype.phys_info.rotthrust.z);
  2486.                         obj->mtype.phys_info.turnroll    = SWAPINT(obj->mtype.phys_info.turnroll);
  2487.                         obj->mtype.phys_info.flags       = SWAPSHORT(obj->mtype.phys_info.flags);
  2488.                         break;
  2489.                        
  2490.                 case MT_SPINNING:
  2491.                         obj->mtype.spin_rate.x = SWAPINT(obj->mtype.spin_rate.x);
  2492.                         obj->mtype.spin_rate.y = SWAPINT(obj->mtype.spin_rate.y);
  2493.                         obj->mtype.spin_rate.z = SWAPINT(obj->mtype.spin_rate.z);
  2494.                         break;
  2495.         }
  2496.        
  2497.         switch (obj->control_type)
  2498.         {
  2499.                 case CT_WEAPON:
  2500.                         obj->ctype.laser_info.parent_type      = SWAPSHORT(obj->ctype.laser_info.parent_type);
  2501.                         obj->ctype.laser_info.parent_num       = SWAPSHORT(obj->ctype.laser_info.parent_num);
  2502.                         obj->ctype.laser_info.parent_signature = SWAPINT(obj->ctype.laser_info.parent_signature);
  2503.                         obj->ctype.laser_info.creation_time    = SWAPINT(obj->ctype.laser_info.creation_time);
  2504.                         obj->ctype.laser_info.last_hitobj      = SWAPSHORT(obj->ctype.laser_info.last_hitobj);
  2505.                         obj->ctype.laser_info.track_goal       = SWAPSHORT(obj->ctype.laser_info.track_goal);
  2506.                         obj->ctype.laser_info.multiplier       = SWAPINT(obj->ctype.laser_info.multiplier);
  2507.                         break;
  2508.                        
  2509.                 case CT_EXPLOSION:
  2510.                         obj->ctype.expl_info.spawn_time    = SWAPINT(obj->ctype.expl_info.spawn_time);
  2511.                         obj->ctype.expl_info.delete_time   = SWAPINT(obj->ctype.expl_info.delete_time);
  2512.                         obj->ctype.expl_info.delete_objnum = SWAPSHORT(obj->ctype.expl_info.delete_objnum);
  2513.                         obj->ctype.expl_info.attach_parent = SWAPSHORT(obj->ctype.expl_info.attach_parent);
  2514.                         obj->ctype.expl_info.prev_attach   = SWAPSHORT(obj->ctype.expl_info.prev_attach);
  2515.                         obj->ctype.expl_info.next_attach   = SWAPSHORT(obj->ctype.expl_info.next_attach);
  2516.                         break;
  2517.                        
  2518.                 case CT_AI:
  2519.                         obj->ctype.ai_info.hide_segment           = SWAPSHORT(obj->ctype.ai_info.hide_segment);
  2520.                         obj->ctype.ai_info.hide_index             = SWAPSHORT(obj->ctype.ai_info.hide_index);
  2521.                         obj->ctype.ai_info.path_length            = SWAPSHORT(obj->ctype.ai_info.path_length);
  2522. #if defined(DXX_BUILD_DESCENT_I)
  2523.                         obj->ctype.ai_info.cur_path_index         = SWAPSHORT(obj->ctype.ai_info.cur_path_index);
  2524. #elif defined(DXX_BUILD_DESCENT_II)
  2525.                         obj->ctype.ai_info.dying_start_time       = SWAPINT(obj->ctype.ai_info.dying_start_time);
  2526. #endif
  2527.                         obj->ctype.ai_info.danger_laser_num       = SWAPSHORT(obj->ctype.ai_info.danger_laser_num);
  2528.                         obj->ctype.ai_info.danger_laser_signature = SWAPINT(obj->ctype.ai_info.danger_laser_signature);
  2529.                         break;
  2530.                        
  2531.                 case CT_LIGHT:
  2532.                         obj->ctype.light_info.intensity = SWAPINT(obj->ctype.light_info.intensity);
  2533.                         break;
  2534.                        
  2535.                 case CT_POWERUP:
  2536.                         obj->ctype.powerup_info.count         = SWAPINT(obj->ctype.powerup_info.count);
  2537. #if defined(DXX_BUILD_DESCENT_II)
  2538.                         obj->ctype.powerup_info.creation_time = SWAPINT(obj->ctype.powerup_info.creation_time);
  2539.                         obj->ctype.powerup_info.flags         = SWAPINT(obj->ctype.powerup_info.flags);
  2540. #endif
  2541.                         break;
  2542.         }
  2543.        
  2544.         switch (obj->render_type)
  2545.         {
  2546.                 case RT_MORPH:
  2547.                 case RT_POLYOBJ:
  2548.                 case RT_NONE: // HACK below
  2549.                 {
  2550.                         if (obj->render_type == RT_NONE && obj->type != OBJ_GHOST) // HACK: when a player is dead or not connected yet, clients still expect to get polyobj data - even if render_type == RT_NONE at this time.
  2551.                                 break;
  2552.                         obj->rtype.pobj_info.model_num                = SWAPINT(obj->rtype.pobj_info.model_num);
  2553.                         for (uint_fast32_t i=0;i<MAX_SUBMODELS;i++)
  2554.                         {
  2555.                                 obj->rtype.pobj_info.anim_angles[i].p = SWAPINT(obj->rtype.pobj_info.anim_angles[i].p);
  2556.                                 obj->rtype.pobj_info.anim_angles[i].b = SWAPINT(obj->rtype.pobj_info.anim_angles[i].b);
  2557.                                 obj->rtype.pobj_info.anim_angles[i].h = SWAPINT(obj->rtype.pobj_info.anim_angles[i].h);
  2558.                         }
  2559.                         obj->rtype.pobj_info.subobj_flags             = SWAPINT(obj->rtype.pobj_info.subobj_flags);
  2560.                         obj->rtype.pobj_info.tmap_override            = SWAPINT(obj->rtype.pobj_info.tmap_override);
  2561.                         obj->rtype.pobj_info.alt_textures             = SWAPINT(obj->rtype.pobj_info.alt_textures);
  2562.                         break;
  2563.                 }
  2564.                        
  2565.                 case RT_WEAPON_VCLIP:
  2566.                 case RT_HOSTAGE:
  2567.                 case RT_POWERUP:
  2568.                 case RT_FIREBALL:
  2569.                         obj->rtype.vclip_info.vclip_num = SWAPINT(obj->rtype.vclip_info.vclip_num);
  2570.                         obj->rtype.vclip_info.frametime = SWAPINT(obj->rtype.vclip_info.frametime);
  2571.                         break;
  2572.                        
  2573.                 case RT_LASER:
  2574.                         break;
  2575.                        
  2576.         }
  2577. }
  2578.  
  2579. }
  2580.  
  2581. namespace dcx {
  2582.  
  2583. void (check_warn_object_type)(const object_base &o, object_type_t t, const char *file, unsigned line)
  2584. {
  2585.         if (o.type != t)
  2586.                 con_printf(CON_URGENT, "%s:%u: BUG: object %p has type %u, expected %u", file, line, &o, o.type, t);
  2587. }
  2588.  
  2589. }
  2590.