]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_respawn.cpp
added copyright header
[taylor/freespace2.git] / src / network / multi_respawn.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Network/multi_respawn.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * $Log$
16  * Revision 1.3  2002/06/09 04:41:23  relnev
17  * added copyright header
18  *
19  * Revision 1.2  2002/05/07 03:16:47  theoddone33
20  * The Great Newline Fix
21  *
22  * Revision 1.1.1.1  2002/05/03 03:28:10  root
23  * Initial import.
24  * 
25  * 
26  * 24    9/13/99 4:52p Dave
27  * RESPAWN FIX
28  * 
29  * 23    8/24/99 8:55p Dave
30  * Make sure nondimming pixels work properly in tech menu.
31  * 
32  * 22    8/22/99 5:53p Dave
33  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
34  * instead of ship designations for multiplayer players.
35  * 
36  * 21    8/06/99 9:46p Dave
37  * Hopefully final changes for the demo.
38  * 
39  * 20    8/06/99 2:44a Dave
40  * Make sure dead players who leave respawn AI.
41  * 
42  * 19    8/03/99 11:02p Dave
43  * Maybe fixed sync problems in multiplayer.
44  * 
45  * 18    6/16/99 6:33p Dave
46  * Fixed HUD text problem in multiplayer respawns.
47  * 
48  * 17    4/02/99 4:11p Anoop
49  * Temporary fix for RFC code.
50  * 
51  * 16    3/19/99 9:51a Dave
52  * Checkin to repair massive source safe crash. Also added support for
53  * pof-style nebulae, and some new weapons code.
54  * 
55  * 16    3/12/99 3:13p Anoop
56  * Temporary fix to get by dumb TvT respawning problem.
57  * 
58  * 15    3/10/99 6:50p Dave
59  * Changed the way we buffer packets for all clients. Optimized turret
60  * fired packets. Did some weapon firing optimizations.
61  * 
62  * 14    3/09/99 6:24p Dave
63  * More work on object update revamping. Identified several sources of
64  * unnecessary bandwidth.
65  * 
66  * 13    3/02/99 9:25p Dave
67  * Added a bunch of model rendering debug code. Started work on fixing
68  * beam weapon wacky firing.
69  * 
70  * 12    3/01/99 7:39p Dave
71  * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
72  * don't mix respawn points.
73  * 
74  * 11    2/24/99 4:12p Anoop
75  * Switched TEAM_TRAITOR on respawn from TvT to dogfight mode.
76  * 
77  * 10    2/24/99 3:57p Dave
78  * Fixed yet another TEAM_TRAITOR problem in non-dogfight multiplayer.
79  * 
80  * 9     2/24/99 2:25p Dave
81  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
82  * bug for dogfight more.
83  * 
84  * 8     2/23/99 8:11p Dave
85  * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
86  * Small pass over todolist items.
87  * 
88  * 7     2/23/99 2:29p Dave
89  * First run of oldschool dogfight mode. 
90  * 
91  * 6     2/11/99 3:08p Dave
92  * PXO refresh button. Very preliminary squad war support.
93  * 
94  * 5     11/19/98 8:03a Dave
95  * Full support for D3-style reliable sockets. Revamped packet lag/loss
96  * system, made it receiver side and at the lowest possible level.
97  * 
98  * 4     11/17/98 11:12a Dave
99  * Removed player identification by address. Now assign explicit id #'s.
100  * 
101  * 3     11/05/98 5:55p Dave
102  * Big pass at reducing #includes
103  * 
104  * 2     10/07/98 10:53a Dave
105  * Initial checkin.
106  * 
107  * 1     10/07/98 10:50a Dave 
108  *  
109  * $NoKeywords: $
110  */
111
112 #include "systemvars.h"
113 #include "multi.h"
114 #include "object.h"
115 #include "linklist.h"
116 #include "multimsgs.h"
117 #include "multiutil.h"
118 #include "missionweaponchoice.h"
119 #include "observer.h"
120 #include "gamesequence.h"
121 #include "hudconfig.h"
122 #include "hudobserver.h"
123 #include "hudmessage.h"
124 #include "multi_respawn.h"
125 #include "multi_observer.h"
126 #include "multi_team.h"
127 #include "hudwingmanstatus.h"
128 #include "missionparse.h"
129 #include "multiteamselect.h"
130 #include "timer.h"
131
132 // ---------------------------------------------------------------------------------------
133 // MULTI RESPAWN DEFINES/VARS
134 //
135
136 // respawn notice codes
137 #define RESPAWN_BROADCAST                       0x1             // server to clients - create this ship, and if you're the respawner, set it to be your object
138 #define RESPAWN_REQUEST                         0x2             // client to server requesting a respawn (observer or normal)
139 #define AI_RESPAWN_NOTICE                       0x3             // respawn an AI ship
140
141 // struct used to store AI objects which should get respawned
142 #define MAX_AI_RESPAWNS                         MAX_PLAYERS
143 #define AI_RESPAWN_TIME                         (7000)                  // should respawn 2.0 seconds after they die
144
145 typedef struct ai_respawn
146 {
147         p_object                *pobjp;                         // parse object
148         int                     timestamp;                      // timestamp when this object should get respawned
149 } ai_respawn;
150
151 ai_respawn Ai_respawns[MAX_AI_RESPAWNS];                        // this is way too many, but I don't care
152
153 // respawn point
154 typedef struct respawn_point {
155         char ship_name[NAME_LENGTH+1];                                  // for priority respawns
156         vector pos;                                                                                             // respawn location (non-priority respawns)
157         int team;                                                                                               // team it belongs to
158 } respawn_point;
159
160 // respawn points
161 #define MAX_MULTI_RESPAWN_POINTS                                        MAX_PLAYERS
162 respawn_point Multi_respawn_points[MAX_MULTI_RESPAWN_POINTS];
163 int Multi_respawn_point_count = 0;
164 int Multi_next_respawn_point = 0;
165
166 // priority ships for respawning
167 #define MAX_PRIORITY_POINTS                                             10
168 respawn_point Multi_respawn_priority_ships[MAX_PRIORITY_POINTS];
169 int Multi_respawn_priority_count = 0;
170
171 // ---------------------------------------------------------------------------------------
172 // MULTI RESPAWN FORWARD DECLARATIONS
173 //
174
175 // respawn the passed player with the passed ship object and weapon link settings
176 void multi_respawn_player(net_player *pl, char cur_primary_bank, char cur_secondary_bank, ubyte cur_link_status, ushort ship_ets, ushort net_sign, char *parse_name, vector *pos = NULL);
177
178 // respawn an AI ship
179 void multi_respawn_ai(p_object *pobjp);
180
181 // respawn myself as an observer
182 void multi_respawn_as_observer();
183
184 // send a request to the server saying I want to respawn (as an observer or not)
185 void multi_respawn_send_request(int as_observer);
186
187 // send a request to respawn an AI ship
188 void multi_respawn_send_ai_respawn(ushort net_signature);
189
190 // send a broadcast pack indicating a player has respawned
191 void multi_respawn_broadcast(net_player *player);
192
193 // <server> make the given player an observer
194 void multi_respawn_make_observer(net_player *pl);
195
196 // place a newly respawned object intelligently
197 void multi_respawn_place(object *new_obj, int team);
198
199 // respawn the server immediately
200 void multi_respawn_server();
201
202
203 // ---------------------------------------------------------------------------------------
204 // MULTI RESPAWN FUNCTIONS
205 //
206
207 // check to see if a net player needs to be respawned
208 void multi_respawn_check(object *objp)
209 {
210         int player_index;
211         net_player *pl = NULL;
212         p_object *pobjp;
213
214         // get the parse object since we are storing all data for the respawns in the parse object
215         pobjp = mission_parse_get_arrival_ship( objp->net_signature );
216         
217         // the server should check against all players
218         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
219                 player_index = multi_find_player_by_object(objp);
220                 if(player_index != -1){
221                         pl = &Net_players[player_index];
222                 }
223         }
224         // clients should just check against themselves
225         else if(objp == Player_obj){
226                 pl = Net_player;                
227         }       
228
229         // if this ship isn't a player ship, then maybe it is an AI ship which should get respawed.  Only respawn
230         // on the server, then send message to respawn on client.
231         if( pl == NULL ) {
232
233                 // try and find the parse object with this net signature.  If we found it, and it's a player start
234                 // position, respawn it.
235                 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
236                         if ( !pobjp ){
237                                 return;
238                         }
239
240                         // if we need to respawn this ai ship, add him to a list of ships to get respawned
241                         if ( (pobjp->flags & P_OF_PLAYER_START) && (pobjp->respawn_count < Netgame.respawn) && !(Netgame.type_flags & NG_TYPE_DOGFIGHT) ){
242                                 int i;
243
244                                 for (i = 0; i < MAX_AI_RESPAWNS; i++ ) {
245                                         if ( Ai_respawns[i].pobjp == NULL ) {
246                                                 Ai_respawns[i].pobjp = pobjp;
247                                                 Ai_respawns[i].timestamp = timestamp(AI_RESPAWN_TIME);
248                                                 break;
249                                         }
250                                 }
251                                 Assert( i < MAX_AI_RESPAWNS );
252                         }
253                 }
254
255                 return;
256         } else {
257                 // reset his datarate timestamp
258                 extern int OO_gran;
259                 pl->s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
260         }
261
262         Assert( pl != NULL );
263         Assert( pobjp );                                // we have a player, and we should have a record of it.
264         
265         // mark the player as in the state of respawning
266         if( (pobjp->respawn_count < Netgame.respawn) || (Netgame.type_flags & NG_TYPE_DOGFIGHT) ){
267                 pl->flags |= NETINFO_FLAG_RESPAWNING;
268         }
269         // otherwise mark the player as being in limbo
270         else {
271                 pl->flags |= NETINFO_FLAG_LIMBO;
272         }
273 }
274
275 // notify of a player leaving
276 void multi_respawn_player_leave(net_player *pl)
277 {
278         // bogus
279         if(pl == NULL){
280                 return;
281         }
282         if( MULTI_OBSERVER((*pl)) ){
283                 return;
284         }
285         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
286                 return;
287         }
288         if(pl->p_info.p_objp == NULL){
289                 return;
290         }
291
292         // dogfight mode
293         if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){
294                 return;
295         }       
296
297         // if we need to respawn this ai ship, add him to a list of ships to get respawned
298         p_object *pobjp = pl->p_info.p_objp;
299         if ( (pobjp->flags & P_OF_PLAYER_START) && (pobjp->respawn_count < Netgame.respawn) ){
300                 int i;
301
302                 for (i = 0; i < MAX_AI_RESPAWNS; i++ ) {
303                         if ( Ai_respawns[i].pobjp == NULL ) {
304                                 Ai_respawns[i].pobjp = pobjp;
305                                 Ai_respawns[i].timestamp = timestamp(AI_RESPAWN_TIME);
306                                 break;
307                         }
308                 }
309         }
310 }
311
312 // respawn normally
313 void multi_respawn_normal()
314 {
315         // make sure we should be respawning and _not_ as an observer
316         Assert((Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO));
317         
318         // server respawns immediately
319         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
320                 multi_respawn_server();
321         } else {
322                 // client sends a respawn request (multiple ones are ok if he clicks over and over)
323                 multi_respawn_send_request(0);
324         }
325 }
326
327 // respawn as an observer
328 void multi_respawn_observer()
329 {
330         // make sure we should be respawning as an observer 
331         Assert(!(Net_player->flags & NETINFO_FLAG_RESPAWNING) && (Net_player->flags & NETINFO_FLAG_LIMBO));
332
333         // respawn as an observer
334         multi_respawn_as_observer();
335
336         // clients should notify the server that they are doing so
337         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
338                 multi_respawn_send_request(1);
339         }       
340
341         // jump back into the game right away
342         gameseq_post_event(GS_EVENT_ENTER_GAME);                                
343 }       
344
345 // server should check to see if any respawned players have run out of their invulnerability
346 void multi_respawn_handle_invul_players()
347 {
348         int idx;
349         object *objp;
350         for(idx=0;idx<MAX_PLAYERS;idx++){
351                 if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].flags & OF_INVULNERABLE)){    
352                         // make him normal (_non_ invulnerable) on either of 2 conditions :
353                         // 1.) More than 5 seconds have passed
354                         // 2.) He's fired either a primary or a secondary weapon
355                         if( ((Net_players[idx].s_info.invul_timestamp != -1) && (timestamp_elapsed(Net_players[idx].s_info.invul_timestamp))) ||
356                                  ((Net_players[idx].player->ci.fire_primary_count > 0) || (Net_players[idx].player->ci.fire_secondary_count > 0)) ) {
357                                 objp = &Objects[Net_players[idx].player->objnum];
358                                 obj_set_flags(objp,objp->flags & ~(OF_INVULNERABLE));
359                         }
360                 }
361         }
362 }
363
364 // build a list of respawn points for the mission
365 void multi_respawn_build_points()
366 {
367         ship_obj *moveup;
368         respawn_point *r;
369
370         // respawn points
371         Multi_respawn_point_count = 0;
372         Multi_next_respawn_point = 0;
373         moveup = GET_FIRST(&Ship_obj_list);
374         while(moveup != END_OF_LIST(&Ship_obj_list)){
375                 // player ships
376                 if(Objects[moveup->objnum].flags & (OF_PLAYER_SHIP | OF_COULD_BE_PLAYER)){                      
377                         r = &Multi_respawn_points[Multi_respawn_point_count++];
378                         
379                         r->pos = Objects[moveup->objnum].pos;
380                         r->team = Ships[Objects[moveup->objnum].instance].team;                 
381                 }
382                 moveup = GET_NEXT(moveup);
383         }       
384
385         // priority respawn points
386         Multi_respawn_priority_count = 0;
387         moveup = GET_FIRST(&Ship_obj_list);
388         while(moveup != END_OF_LIST(&Ship_obj_list)){
389                 // stuff info
390                 if((Ships[Objects[moveup->objnum].instance].respawn_priority > 0) && (Multi_respawn_priority_count < MAX_PRIORITY_POINTS)){
391                         r = &Multi_respawn_priority_ships[Multi_respawn_priority_count++];
392
393                         strcpy(r->ship_name, Ships[Objects[moveup->objnum].instance].ship_name);
394                         r->team = Ships[Objects[moveup->objnum].instance].team;
395                 }
396                 moveup = GET_NEXT(moveup);
397         }       
398 }
399
400
401 // ---------------------------------------------------------------------------------------
402 // MULTI RESPAWN FORWARD DECLARATIONS
403 //
404
405 void multi_respawn_wing_stuff(ship *shipp)
406 {
407         wing *wingp;
408
409         // deal with re-adding this ship to it's wing
410         Assert( shipp->wingnum != -1 );
411         wingp = &Wings[shipp->wingnum];
412         wingp->ship_index[wingp->current_count] = SHIP_INDEX(shipp);
413         wingp->current_count++;
414
415         hud_set_wingman_status_alive(shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
416 }
417
418 int multi_respawn_common_stuff(p_object *pobjp)
419 {
420         int objnum, team, slot_index;
421         object *objp;
422         ship *shipp;
423         int idx;
424
425         // create the object
426         objnum = parse_create_object(pobjp);
427         Assert(objnum != -1);
428         objp = &Objects[objnum];
429
430         // get the team and slot
431         shipp = &Ships[objp->instance];
432         multi_ts_get_team_and_slot(shipp->ship_name, &team, &slot_index);
433         Assert( team != -1 );
434         Assert( slot_index != -1 );
435
436         // reset object update stuff
437         for(idx=0; idx<MAX_PLAYERS; idx++){
438                 shipp->np_updates[idx].orient_chksum = 0;
439                 shipp->np_updates[idx].pos_chksum = 0;
440                 shipp->np_updates[idx].seq = 0;
441                 shipp->np_updates[idx].status_update_stamp = -1;
442                 shipp->np_updates[idx].subsys_update_stamp = -1;
443                 shipp->np_updates[idx].update_stamp = -1;
444         }
445
446         // change the ship type and the weapons
447         change_ship_type(objp->instance, Wss_slots_teams[team][slot_index].ship_class);
448         wl_bash_ship_weapons(&shipp->weapons,&Wss_slots_teams[team][slot_index]);
449         multi_respawn_wing_stuff( shipp );
450
451         if(Netgame.type_flags & NG_TYPE_TEAM){
452                 multi_team_mark_ship(&Ships[Objects[objnum].instance]);
453         }
454
455         pobjp->respawn_count++;
456
457         return objnum;
458 }
459
460 // respawn the passed player with the passed ship object and weapon link settings
461 void multi_respawn_player(net_player *pl, char cur_primary_bank, char cur_secondary_bank, ubyte cur_link_status, ushort ship_ets, ushort net_sig, char *parse_name, vector *pos)
462 {
463         int objnum;
464         object *objp;
465         ship *shipp;
466         p_object *pobjp;        
467
468         // try and find the parse object
469         pobjp = mission_parse_get_arrival_ship(parse_name);             
470         Assert(pobjp != NULL);
471         if(pobjp == NULL){
472                 return;
473         }
474         objnum = multi_respawn_common_stuff(pobjp);
475
476         Assert( objnum != -1 );
477         objp = &Objects[objnum];
478         shipp = &Ships[objp->instance]; 
479
480         // this is a player, so mark him as a player,
481         objp->flags |= OF_PLAYER_SHIP;
482         objp->flags &= ~OF_COULD_BE_PLAYER;
483
484         // server should mark this player as invulerable for a short time
485         if ( MULTIPLAYER_MASTER ) {
486                 objp->flags |= OF_INVULNERABLE;
487                 pl->s_info.invul_timestamp = timestamp(RESPAWN_INVUL_TIMESTAMP);                                        
488                 multi_respawn_place( objp, shipp->team );
489         }
490
491         // reset his datarate timestamp
492         extern int OO_gran;
493         pl->s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
494
495         // set some player information
496         pl->player->objnum = objnum;
497         if ( pl == Net_player ) {
498                 // this is a hack to ensure that old (dead) player ships are destroyed, since at this point he's actually an OBJ_GHOST
499                 Player_obj->flags |= OF_SHOULD_BE_DEAD;                                         
500                 obj_delete(OBJ_INDEX(Player_obj));      
501                 
502                 Player_obj = objp;
503                 Player_ship = shipp;
504                 Player_ai = &Ai_info[Player_ship->ai_index];
505
506                 //      get rid of the annoying HUD dead message text.
507                 HUD_init_fixed_text();
508         }       
509
510         // clients bash net signature
511         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
512                 objp->net_signature = net_sig;
513         }
514         
515         // restore the correct weapon bank selections
516         shipp->weapons.current_primary_bank = (int)cur_primary_bank;
517         shipp->weapons.current_secondary_bank = (int)cur_secondary_bank;
518         if(cur_link_status & (1<<0)){
519                 shipp->flags |= SF_PRIMARY_LINKED;
520         } else {
521                 shipp->flags &= ~(SF_PRIMARY_LINKED);
522         }                       
523         if(cur_link_status & (1<<1)){
524                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
525         } else {
526                 shipp->flags &= ~(SF_SECONDARY_DUAL_FIRE);
527         }
528
529         Assert( ship_ets != 0 );                // find dave or allender
530
531         // restore the correct ets settings
532         shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
533         // weapon ets
534         shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
535         // engine ets
536         shipp->engine_recharge_index = (ship_ets & 0x000f);
537
538         // if this is a dogfight mission, make him TEAM_TRAITOR
539         if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
540                 shipp->team = TEAM_TRAITOR;
541         }
542
543         // maybe bash ship position
544         if(pos != NULL){
545                 objp->pos = *pos;
546         }
547
548         // unset his respawning flag
549         pl->flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO);
550
551         // blast his control and button info clear
552         memset(&pl->player->bi, 0, sizeof(pl->player->bi));
553         memset(&pl->player->ci, 0, sizeof(pl->player->ci));
554
555         // if this is me, clear accum button info
556         if(pl == Net_player){
557                 // clear multiplayer button info                        
558                 extern button_info Multi_ship_status_bi;
559                 memset(&Multi_ship_status_bi, 0, sizeof(button_info));
560         }
561
562         // notify other players of the respawn
563         if ( MULTIPLAYER_MASTER ){
564                 multi_respawn_broadcast(pl);
565         }
566 }
567
568 // respawns an AI ship.
569 void multi_respawn_ai( p_object *pobjp )
570 {
571         int objnum;
572         object *objp;
573
574         // create the object and change the ship type
575         objnum = multi_respawn_common_stuff( pobjp );
576         objp = &Objects[objnum];
577
578         // be sure the the OF_PLAYER_SHIP flag is unset, and the could be player flag is set
579         obj_set_flags( objp, objp->flags | OF_COULD_BE_PLAYER );
580         objp->flags &= ~OF_PLAYER_SHIP;
581 }
582
583 // <server> make the given player an observer
584 void multi_respawn_make_observer(net_player *pl)
585 {       
586         pl->flags |= (NETINFO_FLAG_OBSERVER | NETINFO_FLAG_OBS_PLAYER);         
587         pl->flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO);
588
589         // MWA 6/3/98 -- don't set to high -- let it default to whatever player chose 
590         //pl->p_info.options.obj_update_level = OBJ_UPDATE_HIGH;
591         pl->last_heard_time = timer_get_fixed_seconds();        
592
593         // reset the ping time for this player
594         multi_ping_reset(&pl->s_info.ping);
595         
596         // timestamp his last_full_update_time
597         pl->s_info.last_full_update_time = timestamp(0);
598
599         // create an observer object for him
600         multi_obs_create_observer(pl);          
601 }
602
603 // respawn myself as an observer
604 void multi_respawn_as_observer()
605 {
606         // configure the hud to be in "observer" mode
607         hud_config_as_observer(Player_ship,Player_ai);  
608
609         // blow away my old player object
610         Player_obj->flags |= OF_SHOULD_BE_DEAD;
611         obj_delete(OBJ_INDEX(Player_obj));
612
613         // create a new shiny observer object for me
614         multi_obs_create_observer(Net_player);
615         
616         // set my object to be the observer object
617         Player_obj = &Objects[Net_player->player->objnum];
618         Player_ship = &Hud_obs_ship;    
619         Player_ai = &Hud_obs_ai;        
620         
621         // set some flags for myself
622         Net_player->flags |= NETINFO_FLAG_OBSERVER;
623         Net_player->flags |= NETINFO_FLAG_OBS_PLAYER;
624         Net_player->flags &= ~(NETINFO_FLAG_LIMBO);
625
626         // clear my auto-match speed flag
627         Net_player->player->flags &= ~(PLAYER_FLAGS_AUTO_MATCH_SPEED | PLAYER_FLAGS_MATCH_TARGET);
628         
629         // reset the control info structure
630         memset(&Player->ci,0,sizeof(control_info));     
631 }
632
633 // send a request to respawn an AI object
634 void multi_respawn_send_ai_respawn( ushort net_signature )
635 {
636         ubyte data[50],val;
637         int packet_size = 0;
638
639         // build the header and add the opcode
640         BUILD_HEADER(RESPAWN_NOTICE);
641         val = AI_RESPAWN_NOTICE;
642         ADD_DATA(val);
643         ADD_DATA( net_signature );
644
645         // broadcast the packet to all players
646         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
647         multi_io_send_to_all_reliable(data, packet_size);       
648 }
649
650 // send a request to the server saying I want to respawn (as an observer or not)
651 void multi_respawn_send_request(int as_observer)
652 {
653         ubyte data[10],val;
654         int packet_size = 0;
655
656         // build the header and add the opcode
657         BUILD_HEADER(RESPAWN_NOTICE);
658         val = RESPAWN_REQUEST;
659         ADD_DATA(val);
660
661         // add a byte indicating whether or not we want to respawn as an observer
662         val = (ubyte)as_observer;
663         ADD_DATA(val);
664
665         // send the request to the server       
666         multi_io_send_reliable(Net_player, data, packet_size);
667 }
668
669 // send a broadcast pack indicating a player has respawned
670 void multi_respawn_broadcast(net_player *np)
671 {
672         ubyte data[50],val;
673         int packet_size = 0;
674         ushort signature;
675         vector pos;
676
677         // broadcast the packet to all players
678         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
679
680         signature = Objects[np->player->objnum].net_signature;
681         pos = Objects[np->player->objnum].pos;
682
683         // build the header and add the opcode
684         BUILD_HEADER(RESPAWN_NOTICE);
685         val = RESPAWN_BROADCAST;
686         ADD_DATA(val);
687
688         // add the data for the respawn
689         ADD_DATA(signature);
690         ADD_DATA(pos);
691         ADD_DATA(np->player_id);
692         ADD_DATA(np->s_info.cur_primary_bank);
693         ADD_DATA(np->s_info.cur_secondary_bank);
694         ADD_DATA(np->s_info.cur_link_status);
695         ADD_DATA(np->s_info.ship_ets);
696         ADD_STRING(np->p_info.p_objp->name);
697
698         Assert( np->s_info.ship_ets != 0 );             // find dave or allender
699
700         multi_io_send_to_all_reliable(data, packet_size);
701 }
702
703 // process an incoming respawn info packet
704 void multi_respawn_process_packet(ubyte *data, header *hinfo)
705 {
706         ubyte code,cur_link_status;
707         char cur_primary_bank,cur_secondary_bank;
708         ushort net_sig,ship_ets;
709         short player_id;
710         int player_index;
711         vector v;       
712         char parse_name[1024] = "";
713         int offset = HEADER_LENGTH;
714
715         // determine who send the packet        
716         player_index = find_player_id(hinfo->id);
717         if(player_index == -1){
718                 nprintf(("Network","Couldn't find player for processing respawn packet!\n"));
719         }
720
721         // get the opcode
722         GET_DATA(code);
723
724         // do something based upon the opcode
725         switch((int)code){
726
727         case AI_RESPAWN_NOTICE: 
728                 p_object *pobjp;
729
730                 GET_DATA( net_sig );
731                 pobjp = mission_parse_get_arrival_ship( net_sig );
732                 Assert( pobjp != NULL );
733                 multi_respawn_ai( pobjp );
734                 break;          
735
736         case RESPAWN_BROADCAST:
737                 // get the respawn data
738                 GET_DATA(net_sig);
739                 GET_DATA(v);
740                 GET_DATA(player_id);
741                 GET_DATA(cur_primary_bank);
742                 GET_DATA(cur_secondary_bank);
743                 GET_DATA(cur_link_status);
744                 GET_DATA(ship_ets);
745                 GET_STRING(parse_name);
746                 player_index = find_player_id(player_id);
747                 if(player_index == -1){
748                         nprintf(("Network","Couldn't find player to respawn!\n"));
749                         break;
750                 }
751
752                 // create the ship and assign its position, net_signature, and class
753                 // respawn the player
754                 multi_respawn_player(&Net_players[player_index], cur_primary_bank, cur_secondary_bank, cur_link_status, ship_ets, net_sig, parse_name, &v);
755
756                 // if this is for me, I should jump back into gameplay
757                 if(&Net_players[player_index] == Net_player){
758                         extern int Player_multi_died_check;
759                         Player_multi_died_check = -1;
760
761                         gameseq_post_event(GS_EVENT_ENTER_GAME);
762                 }
763                 break;
764         
765         case RESPAWN_REQUEST:
766                 // determine whether he wants to respawn as an observer or not
767                 GET_DATA(code);
768
769                 nprintf(("Network","Received respawn request\n"));
770                 if(player_index == -1){
771                         nprintf(("Network","Received respawn request from unknown player!\n"));
772                         break;
773                 }                               
774
775                 // make sure he's not making an invalid request
776                 if((code == 0) && !(Net_players[player_index].flags & NETINFO_FLAG_RESPAWNING)){
777                         nprintf(("Network","Received respawn request from player who shouldn't be respawning!\n"));
778                         Int3();
779                         break;
780                 } else if((code == 1) && !(Net_players[player_index].flags & NETINFO_FLAG_LIMBO)){
781                         nprintf(("Network","Received respawn observer request from a player who shouldn't be respawning as an observer!\n"));
782                         Int3();
783                         break;
784                 }
785
786                 // otherwise perform the operation
787                 // respawn the guy as an observer
788                 if(code){
789                         multi_respawn_make_observer(&Net_players[player_index]);                        
790                 }
791                 // respawn him as normal
792                 else {                                          
793                         // create his new ship, and change him from respawning to respawned
794                         Assert(Net_players[player_index].p_info.p_objp != NULL);
795                         if(Net_players[player_index].p_info.p_objp != NULL){
796                                 multi_respawn_player(&Net_players[player_index], Net_players[player_index].s_info.cur_primary_bank, Net_players[player_index].s_info.cur_secondary_bank,Net_players[player_index].s_info.cur_link_status, Net_players[player_index].s_info.ship_ets, 0, Net_players[player_index].p_info.p_objp->name);
797                         }                       
798                 }       
799                 break;
800         }
801
802         PACKET_SET_SIZE();
803 }
804
805 // respawn the server immediately
806 void multi_respawn_server()
807 {       
808         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
809
810         // respawn me
811         multi_respawn_player(Net_player, Net_player->s_info.cur_primary_bank, Net_player->s_info.cur_secondary_bank, Net_player->s_info.cur_link_status, Net_player->s_info.ship_ets, 0, Net_player->p_info.p_objp->name);
812
813         // jump back into the game
814         gameseq_post_event(GS_EVENT_ENTER_GAME);        
815 }
816
817 // level init for respawn stuff
818 void multi_respawn_init()
819 {
820         int i;
821
822         for (i = 0; i < MAX_AI_RESPAWNS; i++ ) {
823                 Ai_respawns[i].pobjp = NULL;
824                 Ai_respawns[i].timestamp = timestamp(-1);
825         }
826 }
827
828 // function to detect whether or not we have AI ships to respawn this frame
829 void multi_respawn_check_ai()
830 {
831         int i;
832
833         for (i = 0; i < MAX_AI_RESPAWNS; i++ ) {
834                 if ( Ai_respawns[i].pobjp != NULL ) {
835                         if ( timestamp_elapsed(Ai_respawns[i].timestamp) ) {
836
837                                 // be sure that ship is actually gone before respawning it.
838                                 if ( ship_name_lookup(Ai_respawns[i].pobjp->name) != -1 ) {
839                                         Ai_respawns[i].timestamp = timestamp(1000);
840                                 } else {
841                                         multi_respawn_ai( Ai_respawns[i].pobjp );
842                                         multi_respawn_send_ai_respawn( Ai_respawns[i].pobjp->net_signature );
843                                         Ai_respawns[i].pobjp = NULL;
844                                         Ai_respawns[i].timestamp = timestamp(-1);
845                                 }
846                         }
847                 }
848         }
849 }
850
851
852
853 // this is a completely off the cuff way of doing things. Feel free to find a better way.
854 // Currently :
855 // 1. Take the average vector position of all the ships in the game
856 // 2. Check to make sure we aren't within the radius of any of the ships in the game
857 //    a.) If we are, move away along the vector between the two ships (by the radius of the ship it collided with)
858 //    b.) repeat step 2
859 /*
860 #define MOVE_AWAY() { vector away; vm_vec_sub(&away,&new_obj->pos,&hit_check->pos); \
861                            vm_vec_normalize_quick(&away); vm_vec_scale(&away,hit_check->radius+hit_check->radius); \
862                                                          vm_vec_add2(&new_obj->pos,&away); }
863
864 #define WITHIN_RADIUS() { float dist; dist=vm_vec_dist(&new_obj->pos,&hit_check->pos); \
865                                if(dist <= hit_check->radius) collided = 1; }
866 */
867
868 #define WITHIN_BBOX()   do { \
869         float scale = 2.0f; \
870         polymodel *pm = model_get(s_check->modelnum); \
871         collided = 0; \
872         if(pm != NULL){ \
873                 vector temp = new_obj->pos; \
874                 vector gpos; \
875                 vm_vec_sub2(&temp, &hit_check->pos); \
876                 vm_vec_rotate(&gpos, &temp, &hit_check->orient); \
877                 if((gpos.x >= pm->mins.x * scale) && (gpos.y >= pm->mins.y * scale) && (gpos.z >= pm->mins.z * scale) && (gpos.x <= pm->maxs.x * scale) && (gpos.y <= pm->maxs.y * scale) && (gpos.z <= pm->maxs.z * scale)) { \
878                         collided = 1; \
879                 } \
880         } \
881 } while(0)
882
883 #define MOVE_AWAY_BBOX() do { \
884         polymodel *pm = model_get(s_check->modelnum); \
885         if(pm != NULL){ \
886                 switch((int)frand_range(0.0f, 3.9f)){ \
887                 case 0: \
888                         new_obj->pos.x += 200.0f; \
889                         break; \
890                 case 1: \
891                         new_obj->pos.x -= 200.0f; \
892                         break; \
893                 case 2: \
894                         new_obj->pos.y += 200.0f; \
895                         break; \
896                 case 3: \
897                         new_obj->pos.y -= 200.0f; \
898                         break; \
899                 default : \
900                         new_obj->pos.z -= 200.0f; \
901                         break; \
902                 } \
903         } \
904 } while(0)
905
906 void multi_respawn_place(object *new_obj, int team)
907 {
908         ship_obj *moveup;
909         ship *s_check;
910         ship *pri = NULL;
911         object *pri_obj = NULL;
912         object *hit_check;
913         int collided, idx, lookup;
914
915         // first determine if there are any appropriate priority ships to use
916         pri = NULL;
917         pri_obj = NULL;
918         for(idx=0; idx<Multi_respawn_priority_count; idx++){
919                 // all relevant ships
920                 if((Multi_respawn_priority_ships[idx].team == team) || !(Netgame.type_flags & NG_TYPE_TEAM)){
921
922                         lookup = ship_name_lookup(Multi_respawn_priority_ships[idx].ship_name);
923                         if( (lookup >= 0) && ((pri == NULL) || (Ships[lookup].respawn_priority > pri->respawn_priority)) && (Ships[lookup].objnum >= 0) && (Ships[lookup].objnum < MAX_OBJECTS)){
924                                 pri = &Ships[lookup];
925                                 pri_obj = &Objects[Ships[lookup].objnum];
926                         }
927                 }
928         }
929         
930         // if we have a relevant respawn ship
931         if((pri != NULL) && (pri_obj != NULL)){
932                 // pick a point just outside his bounding box
933                 polymodel *pm = model_get(pri->modelnum); 
934
935                 // hmm, ugly. Pick a point 2000 meters to the y direction
936                 if(pm == NULL){                 
937                         vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.rvec, 2000.0f);
938                 } else {
939                         // pick a random direction
940                         int d = (int)frand_range(0.0f, 5.9f);
941                         switch(d){
942                         case 0:
943                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.rvec, (pm->maxs.x - pm->mins.x)); 
944                                 break;
945
946                         case 1:
947                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.rvec, -(pm->maxs.x - pm->mins.x)); 
948                                 break;
949
950                         case 2:
951                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.uvec, (pm->maxs.y - pm->mins.y)); 
952                                 break;
953
954                         case 3:
955                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.uvec, -(pm->maxs.y - pm->mins.y)); 
956                                 break;
957
958                         case 4:
959                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.fvec, (pm->maxs.z - pm->mins.z)); 
960                                 break;
961
962                         case 5:
963                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.fvec, -(pm->maxs.z - pm->mins.z)); 
964                                 break;
965
966                         default:
967                                 vm_vec_scale_add(&new_obj->pos, &pri_obj->pos, &pri_obj->orient.uvec, -(pm->maxs.y - pm->mins.y)); 
968                                 break;
969                         }
970                 }
971         }
972         // otherwise, resort to plain respawn points
973         else {
974                 Assert(Multi_respawn_point_count > 0);
975                 
976                 // get the next appropriate respawn point by team
977                 lookup = 0;             
978                 int count = 0;
979                 while(!lookup && (count < 13)){
980                         if((team == TEAM_TRAITOR) || (team == Multi_respawn_points[Multi_next_respawn_point].team)){
981                                 lookup = 1;
982                         }                       
983
984                         // next item
985                         if(!lookup){
986                                 if(Multi_next_respawn_point >= (Multi_respawn_point_count-1)){
987                                         Multi_next_respawn_point = 0;
988                                 } else {
989                                         Multi_next_respawn_point++;
990                                 }                               
991                         }
992
993                         count++;
994                 }
995
996                 // set respawn info
997                 new_obj->pos = Multi_respawn_points[Multi_next_respawn_point].pos;              
998         }
999
1000         // now make sure we're not colliding with anyone                
1001         do {
1002                 collided = 0;
1003                 moveup = GET_FIRST(&Ship_obj_list);
1004                 while(moveup!=END_OF_LIST(&Ship_obj_list)){
1005                         // don't check the new_obj itself!!
1006                         if(Objects[moveup->objnum].net_signature != new_obj->net_signature){
1007                                 hit_check = &Objects[moveup->objnum];
1008                                 Assert(hit_check->type == OBJ_SHIP);
1009                                 Assert(hit_check->instance >= 0);
1010                                 if((hit_check->type != OBJ_SHIP) || (hit_check->instance < 0)){
1011                                         continue;
1012                                 }
1013                                 s_check = &Ships[hit_check->instance];
1014                                 
1015                                 // just to make sure we don't get any strange magnitude errors
1016                                 if(vm_vec_same(&hit_check->pos, &new_obj->pos)){
1017                                         new_obj->pos.x += 1.0f;
1018                                 }
1019                                 
1020                                 WITHIN_BBOX();                          
1021                                 if(collided){                                           
1022                                         MOVE_AWAY_BBOX();
1023                                         break;
1024                                 } 
1025                                 collided = 0;
1026                         }
1027                         moveup = GET_NEXT(moveup);
1028                 }
1029         } while(collided);              
1030 }
1031