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