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