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