home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / multibot.c < prev    next >
Text File  |  1998-06-08  |  34KB  |  1,170 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: f:/miner/source/main/rcs/multibot.c $
  15.  * $Revision: 2.2 $
  16.  * $Author: john $
  17.  * $Date: 1995/03/21 14:39:10 $
  18.  * 
  19.  * Multiplayer robot code
  20.  * 
  21.  */
  22.  
  23. #pragma off (unreferenced)
  24. static char rcsid[] = "$Id: multibot.c 2.2 1995/03/21 14:39:10 john Exp $";
  25. #pragma on (unreferenced)
  26.  
  27. #ifdef NETWORK
  28. #ifndef SHAREWARE
  29.  
  30. #include <string.h>
  31. #include <stdlib.h>
  32.  
  33. #include "vecmat.h"
  34. #include "multibot.h"
  35. #include "game.h"
  36. #include "modem.h"
  37. #include "network.h"
  38. #include "multi.h"
  39. #include "object.h"
  40. #include "laser.h"
  41. #include "error.h"
  42. #include "mono.h"
  43. #include "timer.h"
  44. #include "text.h"
  45. #include "ai.h"
  46. #include "fireball.h"
  47. #include "aistruct.h"
  48. #include "robot.h"
  49. #include "powerup.h"
  50. #include "scores.h"
  51. #include "gauges.h"
  52. #include "fuelcen.h"
  53. #include "morph.h"
  54. #include "digi.h"
  55. #include "sounds.h"
  56. #include "effects.h"
  57. #include "physics.h" 
  58.  
  59. //
  60. // Code for controlling robots in multiplayer games
  61. //
  62.  
  63. #define STANDARD_EXPL_DELAY (F1_0/4)
  64. #define MIN_CONTROL_TIME    F1_0*2
  65. #define ROBOT_TIMEOUT        F1_0*3
  66.  
  67. #define MIN_TO_ADD    60
  68. #define MAX_TO_DELETE    67
  69.  
  70. int robot_controlled[MAX_ROBOTS_CONTROLLED];
  71. int robot_agitation[MAX_ROBOTS_CONTROLLED];
  72. fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
  73. fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
  74. fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
  75. int robot_send_pending[MAX_ROBOTS_CONTROLLED];
  76. int robot_fired[MAX_ROBOTS_CONTROLLED];
  77. byte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
  78.  
  79. #define MULTI_ROBOT_PRIORITY(objnum, pnum) (((objnum % 4) + pnum) % N_players)
  80.  
  81. //#define MULTI_ROBOT_PRIORITY(objnum, pnum) multi_robot_priority(objnum, pnum)
  82. //int multi_robot_priority(int objnum, int pnum)
  83. //{
  84. //    return( ((objnum % 4) + pnum) % N_players);
  85. //}
  86.  
  87. int
  88. multi_can_move_robot(int objnum, int agitation)
  89. {
  90.     // Determine whether or not I am allowed to move this robot.
  91.     int rval;
  92.  
  93.     // Claim robot if necessary.
  94.  
  95.     if (Player_exploded)
  96.         return 0;
  97.  
  98. #ifndef NDEBUG
  99.     if ((objnum < 0) || (objnum > Highest_object_index))
  100.     {    
  101.         Int3();
  102.         rval = 0;
  103.     }
  104.  
  105.     else if (Objects[objnum].type != OBJ_ROBOT)
  106.     {
  107.         Int3();
  108.         rval = 0;
  109.     }
  110. #endif
  111.  
  112.     else if ((Robot_info[Objects[objnum].id].boss_flag) && (Boss_dying == 1))
  113.         return 0;
  114.  
  115.     else if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num) // Already my robot!
  116.     {
  117.         int slot_num = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
  118.         Assert((slot_num > -1) && (slot_num < MAX_ROBOTS_CONTROLLED));
  119.  
  120.         if (robot_fired[slot_num]) {
  121. //            mprintf((0, "Preventing robot from firing again until firing complete!\n"));
  122.             rval = 0;
  123.         }
  124.         else {
  125.             robot_agitation[slot_num] = agitation;
  126.             robot_last_message_time[slot_num] = GameTime;
  127.             rval = 1;
  128.         }
  129.     }
  130.  
  131.     else if ((Objects[objnum].ctype.ai_info.REMOTE_OWNER != -1) || (agitation < MIN_TO_ADD))
  132.     {
  133.         if (agitation == ROBOT_FIRE_AGITATION) // Special case for firing at non-player
  134.         {
  135.             mprintf((0, "Shooting anyway.\n"));
  136.             rval = 1; // Try to fire at player even tho we're not in control!
  137.         }
  138.         else
  139.             rval = 0;
  140.     }
  141.     else
  142.         rval = multi_add_controlled_robot(objnum, agitation);
  143.  
  144.     return(rval);
  145. }
  146.  
  147. void
  148. multi_check_robot_timeout(void)
  149. {
  150.     static fix lastcheck = 0;
  151.     int i;
  152.  
  153.     if (GameTime > lastcheck + F1_0)
  154.     {
  155.         lastcheck = GameTime;
  156.         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++) 
  157.         {
  158.             if ((robot_controlled[i] != -1) && (robot_last_send_time[i] + ROBOT_TIMEOUT < GameTime)) 
  159.             {
  160.                 if (Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER != Player_num)
  161.                 {        
  162.                     robot_controlled[i] = -1;
  163.                     Int3(); // Non-terminal but Rob is interesting, step over please...
  164.                     return;
  165.                 }
  166. //                Assert(Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER == Player_num);
  167.                 mprintf((0, "Robot %d (slot %d) removed, timed out, frame %d.\n", robot_controlled[i], i, GameTime));
  168.                 if (robot_send_pending[i])
  169.                     multi_send_robot_position(robot_controlled[i], 1);
  170.                 multi_send_release_robot(robot_controlled[i]);
  171. //                multi_delete_controlled_robot(robot_controlled[i]);
  172. //                robot_controlled[i] = -1;
  173.             }
  174.         }
  175.     }            
  176. }
  177.  
  178. void
  179. multi_strip_robots(int playernum)
  180. {
  181.     // Grab all robots away from a player 
  182.     // (player died or exited the game)
  183.  
  184.     int i;
  185.  
  186.     if (Game_mode & GM_MULTI_ROBOTS) {
  187.     
  188.         if (playernum == Player_num)
  189.             for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  190.                 multi_delete_controlled_robot(robot_controlled[i]);
  191.  
  192.         for (i = 1; i <= Highest_object_index; i++)
  193.             if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].ctype.ai_info.REMOTE_OWNER == playernum)) {
  194.                 Assert((Objects[i].control_type == CT_AI) || (Objects[i].control_type == CT_NONE) || (Objects[i].control_type == CT_MORPH));
  195.                 Objects[i].ctype.ai_info.REMOTE_OWNER = -1;
  196.                 if (playernum == Player_num)
  197.                     Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 4;
  198.                 else
  199.                     Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  200.               }
  201.     }
  202.     // Note -- only call this with playernum == Player_num if all other players
  203.     // already know that we are clearing house.  This does not send a release
  204.     // message for each controlled robot!!
  205.  
  206. }
  207.  
  208. void
  209. multi_dump_robots(void)
  210.  
  211. {
  212.     // Dump robot control info for debug purposes
  213.  
  214.     int i;
  215.  
  216.     if (!(Game_mode & GM_MULTI_ROBOTS))
  217.         return;
  218.  
  219.     mprintf((0, "\n"));
  220.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  221.     {
  222.         mprintf((0, "Robot %d) objnum=%d, anger=%d, last_message=%d, last move=%d, gametime=%d\n", i, robot_controlled[i], robot_agitation[i], robot_last_message_time[i], robot_last_send_time[i], GameTime));
  223.     }
  224. }
  225.  
  226. int
  227. multi_add_controlled_robot(int objnum, int agitation)
  228. {
  229.     int i;
  230.     int lowest_agitation = 0x7fffffff; // MAX POSITIVE INT
  231.     int lowest_agitated_bot = -1;
  232.     int first_free_robot = -1;
  233.  
  234.     // Try to add a new robot to the controlled list, return 1 if added, 0 if not.
  235.  
  236.     if (Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM > 0)
  237.     {
  238.         mprintf((0, "Robot %d hands-off = %d.\n", objnum, Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM));
  239.         Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM -= 1;
  240.         return 0;
  241.     }
  242.  
  243.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  244.     {
  245.         if ((robot_controlled[i] == -1) || (Objects[robot_controlled[i]].type != OBJ_ROBOT)) {
  246.             first_free_robot = i;
  247.             break;
  248.         }
  249.  
  250.         if (robot_last_message_time[i] + ROBOT_TIMEOUT < GameTime) {
  251.             mprintf((0, "Robot %d replaced (timeout).\n", robot_controlled[i]));
  252.             if (robot_send_pending[i])
  253.                 multi_send_robot_position(robot_controlled[i], 1);
  254.             multi_send_release_robot(robot_controlled[i]);
  255.             first_free_robot = i;
  256.             break;
  257.         }
  258.  
  259.         if ((robot_controlled[i] != -1) && (robot_agitation[i] < lowest_agitation) && (robot_controlled_time[i] + MIN_CONTROL_TIME < GameTime))
  260.         {
  261.             lowest_agitation = robot_agitation[i];
  262.             lowest_agitated_bot = i;
  263.         }
  264.     }
  265.  
  266.     if (first_free_robot != -1)  // Room left for new robots
  267.         i = first_free_robot;
  268.  
  269.     else if ((agitation > lowest_agitation) && (lowest_agitation <= MAX_TO_DELETE)) // Replace some old robot with a more agitated one
  270.     {
  271.         if (robot_send_pending[lowest_agitated_bot])
  272.             multi_send_robot_position(robot_controlled[lowest_agitated_bot], 1);
  273.         multi_send_release_robot(robot_controlled[lowest_agitated_bot]);
  274.  
  275.         mprintf((0, "Replaced robot %d (agitation %d) with robot %d (agitation %d).\n", robot_controlled[lowest_agitated_bot], lowest_agitation, objnum, agitation));        
  276.  
  277.         i = lowest_agitated_bot;
  278.     }
  279.     else {
  280. //        mprintf((0, "Cannot add robot %d agitation %d (min agitation %d)\n", objnum, agitation, lowest_agitation));
  281.         return(0); // Sorry, can't squeeze him in!
  282.     }
  283.  
  284.     mprintf((0, "Taking control of robot %d, agitation %d.\n", objnum, agitation));
  285.  
  286.     multi_send_claim_robot(objnum);
  287.     robot_controlled[i] = objnum;
  288.     robot_agitation[i] = agitation;
  289.     Objects[objnum].ctype.ai_info.REMOTE_OWNER = Player_num;
  290.     Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = i;
  291.     robot_controlled_time[i] = GameTime;
  292.     robot_last_send_time[i] = robot_last_message_time[i] = GameTime;
  293.     return(1);
  294. }    
  295.  
  296. void
  297. multi_delete_controlled_robot(int objnum)
  298. {
  299.     int i;
  300.  
  301.     // Delete robot object number objnum from list of controlled robots because it is dead
  302.  
  303.     if ( (objnum<0) || (objnum>Highest_object_index))    {
  304. //        mprintf((0, "Trying to releasing control of bogus robot %d\n", objnum));
  305.         return;
  306.     }
  307.  
  308.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  309.         if (robot_controlled[i] == objnum)
  310.             break;
  311.  
  312.     if (i == MAX_ROBOTS_CONTROLLED)
  313.         return;
  314.  
  315. //    mprintf((0, "Releasing control of robot %d\n", objnum));
  316.  
  317.     Assert(Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM == i);
  318.  
  319.     Objects[objnum].ctype.ai_info.REMOTE_OWNER = -1;
  320.     Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  321.     robot_controlled[i] = -1;
  322.     robot_send_pending[i] = 0;
  323.     robot_fired[i] = 0;
  324. }
  325.  
  326. void
  327. multi_send_claim_robot(int objnum)
  328. {
  329.     if ((objnum < 0) || (objnum > Highest_object_index))
  330.     {
  331.         Int3(); // See rob
  332.         return;
  333.     }
  334.  
  335.     if (Objects[objnum].type != OBJ_ROBOT)
  336.     {
  337.         Int3(); // See rob
  338.         return;
  339.     }
  340.  
  341.     // The AI tells us we should take control of this robot. 
  342.  
  343.     multibuf[0] = (char)MULTI_ROBOT_CLAIM;
  344.     multibuf[1] = Player_num;
  345.     *(short *)(multibuf+2) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[4]);
  346.  
  347.     multi_send_data(multibuf, 5, 1);
  348. }
  349.  
  350. void
  351. multi_send_release_robot(int objnum)
  352. {
  353.     if ((objnum < 0) || (objnum > Highest_object_index))
  354.     {
  355.         Int3(); // See rob
  356.         return;
  357.     }
  358.  
  359.     if (Objects[objnum].type != OBJ_ROBOT)
  360.     {
  361.         Int3(); // See rob
  362.         return;
  363.     }
  364.  
  365.     multi_delete_controlled_robot(objnum);
  366.  
  367.     multibuf[0] = (char)MULTI_ROBOT_RELEASE;
  368.     multibuf[1] = Player_num;
  369.     *(short *)(multibuf+2) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[4]);
  370.  
  371.     multi_send_data(multibuf, 5, 1);
  372. }
  373.  
  374. #define MIN_ROBOT_COM_GAP F1_0/12
  375.  
  376. int
  377. multi_send_robot_frame(int sent)
  378. {
  379.     static int last_sent = 0;
  380.  
  381.     int i;
  382.     int rval = 0;
  383.  
  384.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  385.     {
  386.         int sending = (last_sent+1+i)%MAX_ROBOTS_CONTROLLED;
  387.         if ( (robot_controlled[sending] != -1) && ((robot_send_pending[sending] > sent) || (robot_fired[sending] > sent)) )
  388.         {
  389.             if (robot_send_pending[sending])
  390.             {
  391.                 robot_send_pending[sending] = 0;    
  392.                 multi_send_robot_position_sub(robot_controlled[sending]);
  393.             }
  394.  
  395.             if (robot_fired[sending])
  396.             {
  397.                 robot_fired[sending] = 0;
  398.                 multi_send_data(robot_fire_buf[sending], 18, 0);
  399.             }
  400.  
  401.             if (!(Game_mode & GM_NETWORK))
  402.                 sent += 1;
  403.  
  404.             last_sent = sending;
  405.             rval++;
  406.         }
  407.     }
  408.     Assert((last_sent >= 0) && (last_sent <= MAX_ROBOTS_CONTROLLED));
  409.     return(rval);
  410. }
  411.  
  412. void
  413. multi_send_robot_position_sub(int objnum)
  414. {
  415.     int loc = 0;
  416.  
  417. //    mprintf((0, "SENDPOS object %d, Gametime %d.\n", objnum, GameTime));
  418.  
  419.     multibuf[loc] = MULTI_ROBOT_POSITION;                  loc += 1;
  420.     multibuf[loc] = Player_num;                                loc += 1;
  421.     *(short *)(multibuf+loc) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]);
  422.                                                                         loc += 3;
  423.     create_shortpos((shortpos *)(multibuf+loc), Objects+objnum);
  424.                                                                         loc += sizeof(shortpos);
  425.     multi_send_data(multibuf, loc, 0);
  426. }
  427.  
  428. void
  429. multi_send_robot_position(int objnum, int force)
  430. {
  431.     // Send robot position to other player(s).  Includes a byte
  432.     // value describing whether or not they fired a weapon
  433.  
  434.     if (!(Game_mode & GM_MULTI))
  435.         return;
  436.  
  437.     if ((objnum < 0) || (objnum > Highest_object_index))
  438.     {
  439.         Int3(); // See rob
  440.         return;
  441.     }
  442.     if (Objects[objnum].type != OBJ_ROBOT)
  443.     {
  444.         Int3(); // See rob
  445.         return;
  446.     }
  447.  
  448.     if (Objects[objnum].ctype.ai_info.REMOTE_OWNER != Player_num)
  449.         return;
  450.  
  451. //    Objects[objnum].phys_info.drag = Robot_info[Objects[objnum].id].drag; // Set drag to normal
  452.  
  453.     robot_last_send_time[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = GameTime;
  454.  
  455.     robot_send_pending[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = 1+force;
  456.  
  457.     if (force & (Game_mode & GM_NETWORK))
  458.         PacketUrgent = 1;
  459.  
  460.     return;
  461. }
  462.  
  463. void
  464. multi_send_robot_fire(int objnum, int gun_num, vms_vector *fire)
  465. {
  466.     // Send robot fire event
  467.     int loc = 0;
  468.  
  469.     multibuf[loc] = MULTI_ROBOT_FIRE;                        loc += 1;
  470.     multibuf[loc] = Player_num;                                loc += 1;
  471.     *(short *)(multibuf+loc) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]);
  472.                                                                         loc += 3;
  473.     multibuf[loc] = gun_num;                                    loc += 1;
  474.     *(vms_vector *)(multibuf+loc) = *fire;                    loc += sizeof(vms_vector); // 12
  475.     //                                                                 --------------------------
  476.     //                                                                     Total = 18
  477.     if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num)
  478.     {
  479.         int slot = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
  480.         Assert((slot >= 0) && (slot < MAX_ROBOTS_CONTROLLED));
  481.         if (robot_fired[slot] != 0)
  482.             Int3(); // ROB!
  483.         memcpy(robot_fire_buf[slot], multibuf, loc);
  484.         robot_fired[slot] = 1;
  485. //        if (Game_mode & GM_NETWORK)
  486. //            PacketUrgent = 1;
  487.     }
  488.     else
  489.         multi_send_data(multibuf, loc, 0); // Not our robot, send ASAP
  490. }
  491.  
  492. void
  493. multi_send_robot_explode(int objnum, int killer)
  494. {
  495.     // Send robot explosion event to the other players
  496.  
  497.     int loc = 0;
  498.  
  499.     multibuf[loc] = MULTI_ROBOT_EXPLODE;                    loc += 1;
  500.     multibuf[loc] = Player_num;                                loc += 1;
  501.     *(short *)(multibuf+loc) = (short)objnum_local_to_remote(killer, (byte *)&multibuf[loc+2]);
  502.                                                                         loc += 3;
  503.     *(short *)(multibuf+loc) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]);
  504.                                                                         loc += 3;
  505.     multi_send_data(multibuf, loc, 1);
  506.  
  507.     multi_delete_controlled_robot(objnum);
  508. }
  509.  
  510. void
  511. multi_send_create_robot(int station, int objnum, int type)
  512. {
  513.     // Send create robot information
  514.  
  515.     int loc = 0;
  516.  
  517.     multibuf[loc] = MULTI_CREATE_ROBOT;                        loc += 1;
  518.     multibuf[loc] = Player_num;                                loc += 1;
  519.     multibuf[loc] = (byte)station;                            loc += 1;
  520.     *(short *)(multibuf+loc) = (short)objnum;                loc += 2;
  521.     multibuf[loc] = type;                                        loc += 1;
  522.  
  523.     map_objnum_local_to_local((short)objnum);
  524.  
  525.     multi_send_data(multibuf, loc, 1);
  526. }
  527.  
  528. void
  529. multi_send_boss_actions(int bossobjnum, int action, int secondary, int objnum)
  530. {
  531.     // Send special boss behavior information
  532.  
  533.     int loc = 0;
  534.     
  535.     multibuf[loc] = MULTI_BOSS_ACTIONS;                        loc += 1;
  536.     multibuf[loc] = Player_num;                                loc += 1; // Which player is controlling the boss
  537.     *(short *)(multibuf+loc) = bossobjnum;                    loc += 2; // We won't network map this objnum since its the boss
  538.     multibuf[loc] = (byte)action;                                loc += 1; // What is the boss doing?
  539.     multibuf[loc] = (byte)secondary;                            loc += 1; // More info for what he is doing
  540.     *(short *)(multibuf+loc) = objnum;                        loc += 2; // Objnum of object created by gate-in action
  541.     if (action == 3) {
  542.         *(short *)(multibuf+loc) = Objects[objnum].segnum; loc += 2; // Segment number object created in (for gate only)
  543.     }
  544.     else
  545.                                                                         loc += 2; // Dummy
  546.  
  547.     if (action == 1) { // Teleport releases robot
  548.         // Boss is up for grabs after teleporting
  549.         mprintf((0, "Boss teleporting, removed from my list.\n"));
  550.         Assert((Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM >= 0) && (Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM < MAX_ROBOTS_CONTROLLED));
  551.         multi_delete_controlled_robot(bossobjnum);
  552. //        robot_controlled[Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM] = -1;
  553. //        Objects[bossobjnum].ctype.ai_info.REMOTE_OWNER = -1;
  554.         Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM = 5; // Hands-off period!
  555.     }
  556.     if (action == 3) {
  557.         mprintf((0, "LOCAL:Boss gating in robot id %d, objnum %d (%d).\n", secondary, objnum, objnum));
  558.     }
  559.     multi_send_data(multibuf, loc, 1);
  560. }
  561.             
  562. #define MAX_ROBOT_POWERUPS 4
  563.  
  564. void
  565. multi_send_create_robot_powerups(object *del_obj)
  566. {
  567.     // Send create robot information
  568.  
  569.     int loc = 0;
  570.     int i;
  571.  
  572.     multibuf[loc] = MULTI_CREATE_ROBOT_POWERUPS;            loc += 1;
  573.     multibuf[loc] = Player_num;                                loc += 1;
  574.     multibuf[loc] = del_obj->contains_count;                loc += 1;
  575.     multibuf[loc] = del_obj->contains_type;                 loc += 1;
  576.     multibuf[loc] = del_obj->contains_id;                    loc += 1;
  577.     *(short *)(multibuf+loc) = del_obj->segnum;            loc += 2;
  578.     *(vms_vector *)(multibuf+loc) = del_obj->pos;        loc += 12;
  579.  
  580.     memset(multibuf+loc, -1, MAX_ROBOT_POWERUPS*sizeof(short));
  581.  
  582.     if ((Net_create_loc > MAX_ROBOT_POWERUPS) || (Net_create_loc < 1))
  583.     {
  584.         Int3(); // See Rob
  585.     }
  586.     for (i = 0; i < Net_create_loc; i++)
  587.     {
  588.         *(short *)(multibuf+loc) = Net_create_objnums[i];
  589.         loc += 2;
  590.         map_objnum_local_to_local(Net_create_objnums[i]);
  591.     }
  592.  
  593.     Net_create_loc = 0;
  594.  
  595.     multi_send_data(multibuf, 27, 1);
  596. }
  597.  
  598. void
  599. multi_do_claim_robot(char *buf)
  600. {
  601.     short botnum;
  602.     short remote_botnum;
  603.     char pnum;
  604.  
  605.     pnum = buf[1];
  606.  
  607.     remote_botnum = *(short *)(buf+2);
  608.     botnum = objnum_remote_to_local(remote_botnum, (byte)buf[4]);
  609.  
  610.     if ((botnum > Highest_object_index) || (botnum < 0)) {
  611.         mprintf((1, "Ignoring claim message for object I don't have.\n"));
  612. //        Int3(); // See rob
  613.         return;
  614.     }
  615.  
  616.     if (Objects[botnum].type != OBJ_ROBOT) {
  617.         mprintf((1, "Got MYBOT message for non-Robot!\n"));
  618.         return;
  619.     }
  620.     
  621.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != -1)
  622.     {
  623.         mprintf((0, "Got a MYBOT message bot %d (%d) currently controlled by player %d.\n", botnum, remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER));
  624.         if (MULTI_ROBOT_PRIORITY(remote_botnum, pnum) <= MULTI_ROBOT_PRIORITY(remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER))
  625.             return;
  626.     }
  627.     
  628.     // Perform the requested change
  629.  
  630.     mprintf((0, "Player %d taking control of robot %d (%d).\n", pnum, botnum, remote_botnum));
  631.  
  632.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == Player_num)
  633.     {
  634.         multi_delete_controlled_robot(botnum);
  635.     }
  636.  
  637.     Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
  638.     Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  639. }
  640.  
  641. void
  642. multi_do_release_robot(char *buf)
  643. {
  644.     short botnum;
  645.     char pnum;
  646.  
  647.     pnum = buf[1];
  648.  
  649.     botnum = (objnum_remote_to_local(*(short *)(buf+2), (byte)buf[4]));
  650.  
  651.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  652.         mprintf((1, "Ignoring release message for object I don't have.\n"));
  653. //        Int3(); // See rob
  654.         return;
  655.     }
  656.  
  657.     if (Objects[botnum].type != OBJ_ROBOT) {
  658.         mprintf((1, "Got RELEASE message for non-Robot!\n"));
  659.         return;
  660.     }
  661.     
  662.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
  663.     {
  664.         mprintf((0, "Got a RELEASE message for bot %d not controlled by player %d.\n", botnum, pnum));
  665.         return;
  666.     }
  667.     
  668.     // Perform the requested change
  669.  
  670.     mprintf((0, "Player %d releasing control of robot %d (%d).\n", pnum, botnum, *(short *)(buf+2)));
  671.  
  672.     Objects[botnum].ctype.ai_info.REMOTE_OWNER = -1;
  673.     Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  674. }
  675.  
  676. void
  677. multi_do_robot_position(char *buf)
  678. {
  679.     // Process robot movement sent by another player
  680.  
  681.     short botnum;
  682.     char pnum;
  683.     int loc = 1;
  684.  
  685.     pnum = buf[loc];                                        loc += 1;
  686.  
  687.     botnum = objnum_remote_to_local(*(short *)(buf+loc), (byte)buf[loc+2]);
  688.                                                                 loc += 3;
  689.  
  690.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  691.         mprintf((1, "Got robot position for object I don't have.\n"));
  692. //        Int3(); // See rob
  693.         return;
  694.     }
  695.  
  696.     if ((Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING)) {
  697. //        mprintf((0, "Ignoring position packet for non-robot or exploding robot.\n"));
  698.         return;
  699.     }
  700.         
  701.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
  702.     {    
  703.         if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == -1)
  704.         {
  705.             // Robot claim packet must have gotten lost, let this player claim it.
  706.             if (Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM > 3) {
  707.                 mprintf((0, "Player %d taking control of robot %d WITHOUT claim message.\n", pnum, botnum));
  708.                 Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
  709.                 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  710.             }
  711.             else
  712.                 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM++;
  713.         }
  714.         else
  715.         {
  716.             mprintf((0, "Ignoring position for robot %d not controlled by player %d.\n", botnum, pnum));
  717.             return;
  718.         }
  719.     }
  720.  
  721.     set_thrust_from_velocity(&Objects[botnum]); // Try to smooth out movement
  722. //    Objects[botnum].phys_info.drag = Robot_info[Objects[botnum].id].drag >> 4; // Set drag to low
  723.     
  724.     extract_shortpos(&Objects[botnum], (shortpos *)(buf+loc)); 
  725. }
  726.  
  727. void
  728. multi_do_robot_fire(char *buf)
  729. {
  730.     // Send robot fire event
  731.     int loc = 1;
  732.     int botnum, pnum, gun_num;
  733.     vms_vector fire, gun_point;
  734.     robot_info *robptr;
  735.  
  736.     pnum = buf[loc];                                                loc += 1;
  737.     botnum = objnum_remote_to_local(*(short *)(buf+loc), (byte)buf[loc+2]);
  738.                                                                         loc += 3;
  739.     gun_num = (byte)buf[loc];                                            loc += 1;
  740.     fire = *(vms_vector *)(buf+loc);                            
  741.  
  742.     if ((botnum < 0) || (botnum > Highest_object_index) || (Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING))
  743.     {
  744. //        mprintf((1, "Ignored robot fire from deleted robot.\n"));
  745. //        Int3(); // See Rob, probably not serious tho
  746.         return;
  747.     }
  748.     
  749.     // Do the firing
  750.     
  751.     if (gun_num == -1)
  752.     {
  753.         // Drop proximity bombs
  754.         vm_vec_add(&gun_point, &Objects[botnum].pos, &fire);
  755.     }
  756.     else 
  757.     {
  758.         calc_gun_point(&gun_point, &Objects[botnum], gun_num);
  759.     }
  760.     robptr = &Robot_info[Objects[botnum].id];
  761.     
  762.     if (gun_num == -1) 
  763.         Laser_create_new_easy( &fire, &gun_point, botnum, PROXIMITY_ID, 1);
  764.     else
  765.         Laser_create_new_easy( &fire, &gun_point, botnum, robptr->weapon_type, 1);
  766. }
  767.  
  768. int
  769. multi_explode_robot_sub(int botnum, int killer)
  770. {
  771.     object *robot;
  772.  
  773.     killer = killer;
  774.  
  775.     if ((botnum < 0) || (botnum > Highest_object_index)) { // Objnum in range?
  776.         Int3(); // See rob
  777.         return 0;
  778.     }
  779.  
  780.     if (Objects[botnum].type != OBJ_ROBOT) { // Object is robot?
  781.         mprintf((1, "Non-robot explosion ignored.\n"));
  782. //        Int3(); // See rob
  783.         return 0;
  784.     }
  785.  
  786.     if (Objects[botnum].flags & OF_EXPLODING) { // Object not already exploding
  787.         return 0;
  788.     }
  789.  
  790.     // Data seems valid, explode the sucker
  791.  
  792.     if (Network_send_objects && network_objnum_is_past(botnum))
  793.     {
  794.         mprintf((0, "Resetting object sync due to object removal.\n"));
  795.         Network_send_objnum = -1;
  796.     }
  797.  
  798.     robot = &Objects[botnum];
  799.  
  800.     mprintf((0, "Robot %d controlled by player %d exploded.\n", botnum, robot->ctype.ai_info.REMOTE_OWNER));
  801.  
  802.     // Drop non-random KEY powerups locally only!
  803.     if ((robot->contains_count > 0) && (robot->contains_type == OBJ_POWERUP) && (Game_mode & GM_MULTI_COOP) && (robot->contains_id >= POW_KEY_BLUE) && (robot->contains_id <= POW_KEY_GOLD))
  804.     {
  805.         object_create_egg(robot);
  806.     }
  807.     else if (robot->ctype.ai_info.REMOTE_OWNER == Player_num) 
  808.     {
  809.         multi_drop_robot_powerups(robot-Objects);
  810.         multi_delete_controlled_robot(robot-Objects);
  811.     }
  812.  
  813.     if (Robot_info[robot->id].boss_flag) {
  814.         if (!Boss_dying)
  815.             start_boss_death_sequence(robot);
  816.         else
  817.             return 0;
  818.     } else
  819.         explode_object(robot, STANDARD_EXPL_DELAY);
  820.     return 1;
  821. }
  822.  
  823. void
  824. multi_do_robot_explode(char *buf)
  825. {
  826.     // Explode robot controlled by other player
  827.  
  828.     int botnum;
  829.     int loc = 1;
  830.     short killer;
  831.     int pnum;
  832.     int rval;
  833.  
  834.     pnum = buf[loc];                     loc += 1;
  835.     killer = objnum_remote_to_local(*(short *)(buf+loc), (byte)buf[loc+2]);
  836.                                             loc += 3;
  837.     botnum = objnum_remote_to_local(*(short *)(buf+loc), (byte)buf[loc+2]);
  838.                                             loc += 3;
  839.  
  840. //    Assert((botnum > -1) && (botnum <= Highest_object_index));
  841.  
  842.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  843. //        Int3();
  844. //        mprintf((0, "Ignored explode message for robot I don't have.\n"));
  845.         return;
  846.     }
  847.  
  848.     rval = multi_explode_robot_sub(botnum, killer);
  849.  
  850.     if (rval && (killer == Players[Player_num].objnum))
  851.         add_points_to_score(Robot_info[Objects[botnum].id].score_value);
  852. }
  853.  
  854. extern fix EnergyToCreateOneRobot; // From fuelcen.c 
  855. extern object *create_morph_robot(segment *segp, vms_vector *object_pos, int object_id); // from fuelcen.c
  856.  
  857. void
  858. multi_do_create_robot(char *buf)
  859. {
  860.     
  861.     int fuelcen_num = buf[2];
  862.     int pnum = buf[1];
  863.     short objnum = *(short *)(buf+3);
  864.     int type = buf[5];
  865.  
  866.     FuelCenter *robotcen;
  867.     vms_vector cur_object_loc, direction;
  868.     object *obj;
  869.  
  870.     if ((pnum < 0) || (objnum < 0) || (fuelcen_num < 0) || (fuelcen_num >= Num_fuelcenters) || (pnum >= N_players))
  871.     {
  872.         Int3(); // Bogus data
  873.         return;
  874.     }
  875.  
  876.     robotcen = &Station[fuelcen_num];
  877.  
  878.     mprintf((1, "NETWORK: Creating morph robot from matcen %d.\n", fuelcen_num)); // DEBUG
  879.  
  880.     // Play effect and sound
  881.  
  882.     compute_segment_center(&cur_object_loc, &Segments[robotcen->segnum]);
  883.     obj = object_create_explosion(robotcen->segnum, &cur_object_loc, i2f(10), VCLIP_MORPHING_ROBOT);
  884.     if (obj)
  885.         extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
  886.     if (Vclip[VCLIP_MORPHING_ROBOT].sound_num > -1)
  887.         digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, robotcen->segnum, 0, &cur_object_loc, 0, F1_0 );
  888.  
  889.     // Set robot center flags, in case we become the master for the next one
  890.  
  891.     robotcen->Flag = 0;
  892.     robotcen->Capacity -= EnergyToCreateOneRobot;
  893.     robotcen->Timer = 0;
  894.  
  895.     obj = create_morph_robot(&Segments[robotcen->segnum], &cur_object_loc, type);
  896.     if (obj == NULL)
  897.         return; // Cannot create object!
  898.     
  899.     obj->matcen_creator = robotcen-Station | 0x80;
  900. //    extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
  901.     vm_vec_sub( &direction, &ConsoleObject->pos, &obj->pos );
  902.     vm_vector_2_matrix( &obj->orient, &direction, &obj->orient.uvec, NULL);
  903.     morph_start( obj );
  904.  
  905.     mprintf((1, "matcen created robot %d (remote %d)\n", obj-Objects, objnum));
  906.     map_objnum_local_to_remote(obj-Objects, objnum, pnum);
  907.  
  908.     Assert(obj->ctype.ai_info.REMOTE_OWNER == -1);
  909. }
  910.  
  911. void
  912. multi_do_boss_actions(char *buf)
  913. {
  914.     // Code to handle remote-controlled boss actions
  915.  
  916.     object *boss_obj;
  917.     int boss_objnum;
  918.     int pnum;
  919.     int action, secondary;
  920.     int loc = 1;
  921.     short remote_objnum, segnum;
  922.  
  923.     pnum = buf[loc];                             loc += 1;
  924.     boss_objnum = *(short *)(buf+loc);    loc += 2;
  925.     action = buf[loc];                        loc += 1;
  926.     secondary = buf[loc];                    loc += 1;
  927.     remote_objnum = *(short *)(buf+loc);loc += 2;
  928.     segnum = *(short *)(buf+loc);            loc += 2;
  929.     
  930.     if ((boss_objnum < 0) || (boss_objnum > Highest_object_index))
  931.     {
  932.         Int3();  // See Rob
  933.         return;
  934.     }
  935.  
  936.     boss_obj = &Objects[boss_objnum];
  937.  
  938.     if ((boss_obj->type != OBJ_ROBOT) || !(Robot_info[boss_obj->id].boss_flag))
  939.     {
  940.         Int3(); // Got boss actions for a robot who's not a boss?
  941.         return;
  942.     }
  943.         
  944.     mprintf((0, "REMOTE: performing boss action %d.\n", action));
  945.  
  946.     switch(action) 
  947.     {
  948.         case 1: // Teleport
  949.             {    
  950.                 int teleport_segnum;
  951.                 vms_vector boss_dir;
  952.  
  953.                 if ((secondary < 0) || (secondary > Num_boss_teleport_segs))
  954.                 {
  955.                     Int3(); // Bad segnum for boss teleport, ROB!!
  956.                     return;
  957.                 }
  958.                 teleport_segnum = Boss_teleport_segs[secondary];
  959.                 mprintf((0, "Boss is teleporting, remove from other's list.\n"));
  960.                 if ((teleport_segnum < 0) || (teleport_segnum > Highest_segment_index))
  961.                 {
  962.                     Int3();  // See Rob
  963.                     return;
  964.                 }
  965.                 compute_segment_center(&boss_obj->pos, &Segments[teleport_segnum]);
  966.                 obj_relink(boss_obj-Objects, teleport_segnum);
  967.                 Last_teleport_time = GameTime;
  968.         
  969.                 vm_vec_sub(&boss_dir, &Objects[Players[pnum].objnum].pos, &boss_obj->pos);
  970.                 vm_vector_2_matrix(&boss_obj->orient, &boss_dir, NULL, NULL);
  971.  
  972.                 digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, teleport_segnum, 0, &boss_obj->pos, 0 , F1_0);
  973.                 digi_kill_sound_linked_to_object( boss_obj-Objects);
  974.                 digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, boss_obj-Objects, 1, F1_0, F1_0*512 );    //    F1_0*512 means play twice as loud
  975.                 Ai_local_info[boss_obj-Objects].next_fire = 0;
  976.  
  977.                 if (boss_obj->ctype.ai_info.REMOTE_OWNER == Player_num)
  978.                 {
  979.                     mprintf((1, "WARNING: Accepted teleport message for boss when I controlled him!\n"));
  980.                     multi_delete_controlled_robot(boss_objnum);
  981. //                    robot_controlled[boss_obj->ctype.ai_info.REMOTE_SLOT_NUM] = -1;
  982.                 }
  983.  
  984.                 boss_obj->ctype.ai_info.REMOTE_OWNER = -1; // Boss is up for grabs again!
  985.                 boss_obj->ctype.ai_info.REMOTE_SLOT_NUM = 0; // Available immediately!
  986.             }
  987.             break;
  988.         case 2: // Cloak
  989.             Boss_hit_this_frame = 0;
  990.             Boss_cloak_start_time = GameTime;
  991.             Boss_cloak_end_time = GameTime + Boss_cloak_duration;
  992.             boss_obj->ctype.ai_info.CLOAKED = 1;
  993.             break;
  994.         case 3: // Gate in robots!
  995.             {
  996.                 // Do some validity checking
  997.                 if ( (remote_objnum >= MAX_OBJECTS) || (remote_objnum < 0) || (segnum < 0) || (segnum > Highest_segment_index) )
  998.                 {
  999.                     Int3(); // See Rob, bad data in boss gate action message
  1000.                     return;
  1001.                 }
  1002.  
  1003.                 // Gate one in!
  1004.                 if (gate_in_robot(secondary, segnum))
  1005.                     map_objnum_local_to_remote(Net_create_objnums[0], remote_objnum, pnum);
  1006.                 mprintf((0, "REMOTE: Boss gating in robot id %d, objnum %d (%d).\n", secondary, Net_create_objnums[0], remote_objnum));
  1007.             }
  1008.             break;
  1009.         case 4: // Start effect
  1010.             restart_effect(BOSS_ECLIP_NUM);
  1011.             break;
  1012.         case 5:    // Stop effect
  1013.             stop_effect(BOSS_ECLIP_NUM);
  1014.             break;
  1015.         default:
  1016.             Int3(); // Illegal type to boss actions
  1017.     }
  1018. }
  1019.  
  1020. void
  1021. multi_do_create_robot_powerups(char *buf)
  1022. {
  1023.     // Code to drop remote-controlled robot powerups
  1024.  
  1025.     int loc = 1;
  1026.     object del_obj;
  1027.     int pnum, egg_objnum, i;
  1028.  
  1029.     pnum = buf[loc];                                loc += 1;
  1030.     del_obj.contains_count = buf[loc];        loc += 1;    
  1031.     del_obj.contains_type = buf[loc];        loc += 1;
  1032.     del_obj.contains_id = buf[loc];             loc += 1;
  1033.     del_obj.segnum = *(short *)(buf+loc);    loc += 2;
  1034.     del_obj.pos = *(vms_vector *)(buf+loc);    loc += 12;
  1035.     vm_vec_zero(&del_obj.mtype.phys_info.velocity);
  1036.  
  1037.     Assert((pnum >= 0) && (pnum < N_players));
  1038.  
  1039.     Net_create_loc = 0;
  1040.     srand(1245L);
  1041.  
  1042.     egg_objnum = object_create_egg(&del_obj);
  1043.  
  1044.     if (egg_objnum == -1)
  1045.         return; // Object buffer full
  1046.  
  1047. //    Assert(egg_objnum > -1);
  1048.     Assert((Net_create_loc > 0) && (Net_create_loc <= MAX_ROBOT_POWERUPS));
  1049.  
  1050.     for (i = 0; i < Net_create_loc; i++)
  1051.     {
  1052.         if (*(short *)(buf+loc) != -1)
  1053.             map_objnum_local_to_remote((short)Net_create_objnums[i], *(short *)(buf+loc), pnum);
  1054.         else
  1055.             Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD; // Delete objects other guy didn't create one of
  1056.         loc += 2;
  1057.     }
  1058. }
  1059.  
  1060. void
  1061. multi_drop_robot_powerups(int objnum)
  1062. {
  1063.     // Code to handle dropped robot powerups in network mode ONLY!
  1064.  
  1065.     object *del_obj;
  1066.     int egg_objnum = -1;
  1067.     robot_info    *robptr; 
  1068.  
  1069.     if ((objnum < 0) || (objnum > Highest_object_index))
  1070.     {
  1071.         Int3();  // See rob
  1072.         return;
  1073.     }
  1074.     
  1075.     del_obj = &Objects[objnum];
  1076.  
  1077.     if (del_obj->type != OBJ_ROBOT)
  1078.     {
  1079.         Int3(); // dropping powerups for non-robot, Rob's fault
  1080.         return;
  1081.     }
  1082.  
  1083.     robptr = &Robot_info[del_obj->id];
  1084.  
  1085.     Net_create_loc = 0;
  1086.  
  1087.     if (del_obj->contains_count > 0) { 
  1088.         //    If dropping a weapon that the player has, drop energy instead, unless it's vulcan, in which case drop vulcan ammo.
  1089.         if (del_obj->contains_type == OBJ_POWERUP) {
  1090.             maybe_replace_powerup_with_energy(del_obj);
  1091.  
  1092.             // No key drops in non-coop games!
  1093.             if (!(Game_mode & GM_MULTI_COOP)) {
  1094.                 if ((del_obj->contains_id >= POW_KEY_BLUE) && (del_obj->contains_id <= POW_KEY_GOLD))
  1095.                     del_obj->contains_count = 0;
  1096.             }
  1097.         }
  1098.         srand(1245L);
  1099.         if (del_obj->contains_count > 0)
  1100.             egg_objnum = object_create_egg(del_obj);
  1101.     }
  1102.         
  1103.     else if (del_obj->ctype.ai_info.REMOTE_OWNER == -1) // No random goodies for robots we weren't in control of
  1104.         return;
  1105.  
  1106.     else if (robptr->contains_count) {
  1107.         srand(timer_get_approx_seconds());
  1108.         if (((rand() * 16) >> 15) < robptr->contains_prob) {
  1109.             del_obj->contains_count = ((rand() * robptr->contains_count) >> 15) + 1;
  1110.             del_obj->contains_type = robptr->contains_type;
  1111.             del_obj->contains_id = robptr->contains_id;
  1112.             if (del_obj->contains_type == OBJ_POWERUP)
  1113.                 maybe_replace_powerup_with_energy(del_obj);
  1114.             srand(1245L);
  1115.             if (del_obj->contains_count > 0)
  1116.                 egg_objnum = object_create_egg(del_obj);
  1117.         }
  1118.     }
  1119.  
  1120.     if (egg_objnum >= 0) {
  1121.         // Transmit the object creation to the other players         
  1122.         mprintf((0, "Dropped %d powerups for robot %d.\n", Net_create_loc, del_obj-Objects));
  1123.         multi_send_create_robot_powerups(del_obj);
  1124.     }
  1125. }
  1126.  
  1127. //    -----------------------------------------------------------------------------
  1128. //    Robot *robot got whacked by player player_num and requests permission to do something about it.
  1129. //    Note: This function will be called regardless of whether Game_mode is a multiplayer mode, so it
  1130. //    should quick-out if not in a multiplayer mode.  On the other hand, it only gets called when a
  1131. //    player or player weapon whacks a robot, so it happens rarely.
  1132. void multi_robot_request_change(object *robot, int player_num)
  1133. {
  1134.     int slot, remote_objnum;
  1135.     byte dummy;
  1136.  
  1137.     if (!(Game_mode & GM_MULTI_ROBOTS))
  1138.         return;
  1139.     
  1140.     if (robot->ctype.ai_info.REMOTE_OWNER != Player_num)
  1141.         return;
  1142.  
  1143.     slot = robot->ctype.ai_info.REMOTE_SLOT_NUM;
  1144.  
  1145.     if ((slot < 0) || (slot >= MAX_ROBOTS_CONTROLLED)) {
  1146.         Int3();
  1147.         return;
  1148.     }
  1149.  
  1150.     remote_objnum = objnum_local_to_remote(robot-Objects, &dummy);
  1151.     if (remote_objnum < 0)
  1152.         return;
  1153.  
  1154.     mprintf((0, "request_change(): my pri %d, player %d's pri %d.\n", MULTI_ROBOT_PRIORITY(remote_objnum, Player_num),
  1155.               player_num, MULTI_ROBOT_PRIORITY(remote_objnum, player_num)));
  1156.  
  1157.     if ( (robot_agitation[slot] < 70) || (MULTI_ROBOT_PRIORITY(remote_objnum, player_num) > MULTI_ROBOT_PRIORITY(remote_objnum, Player_num)) || (rand() > 0x4400))
  1158.     {
  1159.         mprintf((0, "Robot %d (%d) released because it got hit by Player %d.\n", robot-Objects, remote_objnum, player_num));
  1160.         if (robot_send_pending[slot])
  1161.             multi_send_robot_position(robot_controlled[slot], -1);
  1162.         multi_send_release_robot(robot_controlled[slot]);
  1163.         robot->ctype.ai_info.REMOTE_SLOT_NUM = 5;  // Hands-off period
  1164.     }
  1165. }
  1166.  
  1167. #endif
  1168. #endif
  1169. 
  1170.