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.
16 * Multiplayer robot code
57 int multi_add_controlled_robot(int objnum, int agitation);
58 void multi_send_release_robot(int objnum);
59 void multi_delete_controlled_robot(int objnum);
60 void multi_send_robot_position_sub(int objnum);
63 // Code for controlling robots in multiplayer games
67 #define STANDARD_EXPL_DELAY (F1_0/4)
68 #define MIN_CONTROL_TIME F1_0*1
69 #define ROBOT_TIMEOUT F1_0*2
72 #define MAX_TO_DELETE 61
74 int robot_controlled[MAX_ROBOTS_CONTROLLED];
75 int robot_agitation[MAX_ROBOTS_CONTROLLED];
76 fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
77 fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
78 fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
79 int robot_send_pending[MAX_ROBOTS_CONTROLLED];
80 int robot_fired[MAX_ROBOTS_CONTROLLED];
81 sbyte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
83 #define MULTI_ROBOT_PRIORITY(objnum, pnum) (((objnum % 4) + pnum) % N_players)
85 extern void multi_send_stolen_items();
86 extern int multi_powerup_is_allowed (int);
88 //#define MULTI_ROBOT_PRIORITY(objnum, pnum) multi_robot_priority(objnum, pnum)
89 //int multi_robot_priority(int objnum, int pnum)
91 // return( ((objnum % 4) + pnum) % N_players);
95 multi_can_move_robot(int objnum, int agitation)
97 // Determine whether or not I am allowed to move this robot.
100 // Claim robot if necessary.
106 if ((objnum < 0) || (objnum > Highest_object_index))
112 else if (Objects[objnum].type != OBJ_ROBOT)
119 else if ((Robot_info[Objects[objnum].id].boss_flag) && (Boss_dying == 1))
122 else if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num) // Already my robot!
124 int slot_num = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
126 if ((slot_num < 0) || (slot_num >= MAX_ROBOTS_CONTROLLED))
128 mprintf ((0,"Not asserting in multi_can_move_robot!"));
132 if (robot_fired[slot_num]) {
133 // mprintf((0, "Preventing robot from firing again until firing complete!\n"));
137 robot_agitation[slot_num] = agitation;
138 robot_last_message_time[slot_num] = GameTime;
143 else if ((Objects[objnum].ctype.ai_info.REMOTE_OWNER != -1) || (agitation < MIN_TO_ADD))
145 if (agitation == ROBOT_FIRE_AGITATION) // Special case for firing at non-player
147 mprintf((0, "Shooting anyway.\n"));
148 rval = 1; // Try to fire at player even tho we're not in control!
154 rval = multi_add_controlled_robot(objnum, agitation);
160 multi_check_robot_timeout(void)
162 static fix lastcheck = 0;
165 if (GameTime > lastcheck + F1_0)
167 lastcheck = GameTime;
168 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
170 if ((robot_controlled[i] != -1) && (robot_last_send_time[i] + ROBOT_TIMEOUT < GameTime))
172 if (Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER != Player_num)
174 robot_controlled[i] = -1;
175 Int3(); // Non-terminal but Rob is interesting, step over please...
178 if (Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER !=Player_num)
180 mprintf ((0,"What? This ain't my bot and I'm trying to time it out!\n"));
183 mprintf((0, "Robot %d (slot %d) removed, timed out, frame %d.\n", robot_controlled[i], i, GameTime));
184 if (robot_send_pending[i])
185 multi_send_robot_position(robot_controlled[i], 1);
186 multi_send_release_robot(robot_controlled[i]);
187 // multi_delete_controlled_robot(robot_controlled[i]);
188 // robot_controlled[i] = -1;
195 multi_strip_robots(int playernum)
197 // Grab all robots away from a player
198 // (player died or exited the game)
202 if (Game_mode & GM_MULTI_ROBOTS) {
204 if (playernum == Player_num)
205 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
206 multi_delete_controlled_robot(robot_controlled[i]);
208 for (i = 1; i <= Highest_object_index; i++)
209 if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].ctype.ai_info.REMOTE_OWNER == playernum)) {
210 Assert((Objects[i].control_type == CT_AI) || (Objects[i].control_type == CT_NONE) || (Objects[i].control_type == CT_MORPH));
211 Objects[i].ctype.ai_info.REMOTE_OWNER = -1;
212 if (playernum == Player_num)
213 Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 4;
215 Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 0;
218 // Note -- only call this with playernum == Player_num if all other players
219 // already know that we are clearing house. This does not send a release
220 // message for each controlled robot!!
225 multi_dump_robots(void)
228 // Dump robot control info for debug purposes
232 if (!(Game_mode & GM_MULTI_ROBOTS))
236 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
238 mprintf((0, "Robot %d) objnum=%d, anger=%d, last_message=%d, last move=%d, gametime=%d\n", i, robot_controlled[i], robot_agitation[i], robot_last_message_time[i], robot_last_send_time[i], GameTime));
243 multi_add_controlled_robot(int objnum, int agitation)
246 int lowest_agitation = 0x7fffffff; // MAX POSITIVE INT
247 int lowest_agitated_bot = -1;
248 int first_free_robot = -1;
250 // Try to add a new robot to the controlled list, return 1 if added, 0 if not.
252 if (Robot_info[Objects[objnum].id].boss_flag) // this is a boss, so make sure he gets a slot
253 agitation=(agitation*3)+Player_num;
255 if (Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM > 0)
257 mprintf((0, "Robot %d hands-off = %d.\n", objnum, Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM));
258 Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM -= 1;
262 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
264 if ((robot_controlled[i] == -1) || (Objects[robot_controlled[i]].type != OBJ_ROBOT)) {
265 first_free_robot = i;
269 if (robot_last_message_time[i] + ROBOT_TIMEOUT < GameTime) {
270 mprintf((0, "Robot %d replaced (timeout).\n", robot_controlled[i]));
271 if (robot_send_pending[i])
272 multi_send_robot_position(robot_controlled[i], 1);
273 multi_send_release_robot(robot_controlled[i]);
274 first_free_robot = i;
278 if ((robot_controlled[i] != -1) && (robot_agitation[i] < lowest_agitation) && (robot_controlled_time[i] + MIN_CONTROL_TIME < GameTime))
280 lowest_agitation = robot_agitation[i];
281 lowest_agitated_bot = i;
285 // mprintf ((0,"first_free_robot=%d lowest_agitated_bot=%d\n",first_free_robot,lowest_agitated_bot));
287 if (first_free_robot != -1) // Room left for new robots
288 i = first_free_robot;
290 else if ((agitation > lowest_agitation))// && (lowest_agitation <= MAX_TO_DELETE)) // Replace some old robot with a more agitated one
292 if (robot_send_pending[lowest_agitated_bot])
293 multi_send_robot_position(robot_controlled[lowest_agitated_bot], 1);
294 multi_send_release_robot(robot_controlled[lowest_agitated_bot]);
296 mprintf((0, "Replaced robot %d (agitation %d) with robot %d (agitation %d).\n", robot_controlled[lowest_agitated_bot], lowest_agitation, objnum, agitation));
298 i = lowest_agitated_bot;
301 // mprintf((0, "Cannot add robot %d agitation %d (min agitation %d)\n", objnum, agitation, lowest_agitation));
302 return(0); // Sorry, can't squeeze him in!
305 mprintf((0, "Taking control of robot %d, agitation %d.\n", objnum, agitation));
307 multi_send_claim_robot(objnum);
308 robot_controlled[i] = objnum;
309 robot_agitation[i] = agitation;
310 Objects[objnum].ctype.ai_info.REMOTE_OWNER = Player_num;
311 Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = i;
312 robot_controlled_time[i] = GameTime;
313 robot_last_send_time[i] = robot_last_message_time[i] = GameTime;
318 multi_delete_controlled_robot(int objnum)
322 // Delete robot object number objnum from list of controlled robots because it is dead
324 if ( (objnum<0) || (objnum>Highest_object_index)) {
325 mprintf((0, "Trying to releasing control of bogus robot %d\n", objnum));
329 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
330 if (robot_controlled[i] == objnum)
333 if (i == MAX_ROBOTS_CONTROLLED)
336 mprintf((0, "Releasing control of robot %d\n", objnum));
338 if (Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM != i)
340 mprintf ((0,"Saved from asserting in multi_delete_controlled_bot!"));
341 Int3(); // can't release this bot!
345 Objects[objnum].ctype.ai_info.REMOTE_OWNER = -1;
346 Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
347 robot_controlled[i] = -1;
348 robot_send_pending[i] = 0;
353 multi_send_claim_robot(int objnum)
357 if ((objnum < 0) || (objnum > Highest_object_index))
363 if (Objects[objnum].type != OBJ_ROBOT)
369 // The AI tells us we should take control of this robot.
371 multibuf[0] = (char)MULTI_ROBOT_CLAIM;
372 multibuf[1] = Player_num;
373 s = objnum_local_to_remote(objnum, (sbyte *)&multibuf[4]);
374 PUT_INTEL_SHORT(multibuf+2, s);
376 multi_send_data(multibuf, 5, 2);
377 multi_send_data(multibuf, 5, 2);
378 multi_send_data(multibuf, 5, 2);
383 multi_send_release_robot(int objnum)
387 if ((objnum < 0) || (objnum > Highest_object_index))
393 if (Objects[objnum].type != OBJ_ROBOT)
399 multi_delete_controlled_robot(objnum);
401 multibuf[0] = (char)MULTI_ROBOT_RELEASE;
402 multibuf[1] = Player_num;
403 s = objnum_local_to_remote(objnum, (sbyte *)&multibuf[4]);
404 PUT_INTEL_SHORT(multibuf+2, s);
406 multi_send_data(multibuf, 5, 2);
407 multi_send_data(multibuf, 5, 2);
408 multi_send_data(multibuf, 5, 2);
411 #define MIN_ROBOT_COM_GAP F1_0/12
414 multi_send_robot_frame(int sent)
416 static int last_sent = 0;
421 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
423 int sending = (last_sent+1+i)%MAX_ROBOTS_CONTROLLED;
424 if ( (robot_controlled[sending] != -1) && ((robot_send_pending[sending] > sent) || (robot_fired[sending] > sent)) )
426 if (robot_send_pending[sending])
428 robot_send_pending[sending] = 0;
429 multi_send_robot_position_sub(robot_controlled[sending]);
432 if (robot_fired[sending])
434 robot_fired[sending] = 0;
435 multi_send_data((char *)robot_fire_buf[sending], 18, 1);
438 if (!(Game_mode & GM_NETWORK))
445 Assert((last_sent >= 0) && (last_sent <= MAX_ROBOTS_CONTROLLED));
450 multi_send_robot_position_sub(int objnum)
454 #ifdef WORDS_BIGENDIAN
458 // mprintf((0, "SENDPOS object %d, Gametime %d.\n", objnum, GameTime));
460 multibuf[loc] = MULTI_ROBOT_POSITION; loc += 1;
461 multibuf[loc] = Player_num; loc += 1;
462 s = objnum_local_to_remote(objnum, (sbyte *)&multibuf[loc+2]);
463 PUT_INTEL_SHORT(multibuf+loc, s);
466 #ifndef WORDS_BIGENDIAN
467 create_shortpos((shortpos *)(multibuf+loc), &Objects[objnum], 0); loc += sizeof(shortpos);
469 create_shortpos(&sp, &Objects[objnum], 1);
470 memcpy(&(multibuf[loc]), (ubyte *)(sp.bytemat), 9);
472 memcpy(&(multibuf[loc]), (ubyte *)&(sp.xo), 14);
475 multi_send_data(multibuf, loc, 1);
479 multi_send_robot_position(int objnum, int force)
481 // Send robot position to other player(s). Includes a byte
482 // value describing whether or not they fired a weapon
484 if (!(Game_mode & GM_MULTI))
487 if ((objnum < 0) || (objnum > Highest_object_index))
492 if (Objects[objnum].type != OBJ_ROBOT)
498 if (Objects[objnum].ctype.ai_info.REMOTE_OWNER != Player_num)
501 // Objects[objnum].phys_info.drag = Robot_info[Objects[objnum].id].drag; // Set drag to normal
503 robot_last_send_time[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = GameTime;
505 robot_send_pending[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = 1+force;
507 if (force & (Game_mode & GM_NETWORK))
514 multi_send_robot_fire(int objnum, int gun_num, vms_vector *fire)
516 // Send robot fire event
519 #ifdef WORDS_BIGENDIAN
520 vms_vector swapped_vec;
523 multibuf[loc] = MULTI_ROBOT_FIRE; loc += 1;
524 multibuf[loc] = Player_num; loc += 1;
525 s = objnum_local_to_remote(objnum, (sbyte *)&multibuf[loc+2]);
526 PUT_INTEL_SHORT(multibuf+loc, s);
528 multibuf[loc] = gun_num; loc += 1;
529 #ifndef WORDS_BIGENDIAN
530 memcpy(multibuf+loc, fire, sizeof(vms_vector)); loc += sizeof(vms_vector); // 12
531 // --------------------------
534 swapped_vec.x = (fix)INTEL_INT((int)fire->x);
535 swapped_vec.y = (fix)INTEL_INT((int)fire->y);
536 swapped_vec.z = (fix)INTEL_INT((int)fire->z);
537 memcpy(multibuf+loc, &swapped_vec, sizeof(vms_vector)); loc += sizeof(vms_vector);
540 if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num)
542 int slot = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
543 if (slot<0 || slot>=MAX_ROBOTS_CONTROLLED)
545 mprintf ((0,"Saved from asserting in robofire\n"));
548 if (robot_fired[slot] != 0)
550 mprintf ((0,"Couldn't find an open slot for robo firing!\n"));
554 memcpy(robot_fire_buf[slot], multibuf, loc);
555 robot_fired[slot] = 1;
556 if (Game_mode & GM_NETWORK)
560 multi_send_data(multibuf, loc, 2); // Not our robot, send ASAP
564 multi_send_robot_explode(int objnum, int killer,char isthief)
566 // Send robot explosion event to the other players
571 multibuf[loc] = MULTI_ROBOT_EXPLODE; loc += 1;
572 multibuf[loc] = Player_num; loc += 1;
573 s = (short)objnum_local_to_remote(killer, (sbyte *)&multibuf[loc+2]);
574 PUT_INTEL_SHORT(multibuf+loc, s); loc += 3;
576 s = (short)objnum_local_to_remote(objnum, (sbyte *)&multibuf[loc+2]);
577 PUT_INTEL_SHORT(multibuf+loc, s); loc += 3;
579 multibuf[loc]=isthief; loc++;
581 multi_send_data(multibuf, loc, 1);
583 multi_delete_controlled_robot(objnum);
587 multi_send_create_robot(int station, int objnum, int type)
589 // Send create robot information
593 multibuf[loc] = MULTI_CREATE_ROBOT; loc += 1;
594 multibuf[loc] = Player_num; loc += 1;
595 multibuf[loc] = (sbyte)station; loc += 1;
596 PUT_INTEL_SHORT(multibuf+loc, objnum); loc += 2;
597 multibuf[loc] = type; loc += 1;
599 map_objnum_local_to_local((short)objnum);
601 multi_send_data(multibuf, loc, 2);
605 multi_send_boss_actions(int bossobjnum, int action, int secondary, int objnum)
607 // Send special boss behavior information
611 multibuf[loc] = MULTI_BOSS_ACTIONS; loc += 1;
612 multibuf[loc] = Player_num; loc += 1; // Which player is controlling the boss
613 PUT_INTEL_SHORT(multibuf+loc, bossobjnum); loc += 2; // We won't network map this objnum since it's the boss
614 multibuf[loc] = (sbyte)action; loc += 1; // What is the boss doing?
615 multibuf[loc] = (sbyte)secondary; loc += 1; // More info for what he is doing
616 PUT_INTEL_SHORT(multibuf+loc, objnum); loc += 2; // Objnum of object created by gate-in action
618 PUT_INTEL_SHORT(multibuf+loc, Objects[objnum].segnum); loc += 2; // Segment number object created in (for gate only)
623 if (action == 1) { // Teleport releases robot
624 // Boss is up for grabs after teleporting
625 mprintf((0, "Boss teleporting, removed from my list.\n"));
626 Assert((Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM >= 0) && (Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM < MAX_ROBOTS_CONTROLLED));
627 multi_delete_controlled_robot(bossobjnum);
628 // robot_controlled[Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM] = -1;
629 // Objects[bossobjnum].ctype.ai_info.REMOTE_OWNER = -1;
630 // Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM = 5; // Hands-off period!
633 mprintf((0, "LOCAL:Boss gating in robot id %d, objnum %d (%d).\n", secondary, objnum, objnum));
635 multi_send_data(multibuf, loc, 1);
638 #define MAX_ROBOT_POWERUPS 4
641 multi_send_create_robot_powerups(object *del_obj)
643 // Send create robot information
647 #ifdef WORDS_BIGENDIAN
648 vms_vector swapped_vec;
651 multibuf[loc] = MULTI_CREATE_ROBOT_POWERUPS; loc += 1;
652 multibuf[loc] = Player_num; loc += 1;
653 multibuf[loc] = del_obj->contains_count; loc += 1;
654 multibuf[loc] = del_obj->contains_type; loc += 1;
655 multibuf[loc] = del_obj->contains_id; loc += 1;
656 PUT_INTEL_SHORT(multibuf+loc, del_obj->segnum); loc += 2;
657 #ifndef WORDS_BIGENDIAN
658 memcpy(multibuf+loc, &del_obj->pos, sizeof(vms_vector)); loc += 12;
660 swapped_vec.x = (fix)INTEL_INT((int)del_obj->pos.x);
661 swapped_vec.y = (fix)INTEL_INT((int)del_obj->pos.y);
662 swapped_vec.z = (fix)INTEL_INT((int)del_obj->pos.z);
663 memcpy(multibuf+loc, &swapped_vec, sizeof(vms_vector)); loc += 12;
666 memset(multibuf+loc, -1, MAX_ROBOT_POWERUPS*sizeof(short));
668 if (del_obj->contains_count!=Net_create_loc)
669 Int3(); //Get Jason, a bad thing happened
671 if ((Net_create_loc > MAX_ROBOT_POWERUPS) || (Net_create_loc < 1))
675 for (i = 0; i < Net_create_loc; i++)
677 PUT_INTEL_SHORT(multibuf+loc, Net_create_objnums[i]);
679 map_objnum_local_to_local(Net_create_objnums[i]);
684 multi_send_data(multibuf, 27, 2);
688 multi_do_claim_robot(char *buf)
690 short botnum, remote_botnum;
695 remote_botnum = GET_INTEL_SHORT(buf + 2);
696 botnum = objnum_remote_to_local(remote_botnum, (sbyte)buf[4]);
698 if ((botnum > Highest_object_index) || (botnum < 0)) {
699 mprintf((1, "Ignoring claim message for object I don't have.\n"));
700 // Int3(); // See rob
704 if (Objects[botnum].type != OBJ_ROBOT) {
705 mprintf((1, "Got MYBOT message for non-Robot!\n"));
709 if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != -1)
711 mprintf((0, "Got a MYBOT message bot %d (%d) currently controlled by player %d.\n", botnum, remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER));
712 if (MULTI_ROBOT_PRIORITY(remote_botnum, pnum) <= MULTI_ROBOT_PRIORITY(remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER))
716 // Perform the requested change
718 mprintf((0, "Player %d taking control of robot %d (%d).\n", pnum, botnum, remote_botnum));
720 if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == Player_num)
722 multi_delete_controlled_robot(botnum);
725 Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
726 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
730 multi_do_release_robot(char *buf)
732 short botnum, remote_botnum;
737 remote_botnum = GET_INTEL_SHORT(buf + 2);
738 botnum = objnum_remote_to_local(remote_botnum, (sbyte)buf[4]);
740 if ((botnum < 0) || (botnum > Highest_object_index)) {
741 mprintf((1, "Ignoring release message for object I don't have.\n"));
742 // Int3(); // See rob
746 if (Objects[botnum].type != OBJ_ROBOT) {
747 mprintf((1, "Got RELEASE message for non-Robot!\n"));
751 if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
753 mprintf((0, "Got a RELEASE message for bot %d not controlled by player %d.\n", botnum, pnum));
757 // Perform the requested change
759 mprintf((0, "Player %d releasing control of robot %d (%d).\n", pnum, botnum, remote_botnum));
761 Objects[botnum].ctype.ai_info.REMOTE_OWNER = -1;
762 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
766 multi_do_robot_position(char *buf)
768 // Process robot movement sent by another player
770 short botnum, remote_botnum;
773 #ifdef WORDS_BIGENDIAN
777 pnum = buf[loc]; loc += 1;
779 remote_botnum = GET_INTEL_SHORT(buf + loc);
780 botnum = objnum_remote_to_local(remote_botnum, (sbyte)buf[loc+2]); loc += 3;
782 if ((botnum < 0) || (botnum > Highest_object_index)) {
783 mprintf((1, "Got robot position for object I don't have.\n"));
784 // Int3(); // See rob
788 if ((Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING)) {
789 // mprintf((0, "Ignoring position packet for non-robot or exploding robot.\n"));
793 if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
795 if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == -1)
797 // Robot claim packet must have gotten lost, let this player claim it.
798 if (Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM > 3) {
799 mprintf((0, "Player %d taking control of robot %d WITHOUT claim message.\n", pnum, botnum));
800 Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
801 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
804 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM++;
808 mprintf((0, "Ignoring position for robot %d not controlled by player %d.\n", botnum, pnum));
813 set_thrust_from_velocity(&Objects[botnum]); // Try to smooth out movement
814 // Objects[botnum].phys_info.drag = Robot_info[Objects[botnum].id].drag >> 4; // Set drag to low
816 #ifndef WORDS_BIGENDIAN
817 extract_shortpos(&Objects[botnum], (shortpos *)(buf+loc), 0);
819 memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + loc), 9); loc += 9;
820 memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + loc), 14);
821 extract_shortpos(&Objects[botnum], &sp, 1);
826 multi_do_robot_fire(char *buf)
828 // Send robot fire event
833 vms_vector fire, gun_point;
836 pnum = buf[loc]; loc += 1;
837 remote_botnum = GET_INTEL_SHORT(buf + loc);
838 botnum = objnum_remote_to_local(remote_botnum, (sbyte)buf[loc+2]); loc += 3;
839 gun_num = (sbyte)buf[loc]; loc += 1;
840 memcpy(&fire, buf+loc, sizeof(vms_vector));
841 fire.x = (fix)INTEL_INT((int)fire.x);
842 fire.y = (fix)INTEL_INT((int)fire.y);
843 fire.z = (fix)INTEL_INT((int)fire.z);
845 if ((botnum < 0) || (botnum > Highest_object_index) || (Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING))
847 // mprintf((1, "Ignored robot fire from deleted robot.\n"));
848 // Int3(); // See Rob, probably not serious tho
854 if (gun_num == -1 || gun_num==-2)
856 // Drop proximity bombs
857 vm_vec_add(&gun_point, &Objects[botnum].pos, &fire);
861 calc_gun_point(&gun_point, &Objects[botnum], gun_num);
863 robptr = &Robot_info[Objects[botnum].id];
866 Laser_create_new_easy( &fire, &gun_point, botnum, PROXIMITY_ID, 1);
867 else if (gun_num == -2)
868 Laser_create_new_easy( &fire, &gun_point, botnum, SUPERPROX_ID, 1);
870 Laser_create_new_easy( &fire, &gun_point, botnum, robptr->weapon_type, 1);
873 extern void drop_stolen_items (object *);
876 multi_explode_robot_sub(int botnum, int killer,char isthief)
880 if ((botnum < 0) || (botnum > Highest_object_index)) { // Objnum in range?
885 if (Objects[botnum].type != OBJ_ROBOT) { // Object is robot?
886 mprintf((1, "Non-robot explosion ignored.\n"));
887 // Int3(); // See rob
891 if (Objects[botnum].flags & OF_EXPLODING) { // Object not already exploding
895 // Data seems valid, explode the sucker
897 if (Network_send_objects && network_objnum_is_past(botnum))
899 mprintf((0, "Resetting object sync due to object removal.\n"));
900 Network_send_objnum = -1;
903 robot = &Objects[botnum];
905 mprintf((0, "Robot %d controlled by player %d exploded.\n", botnum, robot->ctype.ai_info.REMOTE_OWNER));
907 // Drop non-random KEY powerups locally only!
908 if ((robot->contains_count > 0) && (robot->contains_type == OBJ_POWERUP) && (Game_mode & GM_MULTI_COOP) && (robot->contains_id >= POW_KEY_BLUE) && (robot->contains_id <= POW_KEY_GOLD))
910 object_create_egg(robot);
912 else if (robot->ctype.ai_info.REMOTE_OWNER == Player_num)
914 multi_drop_robot_powerups(OBJECT_NUMBER(robot));
915 multi_delete_controlled_robot(OBJECT_NUMBER(robot));
917 else if (robot->ctype.ai_info.REMOTE_OWNER == -1 && network_i_am_master())
919 multi_drop_robot_powerups(OBJECT_NUMBER(robot));
920 //multi_delete_controlled_robot(OBJECT_NUMBER(robot));
923 if (isthief || Robot_info[robot->id].thief)
924 drop_stolen_items(robot);
926 if (Robot_info[robot->id].boss_flag) {
928 start_boss_death_sequence(robot);
931 } else if (Robot_info[robot->id].death_roll) {
932 start_robot_death_sequence(robot);
934 if (robot->id == SPECIAL_REACTOR_ROBOT)
935 special_reactor_stuff();
936 if (Robot_info[robot->id].kamikaze)
937 explode_object(robot,1); // Kamikaze, explode right away, IN YOUR FACE!
939 explode_object(robot,STANDARD_EXPL_DELAY);
946 multi_do_robot_explode(char *buf)
948 // Explode robot controlled by other player
953 short killer, remote_killer;
958 pnum = buf[loc]; loc += 1;
959 remote_killer = GET_INTEL_SHORT(buf + loc);
960 killer = objnum_remote_to_local(remote_killer, (sbyte)buf[loc+2]); loc += 3;
961 remote_botnum = GET_INTEL_SHORT(buf + loc);
962 botnum = objnum_remote_to_local(remote_botnum, (sbyte)buf[loc+2]); loc += 3;
965 if ((botnum < 0) || (botnum > Highest_object_index)) {
967 mprintf((0, "Ignored explode message for robot I don't have.\n"));
971 rval = multi_explode_robot_sub(botnum, killer,thief);
973 if (rval && (killer == Players[Player_num].objnum))
974 add_points_to_score(Robot_info[Objects[botnum].id].score_value);
977 extern fix EnergyToCreateOneRobot; // From fuelcen.c
978 extern object *create_morph_robot(segment *segp, vms_vector *object_pos, int object_id); // from fuelcen.c
981 multi_do_create_robot(char *buf)
984 int fuelcen_num = buf[2];
989 FuelCenter *robotcen;
990 vms_vector cur_object_loc, direction;
993 objnum = GET_INTEL_SHORT(buf + 3);
995 if ((pnum < 0) || (objnum < 0) || (fuelcen_num < 0) || (fuelcen_num >= Num_fuelcenters) || (pnum >= N_players))
997 Int3(); // Bogus data
1001 robotcen = &Station[fuelcen_num];
1003 mprintf((1, "NETWORK: Creating morph robot from matcen %d.\n", fuelcen_num)); // DEBUG
1005 // Play effect and sound
1007 compute_segment_center(&cur_object_loc, &Segments[robotcen->segnum]);
1008 obj = object_create_explosion(robotcen->segnum, &cur_object_loc, i2f(10), VCLIP_MORPHING_ROBOT);
1010 extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
1011 if (Vclip[VCLIP_MORPHING_ROBOT].sound_num > -1)
1012 digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, robotcen->segnum, 0, &cur_object_loc, 0, F1_0 );
1014 // Set robot center flags, in case we become the master for the next one
1017 robotcen->Capacity -= EnergyToCreateOneRobot;
1018 robotcen->Timer = 0;
1020 obj = create_morph_robot(&Segments[robotcen->segnum], &cur_object_loc, type);
1022 return; // Cannot create object!
1024 obj->matcen_creator = (robotcen-Station) | 0x80;
1025 // extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
1026 vm_vec_sub( &direction, &ConsoleObject->pos, &obj->pos );
1027 vm_vector_2_matrix( &obj->orient, &direction, &obj->orient.uvec, NULL);
1030 mprintf((1, "matcen created robot %d (remote %d)\n", OBJECT_NUMBER(obj), objnum));
1031 map_objnum_local_to_remote(OBJECT_NUMBER(obj), objnum, pnum);
1033 Assert(obj->ctype.ai_info.REMOTE_OWNER == -1);
1037 multi_do_boss_actions(char *buf)
1039 // Code to handle remote-controlled boss actions
1044 int action, secondary;
1046 short remote_objnum, segnum;
1048 pnum = buf[loc]; loc += 1;
1049 boss_objnum = GET_INTEL_SHORT(buf + loc); loc += 2;
1050 action = buf[loc]; loc += 1;
1051 secondary = buf[loc]; loc += 1;
1052 remote_objnum = GET_INTEL_SHORT(buf + loc); loc += 2;
1053 segnum = GET_INTEL_SHORT(buf + loc); loc += 2;
1055 if ((boss_objnum < 0) || (boss_objnum > Highest_object_index))
1061 boss_obj = &Objects[boss_objnum];
1063 if ((boss_obj->type != OBJ_ROBOT) || !(Robot_info[boss_obj->id].boss_flag))
1065 Int3(); // Got boss actions for a robot who's not a boss?
1069 mprintf((0, "REMOTE: performing boss action %d.\n", action));
1075 int teleport_segnum;
1076 vms_vector boss_dir;
1078 if ((secondary < 0) || (secondary > Num_boss_teleport_segs))
1080 Int3(); // Bad segnum for boss teleport, ROB!!
1083 teleport_segnum = Boss_teleport_segs[secondary];
1084 mprintf((0, "Boss is teleporting, remove from other's list.\n"));
1085 if ((teleport_segnum < 0) || (teleport_segnum > Highest_segment_index))
1090 compute_segment_center(&boss_obj->pos, &Segments[teleport_segnum]);
1091 obj_relink(OBJECT_NUMBER(boss_obj), teleport_segnum);
1092 Last_teleport_time = GameTime;
1094 vm_vec_sub(&boss_dir, &Objects[Players[pnum].objnum].pos, &boss_obj->pos);
1095 vm_vector_2_matrix(&boss_obj->orient, &boss_dir, NULL, NULL);
1097 digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, teleport_segnum, 0, &boss_obj->pos, 0 , F1_0);
1098 digi_kill_sound_linked_to_object( OBJECT_NUMBER(boss_obj) );
1099 digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, OBJECT_NUMBER(boss_obj), 1, F1_0, F1_0*512 ); // F1_0*512 means play twice as loud
1100 Ai_local_info[OBJECT_NUMBER(boss_obj)].next_fire = 0;
1102 if (boss_obj->ctype.ai_info.REMOTE_OWNER == Player_num)
1104 mprintf((1, "WARNING: Accepted teleport message for boss when I controlled him!\n"));
1105 multi_delete_controlled_robot(boss_objnum);
1106 // robot_controlled[boss_obj->ctype.ai_info.REMOTE_SLOT_NUM] = -1;
1109 boss_obj->ctype.ai_info.REMOTE_OWNER = -1; // Boss is up for grabs again!
1110 boss_obj->ctype.ai_info.REMOTE_SLOT_NUM = 0; // Available immediately!
1114 Boss_hit_time = -F1_0*10;
1115 Boss_cloak_start_time = GameTime;
1116 Boss_cloak_end_time = GameTime + Boss_cloak_duration;
1117 boss_obj->ctype.ai_info.CLOAKED = 1;
1119 case 3: // Gate in robots!
1121 // Do some validity checking
1122 if ( (remote_objnum >= MAX_OBJECTS) || (remote_objnum < 0) || (segnum < 0) || (segnum > Highest_segment_index) )
1124 Int3(); // See Rob, bad data in boss gate action message
1129 if (gate_in_robot(secondary, segnum))
1130 map_objnum_local_to_remote(Net_create_objnums[0], remote_objnum, pnum);
1131 mprintf((0, "REMOTE: Boss gating in robot id %d, objnum %d (%d).\n", secondary, Net_create_objnums[0], remote_objnum));
1134 case 4: // Start effect
1135 restart_effect(BOSS_ECLIP_NUM);
1137 case 5: // Stop effect
1138 stop_effect(BOSS_ECLIP_NUM);
1141 Int3(); // Illegal type to boss actions
1146 multi_do_create_robot_powerups(char *buf)
1148 // Code to drop remote-controlled robot powerups
1152 int pnum, egg_objnum, i;
1154 pnum = buf[loc]; loc += 1;
1155 del_obj.contains_count = buf[loc]; loc += 1;
1156 del_obj.contains_type = buf[loc]; loc += 1;
1157 del_obj.contains_id = buf[loc]; loc += 1;
1158 del_obj.segnum = GET_INTEL_SHORT(buf + loc); loc += 2;
1159 memcpy(&del_obj.pos, buf+loc, sizeof(vms_vector)); loc += 12;
1161 vm_vec_zero(&del_obj.mtype.phys_info.velocity);
1163 del_obj.pos.x = (fix)INTEL_INT((int)del_obj.pos.x);
1164 del_obj.pos.y = (fix)INTEL_INT((int)del_obj.pos.y);
1165 del_obj.pos.z = (fix)INTEL_INT((int)del_obj.pos.z);
1167 Assert((pnum >= 0) && (pnum < N_players));
1168 Assert (pnum!=Player_num); // What? How'd we send ourselves this?
1173 egg_objnum = object_create_egg(&del_obj);
1175 if (egg_objnum == -1)
1176 return; // Object buffer full
1178 // Assert(egg_objnum > -1);
1179 Assert((Net_create_loc > 0) && (Net_create_loc <= MAX_ROBOT_POWERUPS));
1181 for (i = 0; i < Net_create_loc; i++)
1185 s = GET_INTEL_SHORT(buf + loc);
1187 map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
1189 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD; // Delete objects other guy didn't create one of
1195 multi_drop_robot_powerups(int objnum)
1197 // Code to handle dropped robot powerups in network mode ONLY!
1200 int egg_objnum = -1;
1203 if ((objnum < 0) || (objnum > Highest_object_index))
1209 del_obj = &Objects[objnum];
1211 if (del_obj->type != OBJ_ROBOT)
1213 Int3(); // dropping powerups for non-robot, Rob's fault
1217 robptr = &Robot_info[del_obj->id];
1221 if (del_obj->contains_count > 0) {
1222 // If dropping a weapon that the player has, drop energy instead, unless it's vulcan, in which case drop vulcan ammo.
1223 if (del_obj->contains_type == OBJ_POWERUP) {
1224 maybe_replace_powerup_with_energy(del_obj);
1225 if (!multi_powerup_is_allowed(del_obj->contains_id))
1226 del_obj->contains_id=POW_SHIELD_BOOST;
1228 // No key drops in non-coop games!
1229 if (!(Game_mode & GM_MULTI_COOP)) {
1230 if ((del_obj->contains_id >= POW_KEY_BLUE) && (del_obj->contains_id <= POW_KEY_GOLD))
1231 del_obj->contains_count = 0;
1235 if (del_obj->contains_count > 0)
1236 egg_objnum = object_create_egg(del_obj);
1239 else if (del_obj->ctype.ai_info.REMOTE_OWNER == -1) // No random goodies for robots we weren't in control of
1242 else if (robptr->contains_count) {
1243 d_srand(timer_get_approx_seconds());
1244 if (((d_rand() * 16) >> 15) < robptr->contains_prob) {
1245 del_obj->contains_count = ((d_rand() * robptr->contains_count) >> 15) + 1;
1246 del_obj->contains_type = robptr->contains_type;
1247 del_obj->contains_id = robptr->contains_id;
1248 if (del_obj->contains_type == OBJ_POWERUP)
1250 maybe_replace_powerup_with_energy(del_obj);
1251 if (!multi_powerup_is_allowed(del_obj->contains_id))
1252 del_obj->contains_id=POW_SHIELD_BOOST;
1256 if (del_obj->contains_count > 0)
1257 egg_objnum = object_create_egg(del_obj);
1261 if (egg_objnum >= 0) {
1262 // Transmit the object creation to the other players
1263 mprintf((0, "Dropped %d powerups for robot %d.\n", Net_create_loc, OBJECT_NUMBER(del_obj)));
1264 multi_send_create_robot_powerups(del_obj);
1268 // -----------------------------------------------------------------------------
1269 // Robot *robot got whacked by player player_num and requests permission to do something about it.
1270 // Note: This function will be called regardless of whether Game_mode is a multiplayer mode, so it
1271 // should quick-out if not in a multiplayer mode. On the other hand, it only gets called when a
1272 // player or player weapon whacks a robot, so it happens rarely.
1273 void multi_robot_request_change(object *robot, int player_num)
1275 int slot, remote_objnum;
1278 if (!(Game_mode & GM_MULTI_ROBOTS))
1281 // if (robot->ctype.ai_info.REMOTE_OWNER == Player_num)
1284 slot = robot->ctype.ai_info.REMOTE_SLOT_NUM;
1286 if ((slot < 0) || (slot >= MAX_ROBOTS_CONTROLLED)) {
1291 remote_objnum = objnum_local_to_remote(OBJECT_NUMBER(robot), &dummy);
1292 if (remote_objnum < 0)
1295 mprintf((0, "request_change(): my pri %d, player %d's pri %d.\n", MULTI_ROBOT_PRIORITY(remote_objnum, Player_num),
1296 player_num, MULTI_ROBOT_PRIORITY(remote_objnum, player_num)));
1298 if ( (robot_agitation[slot] < 70) || (MULTI_ROBOT_PRIORITY(remote_objnum, player_num) > MULTI_ROBOT_PRIORITY(remote_objnum, Player_num)) || (d_rand() > 0x4400))
1300 mprintf((0, "Robot %d (%d) released because it got hit by Player %d.\n", OBJECT_NUMBER(robot), remote_objnum, player_num));
1301 if (robot_send_pending[slot])
1302 multi_send_robot_position(robot_controlled[slot], -1);
1303 multi_send_release_robot(robot_controlled[slot]);
1304 robot->ctype.ai_info.REMOTE_SLOT_NUM = 5; // Hands-off period