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