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