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