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