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