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