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.  * Dialog box to edit robot properties.
  23.  *
  24.  */
  25.  
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <math.h>
  29. #include <string.h>
  30.  
  31. #include "screens.h"
  32. #include "inferno.h"
  33. #include "segment.h"
  34. #include "event.h"
  35. #include "editor.h"
  36. #include "editor/esegment.h"
  37. #include "editor/medmisc.h"
  38. #include "timer.h"
  39. #include "objpage.h"
  40. #include "maths.h"
  41. #include "dxxerror.h"
  42. #include "kdefs.h"
  43. #include        "object.h"
  44. #include "robot.h"
  45. #include "game.h"
  46. #include "powerup.h"
  47. #include "ai.h"
  48. #include "hostage.h"
  49. #include "eobject.h"
  50. #include "medwall.h"
  51. #include "medrobot.h"
  52. #include "eswitch.h"
  53. #include "ehostage.h"
  54. #include "key.h"
  55. #include "centers.h"
  56. #include "bm.h"
  57. #include "u_mem.h"
  58.  
  59. #include "compiler-range_for.h"
  60. #include "d_enumerate.h"
  61. #include <memory>
  62.  
  63. static int GoodyNextID();
  64. static int GoodyPrevID();
  65.  
  66. //-------------------------------------------------------------------------
  67. // Variables for this module...
  68. //-------------------------------------------------------------------------
  69. static UI_DIALOG                                *MainWindow = NULL;
  70.  
  71. namespace {
  72.  
  73. struct robot_dialog
  74. {
  75.         std::unique_ptr<UI_GADGET_USERBOX> robotViewBox, containsViewBox;
  76.         std::unique_ptr<UI_GADGET_BUTTON> quitButton, prev_powerup_type, next_powerup_type, prev_powerup_id, next_powerup_id, prev_powerup_count, next_powerup_count, prev_robot_type, next_robot_type, next_segment, prev_object, next_object, delete_object, new_object, set_path;
  77.         std::array<std::unique_ptr<UI_GADGET_RADIO>, 6> initialMode;                    //      Number of boxes, AI modes
  78.         fix64 time;
  79.         vms_angvec angles, goody_angles;
  80.         int old_object;
  81. };
  82.  
  83. }
  84.  
  85. namespace dsx {
  86. static window_event_result robot_dialog_handler(UI_DIALOG *dlg,const d_event &event, robot_dialog *r);
  87.  
  88. }
  89. static void call_init_ai_object(vmobjptridx_t objp, ai_behavior behavior)
  90. {
  91.         segnum_t        hide_segment;
  92.  
  93.         if (behavior == ai_behavior::AIB_STATION)
  94.                 hide_segment = Cursegp;
  95.         else {
  96.                 if (Markedsegp != segment_none)
  97.                         hide_segment = Markedsegp;
  98.                 else
  99.                         hide_segment = Cursegp;
  100.         }
  101.  
  102.         init_ai_object(objp, behavior, hide_segment);
  103. }
  104.  
  105. //-------------------------------------------------------------------------
  106. // Called when user presses "Next Type" button.  This only works for polygon
  107. // objects and it just selects the next polygon model for the current object.
  108. //-------------------------------------------------------------------------
  109. static int RobotNextType()
  110. {
  111.         auto &Objects = LevelUniqueObjectState.Objects;
  112.         auto &vmobjptridx = Objects.vmptridx;
  113.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  114.         if (Cur_object_index != object_none )   {
  115.                 const auto &&obj = vmobjptridx(Cur_object_index);
  116.                 if (obj->type == OBJ_ROBOT)
  117.                 {
  118.                         obj->id++;
  119.                         if (obj->id >= LevelSharedRobotInfoState.N_robot_types)
  120.                                 obj->id = 0;
  121.  
  122.                         //Set polygon-object-specific data
  123.                         obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
  124.                         obj->rtype.pobj_info.subobj_flags = 0;
  125.                         //set Physics info
  126.                         obj->mtype.phys_info.flags |= (PF_LEVELLING);
  127.                         obj->shields = Robot_info[get_robot_id(obj)].strength;
  128.                         call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
  129.  
  130.                         Cur_object_id = get_robot_id(obj);
  131.                 }
  132.         }
  133.         Update_flags |= UF_WORLD_CHANGED;
  134.         return 1;
  135. }
  136.  
  137. //-------------------------------------------------------------------------
  138. // Called when user presses "Prev Type" button.  This only works for polygon
  139. // objects and it just selects the prev polygon model for the current object.
  140. //-------------------------------------------------------------------------
  141. static int RobotPrevType()
  142. {
  143.         auto &Objects = LevelUniqueObjectState.Objects;
  144.         auto &vmobjptridx = Objects.vmptridx;
  145.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  146.         if (Cur_object_index != object_none )   {
  147.                 const auto &&obj = vmobjptridx(Cur_object_index);
  148.                 if (obj->type == OBJ_ROBOT)
  149.                 {
  150.                         if (obj->id == 0 )
  151.                                 obj->id = LevelSharedRobotInfoState.N_robot_types - 1;
  152.                         else
  153.                                 obj->id--;
  154.  
  155.                         //Set polygon-object-specific data
  156.                         obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
  157.                         obj->rtype.pobj_info.subobj_flags = 0;
  158.                         //set Physics info
  159.                         obj->mtype.phys_info.flags |= (PF_LEVELLING);
  160.                         obj->shields = Robot_info[get_robot_id(obj)].strength;
  161.                         call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
  162.  
  163.                         Cur_object_id = get_robot_id(obj);
  164.                 }
  165.         }
  166.         Update_flags |= UF_WORLD_CHANGED;
  167.         return 1;
  168. }
  169.  
  170. //-------------------------------------------------------------------------
  171. // Dummy function for Mike to write.
  172. //-------------------------------------------------------------------------
  173. static int med_set_ai_path()
  174. {
  175.         return 1;
  176. }
  177.  
  178. // #define OBJ_NONE             255     //unused object
  179. // #define OBJ_WALL             0               //A wall... not really an object, but used for collisions
  180. // #define OBJ_FIREBALL 1               //a fireball, part of an explosion
  181. // #define OBJ_ROBOT            2               //an evil enemy
  182. // #define OBJ_HOSTAGE  3               //a hostage you need to rescue
  183. // #define OBJ_PLAYER   4               //the player on the console
  184. // #define OBJ_WEAPON   5               //a laser, missile, etc
  185. // #define OBJ_CAMERA   6               //a camera to slew around with
  186. // #define OBJ_POWERUP  7               //a powerup you can pick up
  187. // #define OBJ_DEBRIS   8               //a piece of robot
  188. // #define OBJ_CNTRLCEN 9               //the control center
  189. // #define OBJ_FLARE            10              //the control center
  190. // #define MAX_OBJECT_TYPES     11
  191.  
  192.  
  193. #define GOODY_TYPE_MAX  MAX_OBJECT_TYPES
  194. #define GOODY_X 6
  195. #define GOODY_Y 132
  196.  
  197. //#define       GOODY_ID_MAX_ROBOT      6
  198. //#define       GOODY_ID_MAX_POWERUP    9
  199. #define GOODY_COUNT_MAX 4
  200.  
  201. int             Cur_goody_type = OBJ_POWERUP;
  202. int             Cur_goody_id = 0;
  203. int             Cur_goody_count = 0;
  204.  
  205. static void update_goody_info(void)
  206. {
  207.         auto &Objects = LevelUniqueObjectState.Objects;
  208.         auto &vmobjptr = Objects.vmptr;
  209.         if (Cur_object_index != object_none )   {
  210.                 auto &obj = *vmobjptr(Cur_object_index);
  211.                 if (obj.type == OBJ_ROBOT)
  212.                 {
  213.                         obj.contains_type = Cur_goody_type;
  214.                         obj.contains_id = Cur_goody_id;
  215.                         obj.contains_count = Cur_goody_count;
  216.                 }
  217.         }
  218. }
  219.  
  220. // #define OBJ_WALL             0               //A wall... not really an object, but used for collisions
  221. // #define OBJ_FIREBALL 1               //a fireball, part of an explosion
  222. // #define OBJ_ROBOT            2               //an evil enemy
  223. // #define OBJ_HOSTAGE  3               //a hostage you need to rescue
  224. // #define OBJ_PLAYER   4               //the player on the console
  225. // #define OBJ_WEAPON   5               //a laser, missile, etc
  226. // #define OBJ_CAMERA   6               //a camera to slew around with
  227. // #define OBJ_POWERUP  7               //a powerup you can pick up
  228. // #define OBJ_DEBRIS   8               //a piece of robot
  229. // #define OBJ_CNTRLCEN 9               //the control center
  230. // #define OBJ_FLARE            10              //the control center
  231. // #define MAX_OBJECT_TYPES     11
  232.  
  233.  
  234. static int GoodyNextType()
  235. {
  236.         Cur_goody_type++;
  237.         while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
  238.                 if (Cur_goody_type > GOODY_TYPE_MAX)
  239.                         Cur_goody_type=0;
  240.                 else
  241.                         Cur_goody_type++;
  242.         }
  243.  
  244.         GoodyNextID();
  245.         GoodyPrevID();
  246.  
  247.         update_goody_info();
  248.         return 1;
  249. }
  250.  
  251. static int GoodyPrevType()
  252. {
  253.         Cur_goody_type--;
  254.         while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
  255.                 if (Cur_goody_type < 0)
  256.                         Cur_goody_type = GOODY_TYPE_MAX;
  257.                 else
  258.                         Cur_goody_type--;
  259.         }
  260.  
  261.         GoodyNextID();
  262.         GoodyPrevID();
  263.  
  264.         update_goody_info();
  265.         return 1;
  266. }
  267.  
  268. int GoodyNextID()
  269. {
  270.         Cur_goody_id++;
  271.         if (Cur_goody_type == OBJ_ROBOT) {
  272.                 if (Cur_goody_id >= LevelSharedRobotInfoState.N_robot_types)
  273.                         Cur_goody_id=0;
  274.         } else {
  275.                 if (Cur_goody_id >= N_powerup_types)
  276.                         Cur_goody_id=0;
  277.         }
  278.  
  279.         update_goody_info();
  280.         return 1;
  281. }
  282.  
  283. int GoodyPrevID()
  284. {
  285.         Cur_goody_id--;
  286.         if (Cur_goody_type == OBJ_ROBOT) {
  287.                 if (Cur_goody_id < 0)
  288.                         Cur_goody_id = LevelSharedRobotInfoState.N_robot_types - 1;
  289.         } else {
  290.                 if (Cur_goody_id < 0)
  291.                         Cur_goody_id = N_powerup_types-1;
  292.         }
  293.  
  294.         update_goody_info();
  295.         return 1;
  296. }
  297.  
  298. static int GoodyNextCount()
  299. {
  300.         Cur_goody_count++;
  301.         if (Cur_goody_count > GOODY_COUNT_MAX)
  302.                 Cur_goody_count=0;
  303.  
  304.         update_goody_info();
  305.         return 1;
  306. }
  307.  
  308. static int GoodyPrevCount()
  309. {
  310.         Cur_goody_count--;
  311.         if (Cur_goody_count < 0)
  312.                 Cur_goody_count=GOODY_COUNT_MAX;
  313.  
  314.         update_goody_info();
  315.         return 1;
  316. }
  317.  
  318. static int is_legal_type(int the_type)
  319. {
  320.         return (the_type == OBJ_ROBOT) || (the_type == OBJ_CLUTTER);
  321. }
  322.  
  323. static int is_legal_type_for_this_window(const imobjidx_t objnum)
  324. {
  325.         auto &Objects = LevelUniqueObjectState.Objects;
  326.         auto &vmobjptr = Objects.vmptr;
  327.         if (objnum == object_none)
  328.                 return 1;
  329.         else
  330.                 return is_legal_type(vmobjptr(objnum)->type);
  331. }
  332.  
  333. static int LocalObjectSelectNextinSegment(void)
  334. {
  335.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  336.         auto &Objects = LevelUniqueObjectState.Objects;
  337.         auto &Vertices = LevelSharedVertexState.get_vertices();
  338.         auto &vmobjptr = Objects.vmptr;
  339.         int     rval, first_obj;
  340.  
  341.         rval = ObjectSelectNextinSegment();
  342.         first_obj = Cur_object_index;
  343.  
  344.         if (Cur_object_index != object_none) {
  345.                 while (!is_legal_type_for_this_window(Cur_object_index)) {
  346.                         rval = ObjectSelectNextinSegment();
  347.                         if (first_obj == Cur_object_index)
  348.                                 break;
  349.                 }
  350.  
  351.                 const auto &&objp = vmobjptr(Cur_object_index);
  352.                 Cur_goody_type = objp->contains_type;
  353.                 Cur_goody_id = objp->contains_id;
  354.                 if (objp->contains_count < 0)
  355.                         objp->contains_count = 0;
  356.                 Cur_goody_count = objp->contains_count;
  357.         }
  358.  
  359.         if (Cur_object_index != first_obj)
  360.         {
  361.                 auto &vcvertptr = Vertices.vcptr;
  362.                 set_view_target_from_segment(vcvertptr, Cursegp);
  363.         }
  364.  
  365.         return rval;
  366. }
  367.  
  368. static int LocalObjectSelectNextinMine(void)
  369. {
  370.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  371.         auto &Objects = LevelUniqueObjectState.Objects;
  372.         auto &Vertices = LevelSharedVertexState.get_vertices();
  373.         auto &vmobjptr = Objects.vmptr;
  374.         int     rval, first_obj;
  375.  
  376.         rval = ObjectSelectNextInMine();
  377.  
  378.         first_obj = Cur_object_index;
  379.  
  380.         if (Cur_object_index != object_none) {
  381.                 while (!is_legal_type_for_this_window(Cur_object_index)) {
  382.                         ObjectSelectNextInMine();
  383.                         if (Cur_object_index == first_obj)
  384.                                 break;
  385.                 }
  386.  
  387.                 const auto &&objp = vmobjptr(Cur_object_index);
  388.                 Cur_goody_type = objp->contains_type;
  389.                 Cur_goody_id = objp->contains_id;
  390.                 if (objp->contains_count < 0)
  391.                         objp->contains_count = 0;
  392.                 Cur_goody_count = objp->contains_count;
  393.         }
  394.  
  395.         if (Cur_object_index != first_obj)
  396.         {
  397.                 auto &vcvertptr = Vertices.vcptr;
  398.                 set_view_target_from_segment(vcvertptr, Cursegp);
  399.         }
  400.  
  401.         return rval;
  402. }
  403.  
  404. static int LocalObjectSelectPrevinMine(void)
  405. {
  406.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  407.         auto &Objects = LevelUniqueObjectState.Objects;
  408.         auto &Vertices = LevelSharedVertexState.get_vertices();
  409.         auto &vmobjptr = Objects.vmptr;
  410.         int     rval, first_obj;
  411.  
  412.         rval = ObjectSelectPrevInMine();
  413.  
  414.         first_obj = Cur_object_index;
  415.  
  416.         if (Cur_object_index != object_none) {
  417.                 while (!is_legal_type_for_this_window(Cur_object_index)) {
  418.                         ObjectSelectPrevInMine();
  419.                         if (first_obj == Cur_object_index)
  420.                                 break;
  421.                 }
  422.  
  423.                 const auto &&objp = vmobjptr(Cur_object_index);
  424.                 Cur_goody_type = objp->contains_type;
  425.                 Cur_goody_id = objp->contains_id;
  426.                 if (objp->contains_count < 0)
  427.                         objp->contains_count = 0;
  428.                 Cur_goody_count = objp->contains_count;
  429.         }
  430.  
  431.         if (Cur_object_index != first_obj)
  432.         {
  433.                 auto &vcvertptr = Vertices.vcptr;
  434.                 set_view_target_from_segment(vcvertptr, Cursegp);
  435.         }
  436.  
  437.         return rval;
  438. }
  439.  
  440. static int LocalObjectDelete(void)
  441. {
  442.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  443.         auto &Objects = LevelUniqueObjectState.Objects;
  444.         auto &Vertices = LevelSharedVertexState.get_vertices();
  445.         auto &vcobjptr = Objects.vcptr;
  446.         int     rval;
  447.  
  448.         rval = ObjectDelete();
  449.  
  450.         if (Cur_object_index != object_none) {
  451.                 auto &objp = *vcobjptr(Cur_object_index);
  452.                 Cur_goody_type = objp.contains_type;
  453.                 Cur_goody_id = objp.contains_id;
  454.                 Cur_goody_count = objp.contains_count;
  455.         }
  456.  
  457.         auto &vcvertptr = Vertices.vcptr;
  458.         set_view_target_from_segment(vcvertptr, Cursegp);
  459.  
  460.         return rval;
  461. }
  462.  
  463. static int LocalObjectPlaceObject(void)
  464. {
  465.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  466.         auto &Objects = LevelUniqueObjectState.Objects;
  467.         auto &Vertices = LevelSharedVertexState.get_vertices();
  468.         auto &vmobjptr = Objects.vmptr;
  469.         int     rval;
  470.  
  471.         Cur_goody_count = 0;
  472.  
  473.         if (Cur_object_type != OBJ_ROBOT)
  474.         {
  475.                 Cur_object_type = OBJ_ROBOT;
  476.                 Cur_object_id = 3;      // class 1 drone
  477.                 Num_object_subtypes = LevelSharedRobotInfoState.N_robot_types;
  478.         }
  479.  
  480.         rval = ObjectPlaceObject();
  481.         if (rval == -1)
  482.                 return -1;
  483.  
  484.         const auto &&objp = vmobjptr(Cur_object_index);
  485.         objp->contains_type = Cur_goody_type;
  486.         objp->contains_id = Cur_goody_id;
  487.         objp->contains_count = Cur_goody_count;
  488.  
  489.         auto &vcvertptr = Vertices.vcptr;
  490.         set_view_target_from_segment(vcvertptr, Cursegp);
  491.  
  492.         return rval;
  493. }
  494.  
  495. void close_all_windows(void)
  496. {
  497.         close_trigger_window();
  498.         close_wall_window();
  499.         close_centers_window();
  500.         hostage_close_window();
  501.         robot_close_window();
  502. }
  503.  
  504.  
  505. //-------------------------------------------------------------------------
  506. // Called from the editor... does one instance of the robot dialog box
  507. //-------------------------------------------------------------------------
  508. int do_robot_dialog()
  509. {
  510.         // Only open 1 instance of this window...
  511.         if ( MainWindow != NULL ) return 0;
  512.        
  513.         auto r = std::make_unique<robot_dialog>();
  514.         // Close other windows
  515.         close_all_windows();
  516.         Cur_goody_count = 0;
  517.         memset(&r->angles, 0, sizeof(vms_angvec));
  518.         memset(&r->goody_angles, 0, sizeof(vms_angvec));
  519.  
  520.         // Open a window with a quit button
  521.         MainWindow = ui_create_dialog(TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, robot_dialog_handler, std::move(r));
  522.         return 1;
  523. }
  524.  
  525. static window_event_result robot_dialog_created(UI_DIALOG *const w, robot_dialog *const r)
  526. {
  527.         r->quitButton = ui_add_gadget_button(w, 20, 286, 40, 32, "Done", NULL);
  528.         r->prev_powerup_type = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y-3, 25, 22, "<<", GoodyPrevType);
  529.         r->next_powerup_type = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y-3, 25, 22, ">>", GoodyNextType);
  530.         r->prev_powerup_id = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y+21, 25, 22, "<<", GoodyPrevID);
  531.         r->next_powerup_id = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y+21, 25, 22, ">>", GoodyNextID);
  532.         r->prev_powerup_count = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y+45, 25, 22, "<<", GoodyPrevCount);
  533.         r->next_powerup_count = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y+45, 25, 22, ">>", GoodyNextCount);
  534.         r->initialMode[0] = ui_add_gadget_radio(w,  6, 58, 16, 16, 0, "Hover");
  535.         r->initialMode[1] = ui_add_gadget_radio(w, 76, 58, 16, 16, 0, "Normal");
  536.         r->initialMode[2] = ui_add_gadget_radio(w,  6, 78, 16, 16, 0, "(hide)");
  537.         r->initialMode[3] = ui_add_gadget_radio(w, 76, 78, 16, 16, 0, "Avoid");
  538.         r->initialMode[4] = ui_add_gadget_radio(w,  6, 98, 16, 16, 0, "Follow");
  539.         r->initialMode[5] = ui_add_gadget_radio(w, 76, 98, 16, 16, 0, "Station");
  540.         // The little box the robots will spin in.
  541.         r->robotViewBox = ui_add_gadget_userbox(w, 155, 5, 150, 125);
  542.         // The little box the robots will spin in.
  543.         r->containsViewBox = ui_add_gadget_userbox(w, 10, 202, 100, 80);
  544.         // A bunch of buttons...
  545.         int i = 135;
  546.         r->prev_robot_type = ui_add_gadget_button(w, 190, i, 53, 26, "<<Typ",                   RobotPrevType);
  547.         r->next_robot_type = ui_add_gadget_button(w, 247, i, 53, 26, "Typ>>",                   RobotNextType);                                                 i += 29;               
  548.         r->next_segment = ui_add_gadget_button(w, 190, i, 110, 26, "Next in Seg", LocalObjectSelectNextinSegment);      i += 29;               
  549.         r->prev_object = ui_add_gadget_button(w, 190, i, 53, 26, "<<Obj",                       LocalObjectSelectPrevinMine);
  550.         r->next_object = ui_add_gadget_button(w, 247, i, 53, 26, ">>Obj",                       LocalObjectSelectNextinMine);           i += 29;               
  551.         r->delete_object = ui_add_gadget_button(w, 190, i, 110, 26, "Delete",           LocalObjectDelete);                                             i += 29;               
  552.         r->new_object = ui_add_gadget_button(w, 190, i, 110, 26, "Create New",  LocalObjectPlaceObject);                                i += 29;               
  553.         r->set_path = ui_add_gadget_button(w, 190, i, 110, 26, "Set Path",      med_set_ai_path);
  554.         r->time = timer_query();
  555.         r->old_object = -2;             // Set to some dummy value so everything works ok on the first frame.
  556.         if ( Cur_object_index == object_none)
  557.                 LocalObjectSelectNextinMine();
  558.         return window_event_result::handled;
  559. }
  560.  
  561. void robot_close_window()
  562. {
  563.         if ( MainWindow!=NULL ) {
  564.                 ui_close_dialog( MainWindow );
  565.                 MainWindow = NULL;
  566.         }
  567.  
  568. }
  569.  
  570. namespace dsx {
  571. window_event_result robot_dialog_handler(UI_DIALOG *dlg,const d_event &event, robot_dialog *r)
  572. {
  573.         auto &Objects = LevelUniqueObjectState.Objects;
  574.         auto &vcobjptr = Objects.vcptr;
  575.         auto &vmobjptr = Objects.vmptr;
  576.         auto &vmobjptridx = Objects.vmptridx;
  577.         switch(event.type)
  578.         {
  579.                 case EVENT_WINDOW_CREATED:
  580.                         return robot_dialog_created(dlg, r);
  581.                 case EVENT_WINDOW_CLOSE:
  582.                         std::default_delete<robot_dialog>()(r);
  583.                         MainWindow = NULL;
  584.                         return window_event_result::ignored;
  585.                 default:
  586.                         break;
  587.         }
  588.         fix     DeltaTime;
  589.         fix64   Temp;
  590.         int     first_object_index;
  591.         int keypress = 0;
  592.         window_event_result rval = window_event_result::ignored;
  593.        
  594.         if (event.type == EVENT_KEY_COMMAND)
  595.                 keypress = event_key_get(event);
  596.                
  597.         Assert(MainWindow != NULL);
  598.  
  599.         first_object_index = Cur_object_index;
  600.         while (!is_legal_type_for_this_window(Cur_object_index)) {
  601.                 LocalObjectSelectNextinMine();
  602.                 if (first_object_index == Cur_object_index) {
  603.                         break;
  604.                 }
  605.         }
  606.  
  607.         //------------------------------------------------------------
  608.         // Call the ui code..
  609.         //------------------------------------------------------------
  610.         ui_button_any_drawn = 0;
  611.  
  612.         //------------------------------------------------------------
  613.         // If we change objects, we need to reset the ui code for all
  614.         // of the radio buttons that control the ai mode.  Also makes
  615.         // the current AI mode button be flagged as pressed down.
  616.         //------------------------------------------------------------
  617.         if (r->old_object != Cur_object_index ) {
  618.                 range_for (auto &i, r->initialMode)
  619.                         ui_radio_set_value(i.get(), 0);
  620.                 if ( Cur_object_index != object_none ) {
  621.                         auto &behavior = vmobjptr(Cur_object_index)->ctype.ai_info.behavior;
  622.                         switch (behavior)
  623.                         {
  624.                                 case ai_behavior::AIB_STILL:
  625.                                 case ai_behavior::AIB_NORMAL:
  626.                                 case ai_behavior::AIB_RUN_FROM:
  627.                                 case ai_behavior::AIB_STATION:
  628. #if defined(DXX_BUILD_DESCENT_I)
  629.                                 case ai_behavior::AIB_HIDE:
  630.                                 case ai_behavior::AIB_FOLLOW_PATH:
  631. #elif defined(DXX_BUILD_DESCENT_II)
  632.                                 case ai_behavior::AIB_BEHIND:
  633.                                 case ai_behavior::AIB_SNIPE:
  634.                                 case ai_behavior::AIB_FOLLOW:
  635. #endif
  636.                                         break;
  637.                                 default:
  638.                                         behavior = ai_behavior::AIB_NORMAL;
  639.                                         break;
  640.                         }
  641.                         ui_radio_set_value(r->initialMode[static_cast<std::size_t>(behavior) - MIN_BEHAVIOR].get(), 1);
  642.                 }
  643.         }
  644.  
  645.         //------------------------------------------------------------
  646.         // If any of the radio buttons that control the mode are set, then
  647.         // update the cooresponding AI state.
  648.         //------------------------------------------------------------
  649.         range_for (auto &&eim, enumerate(r->initialMode))
  650.         {
  651.                 auto &im = eim.value;
  652.                 if (GADGET_PRESSED(im.get()))
  653.                 {
  654.                         const auto i = eim.idx;
  655.                         const auto b = static_cast<ai_behavior>(MIN_BEHAVIOR + i);
  656.                         const auto &&objp = vmobjptridx(Cur_object_index);
  657.                         auto &behavior = objp->ctype.ai_info.behavior;
  658.                         if (behavior != b) {
  659.                                 behavior = b;           // Set the ai_state to the cooresponding radio button
  660.                                 call_init_ai_object(objp, b);
  661.                                 rval = window_event_result::handled;
  662.                         }
  663.                 }
  664.         }
  665.  
  666.         //------------------------------------------------------------
  667.         // Redraw the object in the little 64x64 box
  668.         //------------------------------------------------------------
  669.         if (event.type == EVENT_UI_DIALOG_DRAW)
  670.         {
  671.                 // A simple frame time counter for spinning the objects...
  672.                 Temp = timer_query();
  673.                 DeltaTime = Temp - r->time;
  674.                 r->time = Temp;
  675.  
  676.                 if (Cur_object_index != object_none )   {
  677.                         const auto &&obj = vmobjptr(Cur_object_index);
  678.  
  679.                         gr_set_current_canvas( r->robotViewBox->canvas );
  680.                         draw_object_picture(*grd_curcanv, obj->id, r->angles, obj->type);
  681.                         r->angles.h += fixmul(0x1000, DeltaTime );
  682.                 } else {
  683.                         // no object, so just blank out
  684.                         gr_set_current_canvas( r->robotViewBox->canvas );
  685.                         gr_clear_canvas(*grd_curcanv, CGREY);
  686.  
  687.         //              LocalObjectSelectNextInMine();
  688.                 }
  689.  
  690.         //------------------------------------------------------------
  691.         // Redraw the contained object in the other little box
  692.         //------------------------------------------------------------
  693.                 if ((Cur_object_index != object_none ) && (Cur_goody_count > 0))        {
  694.                         gr_set_current_canvas( r->containsViewBox->canvas );
  695.                         if ( Cur_goody_id > -1 )
  696.                                 draw_object_picture(*grd_curcanv, Cur_goody_id, r->goody_angles, Cur_goody_type);
  697.                         else
  698.                                 gr_clear_canvas(*grd_curcanv, CGREY);
  699.                         r->goody_angles.h += fixmul(0x1000, DeltaTime );
  700.                 } else {
  701.                         // no object, so just blank out
  702.                         gr_set_current_canvas( r->containsViewBox->canvas );
  703.                         gr_clear_canvas(*grd_curcanv, CGREY);
  704.  
  705.         //              LocalObjectSelectNextInMine();
  706.                 }
  707.         //------------------------------------------------------------
  708.         // If anything changes in the ui system, redraw all the text that
  709.         // identifies this robot.
  710.         //------------------------------------------------------------
  711.  
  712.                 const char *id_text;
  713.                 const char *type_text;
  714.  
  715.                 if (Cur_object_index != object_none) {
  716.                         const auto &&obj = vmobjptr(Cur_object_index);
  717.                         Cur_goody_type = obj->contains_type;
  718.                         Cur_goody_id = obj->contains_id;
  719.                         if (obj->contains_count < 0)
  720.                                 obj->contains_count = 0;
  721.                         Cur_goody_count = obj->contains_count;
  722.                 }
  723.  
  724.                 ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y,    " Type:");
  725.                 ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+24, "   ID:");
  726.                 ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+48, "Count:");
  727.  
  728.                 switch (Cur_goody_type) {
  729.                         case OBJ_ROBOT:
  730.                                 type_text = "Robot  ";
  731.                                 id_text = Robot_names[Cur_goody_id].data();
  732.                                 break;
  733.                         default:
  734.                                 editor_status_fmt("Illegal contained object type (%i), changing to powerup.", Cur_goody_type);
  735.                                 Cur_goody_type = OBJ_POWERUP;
  736.                                 Cur_goody_id = 0;
  737.                                 DXX_BOOST_FALLTHROUGH;
  738.                         case OBJ_POWERUP:
  739.                                 type_text = "Powerup";
  740.                                 id_text = Powerup_names[Cur_goody_id].data();
  741.                                 break;
  742.                 }
  743.  
  744.                 ui_dputs_at( MainWindow, GOODY_X+108, GOODY_Y, type_text);
  745.                 ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+24, "%-8s", id_text);
  746.                 ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+48, "%i", Cur_goody_count);
  747.  
  748.                 if ( Cur_object_index != object_none )  {
  749.                         const auto id = get_robot_id(vcobjptr(Cur_object_index));
  750.  
  751.                         ui_dprintf_at( MainWindow, 12,  6, "Robot: %3d ", Cur_object_index );
  752.                         ui_dprintf_at( MainWindow, 12, 22, "   Id: %3d", id);
  753.                         ui_dprintf_at( MainWindow, 12, 38, " Name: %-8s", Robot_names[id].data());
  754.  
  755.                 }       else {
  756.                         ui_dprintf_at( MainWindow, 12,  6, "Robot: none" );
  757.                         ui_dprintf_at( MainWindow, 12, 22, " Type: ?  "  );
  758.                         ui_dprintf_at( MainWindow, 12, 38, " Name: ________" );
  759.                 }
  760.         }
  761.        
  762.         if (ui_button_any_drawn || (r->old_object != Cur_object_index) )
  763.                 Update_flags |= UF_WORLD_CHANGED;
  764.         if (GADGET_PRESSED(r->quitButton.get()) || keypress == KEY_ESC)
  765.         {
  766.                 return window_event_result::close;
  767.         }              
  768.  
  769.         r->old_object = Cur_object_index;
  770.        
  771.         return rval;
  772. }
  773. }
  774.  
  775. //      --------------------------------------------------------------------------------------------------------------------------
  776. #define NUM_MATT_THINGS 2
  777.  
  778. #define MATT_LEN                                20
  779.  
  780. static UI_DIALOG                                *MattWindow = NULL;
  781.  
  782. namespace {
  783.  
  784. struct object_dialog
  785. {
  786.         struct creation_context
  787.         {
  788.                 vmobjptr_t obj;
  789.                 creation_context(vmobjptr_t o) :
  790.                         obj(o)
  791.                 {
  792.                 }
  793.         };
  794.         std::unique_ptr<UI_GADGET_INPUTBOX> xtext, ytext, ztext;
  795.         std::array<std::unique_ptr<UI_GADGET_RADIO>, 2> initialMode;
  796.         std::unique_ptr<UI_GADGET_BUTTON> quitButton;
  797. };
  798.  
  799. }
  800.  
  801. static window_event_result object_dialog_handler(UI_DIALOG *dlg,const d_event &event, object_dialog *o);
  802.  
  803. void object_close_window()
  804. {
  805.         if ( MattWindow!=NULL ) {
  806.                 ui_close_dialog( MattWindow );
  807.                 MattWindow = NULL;
  808.         }
  809.  
  810. }
  811.  
  812.  
  813. //-------------------------------------------------------------------------
  814. // Called from the editor... does one instance of the object dialog box
  815. //-------------------------------------------------------------------------
  816. int do_object_dialog()
  817. {
  818.         auto &Objects = LevelUniqueObjectState.Objects;
  819.         auto &vmobjptr = Objects.vmptr;
  820.         if (Cur_object_index == object_none)
  821.                 Cur_object_index = object_first;
  822.  
  823.         auto obj = vmobjptr(Cur_object_index);
  824.         if (obj->type == OBJ_ROBOT)             //don't do this for robots
  825.                 return 0;
  826.  
  827.         // Only open 1 instance of this window...
  828.         if ( MattWindow != NULL )
  829.                 return 0;
  830.        
  831.         auto o = std::make_unique<object_dialog>();
  832.         Cur_goody_count = 0;
  833.  
  834.         // Open a window with a quit button
  835.         object_dialog::creation_context c(obj);
  836.         MattWindow = ui_create_dialog( TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, object_dialog_handler, std::move(o), &c);
  837.         return 1;
  838. }
  839.  
  840. static window_event_result object_dialog_created(UI_DIALOG *const w, object_dialog *const o, const object_dialog::creation_context *const c)
  841. {
  842.         o->quitButton = ui_add_gadget_button(w, 20, 286, 40, 32, "Done", NULL );
  843.         o->quitButton->hotkey = KEY_ENTER;
  844.         // These are the radio buttons for each mode
  845.         o->initialMode[0] = ui_add_gadget_radio(w, 10, 50, 16, 16, 0, "None" );
  846.         o->initialMode[1] = ui_add_gadget_radio(w, 80, 50, 16, 16, 0, "Spinning" );
  847.         o->initialMode[c->obj->movement_type == MT_SPINNING?1:0]->flag = 1;
  848.         char message[MATT_LEN];
  849.         snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.x));
  850.         o->xtext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 132, message);
  851.         snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.y));
  852.         o->ytext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 162, message);
  853.         snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.z));
  854.         o->ztext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 192, message);
  855.         ui_gadget_calc_keys(w);
  856.         w->keyboard_focus_gadget = o->initialMode[0].get();
  857.  
  858.         return window_event_result::handled;
  859. }
  860.  
  861. static window_event_result object_dialog_handler(UI_DIALOG *dlg,const d_event &event, object_dialog *o)
  862. {
  863.         auto &Objects = LevelUniqueObjectState.Objects;
  864.         auto &vmobjptr = Objects.vmptr;
  865.         switch(event.type)
  866.         {
  867.                 case EVENT_WINDOW_CREATED:
  868.                         return object_dialog_created(dlg, o, reinterpret_cast<const object_dialog::creation_context *>(static_cast<const d_create_event &>(event).createdata));
  869.                 case EVENT_WINDOW_CLOSE:
  870.                         std::default_delete<object_dialog>()(o);
  871.                         MattWindow = NULL;
  872.                         return window_event_result::ignored;
  873.                 default:
  874.                         break;
  875.         }
  876.         const auto &&obj = vmobjptr(Cur_object_index);
  877.         int keypress = 0;
  878.         window_event_result rval = window_event_result::ignored;
  879.        
  880.         if (event.type == EVENT_KEY_COMMAND)
  881.                 keypress = event_key_get(event);
  882.        
  883.         Assert(MattWindow != NULL);
  884.  
  885.         //------------------------------------------------------------
  886.         // Call the ui code..
  887.         //------------------------------------------------------------
  888.         ui_button_any_drawn = 0;
  889.  
  890.  
  891.         if (event.type == EVENT_UI_DIALOG_DRAW)
  892.         {
  893.                 ui_dprintf_at( MattWindow, 10, 132,"&X:" );
  894.                 ui_dprintf_at( MattWindow, 10, 162,"&Y:" );
  895.                 ui_dprintf_at( MattWindow, 10, 192,"&Z:" );
  896.         }
  897.        
  898.         if (GADGET_PRESSED(o->quitButton.get()) || keypress == KEY_ESC)
  899.         {
  900.  
  901.                 if (o->initialMode[0]->flag) obj->movement_type = MT_NONE;
  902.                 if (o->initialMode[1]->flag) obj->movement_type = MT_SPINNING;
  903.  
  904.                 obj->mtype.spin_rate.x = fl2f(atof(o->xtext->text.get()));
  905.                 obj->mtype.spin_rate.y = fl2f(atof(o->ytext->text.get()));
  906.                 obj->mtype.spin_rate.z = fl2f(atof(o->ztext->text.get()));
  907.  
  908.                 return window_event_result::close;
  909.         }
  910.        
  911.         return rval;
  912. }
  913.