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