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