]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_obj.cpp
remove invalid alignment flag on stand GUI popup
[taylor/freespace2.git] / src / network / multi_obj.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 #include "freespace.h"
10 #include "timer.h"
11 #include "linklist.h"
12 #include "weapon.h"
13 #include "multimsgs.h"
14 #include "multiutil.h"
15 #include "multi_obj.h"
16 #include "multi_rate.h"
17 #include "multi.h"
18 #include "object.h"
19 #include "key.h"
20 #include "gamesnd.h"
21 #include "spline.h"
22 #include "alphacolors.h"
23 #include "afterburner.h"
24
25 // ---------------------------------------------------------------------------------------------------
26 // OBJECT UPDATE DEFINES/VARS
27 //
28
29 // test stuff
30 float oo_arrive_time[MAX_SHIPS][5];                             // the last 5 arrival times for each ship
31 int oo_arrive_time_count[MAX_SHIPS];                    // size of the arrival queue
32 float oo_arrive_time_avg_diff[MAX_SHIPS];               // the average time between arrivals
33 float oo_arrive_time_next[MAX_SHIPS];                   // how many seconds have gone by. should be equal to oo_arrive_time_avg_diff[] the next time we get an update
34
35 // interp stuff
36 int oo_interp_count[MAX_SHIPS];
37 vector oo_interp_points[MAX_SHIPS][2];
38 bez_spline oo_interp_splines[MAX_SHIPS][2];
39 void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info);
40
41 // how much data we're willing to put into a given oo packet
42 #define OO_MAX_SIZE                                     480
43
44 // tolerance for bashing position
45 #define OO_POS_UPDATE_TOLERANCE 100.0f
46
47 // new improved - more compacted info type
48 #define OO_POS_NEW                                      (1<<0)          // 
49 #define OO_ORIENT_NEW                           (1<<1)          // 
50 #define OO_HULL_NEW                                     (1<<2)          // Hull AND shields
51 #define OO_AFTERBURNER_NEW                      (1<<3)          // 
52 #define OO_SUBSYSTEMS_AND_AI_NEW        (1<<4)          // 
53 #define OO_PRIMARY_BANK                         (1<<5)          // if this is set, fighter has selected bank one
54 #define OO_PRIMARY_LINKED                       (1<<6)          // if this is set, banks are linked
55 #define OO_TRIGGER_DOWN                         (1<<7)          // if this is set, trigger is DOWN
56
57 #define OO_VIEW_CONE_DOT                        (0.1f)
58 #define OO_VIEW_DIFF_TOL                        (0.15f)                 // if the dotproducts differ this far between frames, he's coming into view
59
60 // no timestamp should ever have sat for longer than this. 
61 #define OO_MAX_TIMESTAMP                        2500
62
63 // distance class
64 #define OO_NEAR                                         0
65 #define OO_NEAR_DIST                                    (200.0f)
66 #define OO_MIDRANGE                                     1
67 #define OO_MIDRANGE_DIST                        (600.0f)
68 #define OO_FAR                                                  2
69 #define OO_FAR_DIST                                     (1400.0f)
70
71 // how often we should send full hull/shield updates
72 #define OO_HULL_SHIELD_TIME             600
73 #define OO_SUBSYS_TIME                          1000
74
75 // timestamp values for object update times based on client's update level.
76 int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] = 
77 {
78         100,                            // 15x a second 
79         100,                            // 15x a second
80         66,                             // 30x a second
81         66,
82 };
83
84 // for near ships
85 int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] =
86 {
87         150,                            // low update
88         100,                            // medium update
89         66,                             // high update
90         66,
91 };
92
93 // for medium ships
94 int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] =
95 {
96         250,                            // low update
97         180,                            // medium update
98         120,                            // high update
99         66,
100 };
101
102 // for far ships
103 int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] =
104 {
105         750,                            // low update
106         350,                            // medium update
107         150,                            // high update
108         66,
109 };
110
111 // for near ships
112 int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] = 
113 {
114         300,                            // low update
115         200,                            // medium update
116         100,                            // high update
117         66,
118 };
119
120 // for medium ships
121 int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] = 
122 {
123         800,                            // low update
124         600,                            // medium update
125         300,                            // high update
126         66,
127 };
128
129 // for far ships
130 int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] = 
131 {
132         2500,                   // low update
133         1500,                           // medium update
134         400,                            // high update
135         66,
136 };
137
138 // ship index list for possibly sorting ships based upon distance, etc
139 short OO_ship_index[MAX_SHIPS];
140
141 int OO_update_index = -1;                                                       // index into OO_update_records for displaying update record info
142
143 // ---------------------------------------------------------------------------------------------------
144 // OBJECT UPDATE FUNCTIONS
145 //
146
147 object *OO_player_obj;
148 int OO_sort = 1;
149
150 int multi_oo_sort_func(const void *ship1, const void *ship2)
151 {
152         object *obj1, *obj2;
153         short index1, index2;
154         float dist1, dist2;
155         float dot1, dot2;
156         vector v1, v2;
157         vector vn1, vn2;
158
159         // get the 2 indices
160         memcpy(&index1, ship1, sizeof(short));
161         memcpy(&index2, ship2, sizeof(short));
162
163         // if the indices are bogus, or the objnums are bogus, return ">"
164         if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){
165                 return 1;
166         }
167
168         // get the 2 objects
169         obj1 = &Objects[Ships[index1].objnum];
170         obj2 = &Objects[Ships[index2].objnum];
171
172         // get the distance and dot product to the player obj for both
173         vm_vec_sub(&v1, &OO_player_obj->pos, &obj1->pos);
174         dist1 = vm_vec_copy_normalize(&vn1, &v1);
175         vm_vec_sub(&v2, &OO_player_obj->pos, &obj2->pos);
176         dist2 = vm_vec_copy_normalize(&vn2, &v2);
177         dot1 = vm_vec_dotprod(&OO_player_obj->orient.v.fvec, &vn1);
178         dot2 = vm_vec_dotprod(&OO_player_obj->orient.v.fvec, &vn2);
179
180         // objects in front take precedence
181         if((dot1 < 0.0f) && (dot2 >= 0.0f)){
182                 return 1;
183         } else if((dot2 < 0.0f) && (dot1 >= 0.0f)){
184                 return -1;
185         }
186
187         // otherwise go by distance
188         return (dist1 <= dist2) ? -1 : 1;
189 }
190
191 // build the list of ship indices to use when updating for this player
192 void multi_oo_build_ship_list(net_player *pl)
193 {
194         int ship_index;
195         int idx;
196         ship_obj *moveup;
197         object *player_obj;
198
199         // set all indices to be -1
200         for(idx = 0;idx<MAX_SHIPS; idx++){
201                 OO_ship_index[idx] = -1;
202         }
203
204         // get the player object
205         if(pl->player->objnum < 0){
206                 return;
207         }
208         player_obj = &Objects[pl->player->objnum];
209         
210         // go through all other relevant objects
211         ship_index = 0;
212         for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) {
213                 // if it is an invalid ship object, skip it
214                 if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){
215                         continue;
216                 }
217
218                 // if we're a standalone server, don't send any data regarding its pseudo-ship
219                 if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){
220                         continue;
221                 }               
222                         
223                 // must be a ship, a weapon, and _not_ an observer
224                 if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){
225                         continue;
226                 }
227
228                 // don't send info for dying ships
229                 if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){
230                         continue;
231                 }               
232
233                 // never update the knossos device
234                 if ((Ships[Objects[moveup->objnum].instance].ship_info_index >= 0) && (Ships[Objects[moveup->objnum].instance].ship_info_index < Num_ship_types) && (Ship_info[Ships[Objects[moveup->objnum].instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE)){
235                         continue;
236                 }
237                                 
238                 // don't send him info for himself
239                 if ( &Objects[moveup->objnum] == player_obj ){
240                         continue;
241                 }
242
243                 // don't send info for his targeted ship here, since its always done first
244                 if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){
245                         continue;
246                 }
247
248                 // add the ship 
249                 if(ship_index < MAX_SHIPS){
250                         OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance;
251                 }
252         }
253
254         // maybe qsort the thing here
255         OO_player_obj = player_obj;
256         if(OO_sort){
257                 qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func);
258         }
259 }
260
261 // pack information for a client (myself), return bytes added
262 int multi_oo_pack_client_data(ubyte *data)
263 {
264         ubyte out_flags;
265         ushort tnet_signature;
266         char t_subsys, l_subsys;
267         int packet_size = 0;
268
269         // get our firing stuff
270         out_flags = Net_player->s_info.accum_buttons;   
271
272         // zero these values for now
273         Net_player->s_info.accum_buttons = 0;
274
275         // add any necessary targeting flags
276         if ( Player_ai->current_target_is_locked ){
277                 out_flags |= OOC_TARGET_LOCKED;
278         }
279         if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){     
280                 out_flags |= OOC_TARGET_SEEK_LOCK;
281         }
282         if ( Player->locking_on_center ){
283                 out_flags |= OOC_LOCKING_ON_CENTER;
284         }
285         if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){
286                 out_flags |= OOC_TRIGGER_DOWN;
287         }
288
289         // send my bank info
290         if(Player_ship != NULL){
291                 if(Player_ship->weapons.current_primary_bank > 0){
292                         out_flags |= OOC_PRIMARY_BANK;
293                 }
294
295                 // linked or not
296                 if(Player_ship->flags & SF_PRIMARY_LINKED){
297                         out_flags |= OOC_PRIMARY_LINKED;
298                 }
299         }
300
301         // copy the final flags in
302         memcpy(data, &out_flags, sizeof(ubyte));
303         packet_size++;  
304
305         // client targeting information 
306         t_subsys = -1;
307         l_subsys = -1;
308
309         // if nothing targeted
310         if(Player_ai->target_objnum == -1){
311                 tnet_signature = 0;
312         }
313         // if something is targeted 
314         else {
315                 // target net signature
316                 tnet_signature = Objects[Player_ai->target_objnum].net_signature;
317                         
318                 // targeted subsys index
319                 if(Player_ai->targeted_subsys != NULL){
320                         t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
321                 }
322
323                 // locked targeted subsys index
324                 if(Player->locking_subsys != NULL){
325                         l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 );
326                 }
327         }
328
329         // add them all
330         memcpy(data + packet_size, &tnet_signature, sizeof(ushort));
331         packet_size += sizeof(ushort);
332         memcpy(data + packet_size, &t_subsys, sizeof(char));
333         packet_size += sizeof(char);
334         memcpy(data + packet_size, &l_subsys, sizeof(char));
335         packet_size += sizeof(char);    
336
337         return packet_size;
338 }
339
340 // pack the appropriate info into the data
341 #define PACK_PERCENT(v)                                 {ubyte upercent; if(v < 0.0f){v = 0.0f;} upercent = (v * 255.0f) <= 255.0f ? (ubyte)(v * 255.0f) : (ubyte)255; memcpy(data + packet_size + header_bytes, &upercent, sizeof(ubyte)); packet_size++; }
342 int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out)
343 {       
344         ubyte data[255];
345         ubyte data_size = 0;    
346         char percent;
347         ship *shipp;    
348         ship_info *sip;
349         ubyte ret;
350         float temp;     
351         int header_bytes;
352         int packet_size = 0;    
353
354         // make sure we have a valid ship
355         SDL_assert(objp->type == OBJ_SHIP);
356         if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
357                 shipp = &Ships[objp->instance];
358                 sip = &Ship_info[shipp->ship_info_index];
359         } else {
360                 return 0;
361         }                       
362
363         // invalid player
364         if(pl == NULL){
365                 return 0;
366         }
367
368         // no flags -> do nothing
369         if(oo_flags == 0){
370                 // Int3();
371                 return 0;
372         }
373
374         // if i'm the client, make sure I only send certain things      
375         if(!MULTIPLAYER_MASTER){
376                 SDL_assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW));
377                 SDL_assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW)));
378         } 
379         // server 
380         else {
381                 // SDL_assert(oo_flags & OO_POS_NEW);
382         }
383
384         // header sizes
385         if(MULTIPLAYER_MASTER){
386                 header_bytes = 5;
387         } else {
388                 header_bytes = 2;
389         }       
390
391         // if we're a client (and therefore sending control info), pack client-specific info
392         if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
393                 packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes);            
394         }               
395                 
396         // position, velocity
397         if ( oo_flags & OO_POS_NEW ) {          
398                 ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos );
399                 packet_size += ret;
400                 
401                 // global records
402                 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);         
403                         
404                 ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
405                 packet_size += ret;             
406                         
407                 // global records               
408                 multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret);                         
409         }       
410
411         // orientation  
412         if(oo_flags & OO_ORIENT_NEW){
413                 ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient );
414                 // SDL_assert(ret == OO_ORIENT_RET_SIZE);
415                 packet_size += ret;
416                 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);                         
417
418                 ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info );
419                 packet_size += ret;     
420
421                 // global records               
422                 multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret);         
423         }
424                         
425         // forward thrust       
426         percent = (char)(objp->phys_info.forward_thrust * 100.0f);
427         SDL_assert( percent <= 100 );
428
429         memcpy(data + packet_size + header_bytes, &percent, sizeof(char));
430         packet_size += 1;
431
432         // global records       
433         multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1);   
434
435         // hull info
436         if ( oo_flags & OO_HULL_NEW ){
437                 // add the hull value for this guy              
438                 temp = (objp->hull_strength  / Ship_info[shipp->ship_info_index].initial_hull_strength);                
439                 PACK_PERCENT(temp);                             
440                 multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1);   
441
442                 // pack 2 shield values into each byte
443
444                 // pack quadrant 1
445                 temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
446                 PACK_PERCENT(temp);
447                                 
448                 // pack quadrant 2
449                 temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
450                 PACK_PERCENT(temp);                             
451
452                 // pack quadrant 3
453                 temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));          
454                 PACK_PERCENT(temp);
455                                 
456                 // pack quadrant 2
457                 temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS));                          
458                 PACK_PERCENT(temp);                             
459                                 
460                 multi_rate_add(NET_PLAYER_NUM(pl), "shl", 4);   
461         }       
462
463         // subsystem info
464         if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){
465                 ubyte ns;               
466                 ship_subsys *subsysp;
467                                 
468                 // just in case we have some kind of invalid data (should've been taken care of earlier in this function)
469                 if(shipp->ship_info_index < 0){
470                         ns = 0;
471                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
472                         packet_size++;
473                         
474                         multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);   
475                 }
476                 // add the # of subsystems, and their data
477                 else {
478                         ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems;
479                         memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte));
480                         packet_size++;                  
481                         multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);   
482
483                         // now the subsystems.
484                         for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
485                                 temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits;
486                                 PACK_PERCENT(temp);
487                                 
488                                 multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1);
489                         }
490                 }
491
492                 // ai mode info
493                 ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode);
494                 short submode = (short)(Ai_info[shipp->ai_index].submode);
495                 ushort target_signature;
496
497                 target_signature = 0;
498                 if ( Ai_info[shipp->ai_index].target_objnum != -1 ){
499                         target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature;
500                 }
501
502                 memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte));
503                 packet_size++;
504                 memcpy(data + packet_size + header_bytes, &submode, sizeof(short));
505                 packet_size += 2;
506                 memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort));           
507                 packet_size += 2;               
508                 
509                 multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5);
510
511                 // primary weapon energy
512                 temp = shipp->weapon_energy / sip->max_weapon_reserve;
513                 PACK_PERCENT(temp);
514         }               
515
516         // afterburner info
517         oo_flags &= ~PF_AFTERBURNER_ON;
518         if(objp->phys_info.flags & PF_AFTERBURNER_ON){
519                 oo_flags |= OO_AFTERBURNER_NEW;
520         }
521
522         // if this ship is a support ship, send some extra info
523         ubyte support_extra = 0;
524         if(MULTIPLAYER_MASTER && (sip->flags & SIF_SUPPORT) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
525                 ushort dock_sig;
526
527                 // flag
528                 support_extra = 1;              
529                 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
530                 packet_size += 1;
531                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].ai_flags, sizeof(int));
532                 packet_size += 4;
533                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].mode, sizeof(int));
534                 packet_size += 4;
535                 memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].submode, sizeof(int));
536                 packet_size += 4;
537                 if((Ai_info[shipp->ai_index].dock_objnum < 0) || (Ai_info[shipp->ai_index].dock_objnum >= MAX_OBJECTS)){
538                         dock_sig = 0;
539                 } else {
540                         dock_sig = Objects[Ai_info[shipp->ai_index].dock_objnum].net_signature;
541                 }               
542                 memcpy(data + packet_size + header_bytes, &dock_sig, sizeof(ushort));
543                 packet_size += 2;
544         } else {
545                 support_extra = 0;              
546                 memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte));
547                 packet_size += 1;
548         }                       
549
550         // make sure we have a valid chunk of data
551         SDL_assert(packet_size < 255);
552         if(packet_size >= 255){
553                 return 0;
554         }
555         data_size = (ubyte)packet_size;
556
557         // add the object's net signature, type and oo_flags
558         packet_size = 0;
559         // don't add for clients
560         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){         
561                 multi_rate_add(NET_PLAYER_NUM(pl), "sig", 2);
562                 ADD_USHORT( objp->net_signature );              
563                 
564                 multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1);
565                 ADD_DATA( oo_flags );
566         }       
567         multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1);
568         ADD_DATA( data_size );  
569         
570         multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1);
571         ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq );
572
573         packet_size += data_size;
574
575         // copy to the outgoing data
576         memcpy(data_out, data, packet_size);    
577         
578         return packet_size;     
579 }
580
581 // unpack information for a client , return bytes processed
582 int multi_oo_unpack_client_data(net_player *pl, ubyte *data)
583 {
584         ubyte in_flags;
585         ship *shipp;    
586         int offset = 0;
587         
588         memcpy(&in_flags, data, sizeof(ubyte)); 
589         offset++;
590
591         // get the player ship
592         shipp = NULL;
593         if((pl->player->objnum >= 0) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Objects[pl->player->objnum].instance >= 0)){
594                 shipp = &Ships[Objects[pl->player->objnum].instance];
595         }
596                 
597         // if we have a valid netplayer pointer
598         if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){
599                 // primary fired
600                 pl->player->ci.fire_primary_count = 0;          
601
602                 // secondary fired
603                 pl->player->ci.fire_secondary_count = 0;
604                 if ( in_flags & OOC_FIRE_SECONDARY ){
605                         pl->player->ci.fire_secondary_count = 1;
606                 }
607
608                 // countermeasure fired         
609                 pl->player->ci.fire_countermeasure_count = 0;           
610
611                 // set up aspect locking information
612                 pl->player->locking_on_center = 0;
613                 if ( in_flags & OOC_LOCKING_ON_CENTER ){
614                         pl->player->locking_on_center = 1;
615                 }               
616
617                 // trigger down, bank info
618                 if(shipp != NULL){
619                         if(in_flags & OOC_TRIGGER_DOWN){
620                                 shipp->flags |= SF_TRIGGER_DOWN;
621                         } else {
622                                 shipp->flags &= ~SF_TRIGGER_DOWN;
623                         }
624                         
625                         if(in_flags & OOC_PRIMARY_BANK){                
626                                 shipp->weapons.current_primary_bank = 1;
627                         } else {
628                                 shipp->weapons.current_primary_bank = 0;
629                         }
630
631                         // linked or not                                                                
632                         shipp->flags &= ~SF_PRIMARY_LINKED;
633                         if(in_flags & OOC_PRIMARY_LINKED){                              
634                                 shipp->flags |= SF_PRIMARY_LINKED;
635                         }
636                 }
637
638                 // other locking information
639                 if((shipp != NULL) && (shipp->ai_index != -1)){                 
640                         Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0;
641                         if      ( in_flags & OOC_TARGET_SEEK_LOCK ) {
642                                 Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK;
643                         } else {
644                                 Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK;
645                         }
646                 }
647         }
648         
649         // client targeting information 
650         ushort tnet_sig;
651         char t_subsys,l_subsys;
652         object *tobj;
653
654         // get the data
655         GET_USHORT(tnet_sig);
656         GET_DATA(t_subsys);
657         GET_DATA(l_subsys);
658
659         // try and find the targeted object
660         tobj = NULL;
661         if(tnet_sig != 0){
662                 tobj = multi_get_network_object( tnet_sig );
663         }
664         // maybe fill in targeted object values
665         if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){
666                 // assign the target object
667                 if(Objects[pl->player->objnum].type == OBJ_SHIP){
668                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj);
669                 }
670                 pl->s_info.target_objnum = OBJ_INDEX(tobj);
671
672                 // assign subsystems if possible                                        
673                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
674                         Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL;
675                         if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){
676                                 Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys);
677                         }
678                 }
679
680                 pl->player->locking_subsys = NULL;
681                 if(Objects[pl->player->objnum].type == OBJ_SHIP){               
682                         if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){
683                                 pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys);
684                         }                               
685                 }
686         }                               
687
688         return offset;
689 }
690
691 // unpack the object data, return bytes processed
692 #define UNPACK_PERCENT(v)                                       { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;}
693 int multi_oo_unpack_data(net_player *pl, ubyte *data)
694 {       
695         int offset = 0;         
696         object *pobjp;
697         ushort net_sig = 0;
698         ubyte data_size, oo_flags;
699         ubyte seq_num;
700         char percent;   
701         float fpct;
702         ship *shipp;
703         ship_info *sip;
704
705         // add the object's net signature, type and oo_flags
706         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
707                 GET_USHORT( net_sig );          
708                 GET_DATA( oo_flags );   
709         }
710         // clients always pos and orient stuff only
711         else {          
712                 oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);
713         }
714         GET_DATA( data_size );  
715         GET_DATA( seq_num );
716
717         // try and find the object
718         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
719                 pobjp = multi_get_network_object(net_sig);      
720         } else {        
721                 if((pl != NULL) && (pl->player->objnum != -1)){
722                         pobjp = &Objects[pl->player->objnum];
723                 } else {
724                         pobjp = NULL;
725                 }
726         }       
727         
728         // if we can't find the object, set pointer to bogus object to continue reading the data
729         // ignore out of sequence packets here as well
730         if ( (pobjp == NULL) || (pobjp->type != OBJ_SHIP) || (pobjp->instance < 0) || (pobjp->instance >= MAX_SHIPS) || (Ships[pobjp->instance].ship_info_index < 0) || (Ships[pobjp->instance].ship_info_index >= Num_ship_types)){            
731                 offset += data_size;
732                 return offset;
733         }
734         
735         // ship pointer
736         shipp = &Ships[pobjp->instance];
737         sip = &Ship_info[shipp->ship_info_index];
738
739         // ---------------------------------------------------------------------------------------------------------------
740         // CRITICAL OBJECT UPDATE SHIZ
741         // ---------------------------------------------------------------------------------------------------------------
742         
743         // if the packet is out of order
744         if(seq_num < shipp->np_updates[NET_PLAYER_NUM(pl)].seq){
745                 // non-wraparound case
746                 if((shipp->np_updates[NET_PLAYER_NUM(pl)].seq - seq_num) <= 100){
747                         offset += data_size;
748                         return offset;
749                 }
750         }       
751
752         // if this is from a player, read his button info
753         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
754                 offset += multi_oo_unpack_client_data(pl, data + offset);               
755         }       
756
757         // new info
758         vector new_pos = pobjp->pos;
759         physics_info new_phys_info = pobjp->phys_info;
760         matrix new_orient = pobjp->orient;
761         
762         // position
763         if ( oo_flags & OO_POS_NEW ) {                                          
764                 // AVERAGE TIME BETWEEN PACKETS FOR THIS SHIP
765                 // store this latest time stamp
766                 if(oo_arrive_time_count[shipp - Ships] == 5){
767                         memmove(&oo_arrive_time[shipp - Ships][0], &oo_arrive_time[shipp - Ships][1], sizeof(float) * 4);
768                         oo_arrive_time[shipp - Ships][4] = f2fl(Missiontime);
769                 } else {
770                         oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime);
771                 }
772                 // if we've got 5 elements calculate the average
773                 if(oo_arrive_time_count[shipp - Ships] == 5){
774                         int idx;
775                         oo_arrive_time_avg_diff[shipp - Ships] = 0.0f;
776                         for(idx=0; idx<4; idx++){
777                                 oo_arrive_time_avg_diff[shipp - Ships] += oo_arrive_time[shipp - Ships][idx + 1] - oo_arrive_time[shipp - Ships][idx];
778                         }
779                         oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f;
780                 }
781                 // next expected arrival time
782                 oo_arrive_time_next[shipp - Ships] = 0.0f;
783
784                 // int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos );
785                 int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos );
786                 offset += r1;                           
787
788                 // int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
789                 int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &new_pos, &new_phys_info );
790                 offset += r3;
791                 
792                 // bash desired vel to be velocity
793                 // pobjp->phys_info.desired_vel = pobjp->phys_info.vel;         
794         }       
795
796         // orientation  
797         if ( oo_flags & OO_ORIENT_NEW ) {               
798                 // int r2 = multi_pack_unpack_orient( 0, data + offset, &pobjp->orient );
799                 int r2 = multi_pack_unpack_orient( 0, data + offset, &new_orient );
800                 offset += r2;           
801
802                 // int r5 = multi_pack_unpack_rotvel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info );
803                 int r5 = multi_pack_unpack_rotvel( 0, data + offset, &new_orient, &new_pos, &new_phys_info );
804                 offset += r5;
805
806                 // bash desired rotvel to be 0
807                 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
808         }
809         
810         // forward thrust       
811         percent = (char)(pobjp->phys_info.forward_thrust * 100.0f);
812         SDL_assert( percent <= 100 );
813         GET_DATA(percent);              
814
815         // now stuff all this new info
816         if(oo_flags & OO_POS_NEW){
817                 // if we're past the position update tolerance, bash.
818                 // this should cause our 2 interpolation splines to be exactly the same. so we'll see a jump,
819                 // but it should be nice and smooth immediately afterwards
820                 if(vm_vec_dist(&new_pos, &pobjp->pos) > OO_POS_UPDATE_TOLERANCE){
821                         pobjp->pos = new_pos;
822                 }
823
824                 // recalc any interpolation info                
825                 if(oo_interp_count[shipp - Ships] < 2){
826                         oo_interp_points[shipp - Ships][oo_interp_count[shipp - Ships]++] = new_pos;                    
827                 } else {
828                         oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1];
829                         oo_interp_points[shipp - Ships][1] = new_pos;                   
830
831                         multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info);
832                 }
833                 
834                 pobjp->phys_info.vel = new_phys_info.vel;               
835                 pobjp->phys_info.desired_vel = new_phys_info.vel;
836         } 
837
838         // we'll just sim rotation straight. it works fine.
839         if(oo_flags & OO_ORIENT_NEW){
840                 pobjp->orient = new_orient;
841                 pobjp->phys_info.rotvel = new_phys_info.rotvel;
842                 // pobjp->phys_info.desired_rotvel = vmd_zero_vector;
843                 pobjp->phys_info.desired_rotvel = new_phys_info.rotvel;
844         }       
845
846
847         // ---------------------------------------------------------------------------------------------------------------
848         // ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE!
849         // ---------------------------------------------------------------------------------------------------------------
850         
851         // hull info
852         if ( oo_flags & OO_HULL_NEW ){
853                 UNPACK_PERCENT(fpct);
854                 pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength;          
855
856                 float shield_0, shield_1, shield_2, shield_3;
857                 
858                 // unpack the 4 quadrants
859                 UNPACK_PERCENT(shield_0);
860                 UNPACK_PERCENT(shield_1);
861                 UNPACK_PERCENT(shield_2);
862                 UNPACK_PERCENT(shield_3);
863
864                 pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
865                 pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
866                 pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;
867                 pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS;               
868         }       
869
870         if ( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ) {
871                 ubyte n_subsystems, subsys_count;
872                 float subsystem_percent[MAX_MODEL_SUBSYSTEMS];          
873                 ship_subsys *subsysp;           
874                 float val;              
875                 int i;          
876
877                 SDL_zero(subsystem_percent);
878
879                 // get the data for the subsystems
880                 GET_DATA( n_subsystems );
881                 for ( i = 0; i < n_subsystems; i++ ){
882                         UNPACK_PERCENT( subsystem_percent[i] );
883                 }               
884                 
885                 // fill in the subsystem data
886                 subsys_count = 0;
887                 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
888                         int subsys_type;
889
890                         val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
891                         subsysp->current_hits = val;
892
893                         // add the value just generated (it was zero'ed above) into the array of generic system types
894                         subsys_type = subsysp->system_info->type;                                       // this is the generic type of subsystem
895                         SDL_assert ( subsys_type < SUBSYSTEM_MAX );
896                         shipp->subsys_info[subsys_type].current_hits += val;
897                         subsys_count++;
898
899                         // if we've reached max subsystems for some reason, bail out
900                         if(subsys_count >= n_subsystems){
901                                 break;
902                         }
903                 }
904                 
905                 // recalculate all ship subsystems
906                 ship_recalc_subsys_strength( shipp );                   
907
908                 // ai mode info
909                 ubyte umode;
910                 short submode;
911                 ushort target_signature;
912                 object *target_objp;
913
914                 GET_DATA( umode );
915                 GET_SHORT( submode );
916                 GET_USHORT( target_signature );         
917
918                 if(shipp->ai_index >= 0){
919                         Ai_info[shipp->ai_index].mode = umode;
920                         Ai_info[shipp->ai_index].submode = submode;             
921
922                         // set this guys target objnum
923                         target_objp = multi_get_network_object( target_signature );
924                         if ( target_objp == NULL ){
925                                 Ai_info[shipp->ai_index].target_objnum = -1;
926                         } else {
927                                 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
928                         }
929                 }               
930
931                 // primary weapon energy                
932                 float weapon_energy_pct;
933                 UNPACK_PERCENT(weapon_energy_pct);
934                 shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct;             
935         }       
936
937         // support ship extra info
938         ubyte support_extra;
939         GET_DATA(support_extra);
940         if(support_extra){
941                 ushort dock_sig;
942                 int ai_flags, ai_mode, ai_submode;
943
944                 // flag         
945                 GET_INT(ai_flags);
946                 GET_INT(ai_mode);
947                 GET_INT(ai_submode);
948                 GET_USHORT(dock_sig);           
949
950                 // valid ship?                                                  
951                 if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
952                         Ai_info[shipp->ai_index].ai_flags = ai_flags;
953                         Ai_info[shipp->ai_index].mode = ai_mode;
954                         Ai_info[shipp->ai_index].submode = ai_submode;
955
956                         object *objp = multi_get_network_object( dock_sig );
957                         if(objp != NULL){
958                                 Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp);
959                         }
960                 }                       
961         } 
962
963         // afterburner info
964         if(oo_flags & OO_AFTERBURNER_NEW){
965                 // maybe turn them on
966                 if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){
967                         afterburners_start(pobjp);
968                 }
969         } else {
970                 // maybe turn them off
971                 if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
972                         afterburners_stop(pobjp);
973                 }
974         }
975
976         // primary info (only clients care about this)
977         if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
978                 // what bank
979                 if(oo_flags & OO_PRIMARY_BANK){
980                         shipp->weapons.current_primary_bank = 1;
981                 } else {
982                         shipp->weapons.current_primary_bank = 0;
983                 }
984
985                 // linked or not
986                 shipp->flags &= ~SF_PRIMARY_LINKED;
987                 if(oo_flags & OO_PRIMARY_LINKED){
988                         shipp->flags |= SF_PRIMARY_LINKED;
989                 }
990
991                 // trigger down or not - server doesn't care about this. he'll get it from clients anyway               
992                 shipp->flags &= ~SF_TRIGGER_DOWN;
993                 if(oo_flags & OO_TRIGGER_DOWN){
994                         shipp->flags |= SF_TRIGGER_DOWN;
995                 }               
996         }
997         
998         // if we're the multiplayer server, set eye position and orient
999         if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){
1000                 pl->s_info.eye_pos = pobjp->pos;
1001                 pl->s_info.eye_orient = pobjp->orient;
1002         }               
1003
1004         // update the sequence #
1005         shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
1006
1007         // flag the object as just updated
1008         // pobjp->flags |= OF_JUST_UPDATED;
1009         
1010         return offset;
1011 }
1012
1013 // reset the timestamp appropriately for the passed in object
1014 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1015 {
1016         int stamp = 0;  
1017
1018         // if this is the guy's target, 
1019         if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1020                 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1021         } else {
1022                 // reset the timestamp appropriately
1023                 if(in_cone){
1024                         // base it upon range
1025                         switch(range){
1026                         case OO_NEAR:
1027                                 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1028                                 break;
1029
1030                         case OO_MIDRANGE:
1031                                 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1032                                 break;
1033
1034                         case OO_FAR:
1035                                 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1036                                 break;
1037                         }
1038                 } else {
1039                         // base it upon range
1040                         switch(range){
1041                         case OO_NEAR:
1042                                 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1043                                 break;
1044
1045                         case OO_MIDRANGE:
1046                                 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1047                                 break;
1048
1049                         case OO_FAR:
1050                                 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1051                                 break;
1052                         }
1053                 }                                               
1054         }
1055
1056         // reset the timestamp for this object
1057         if(objp->type == OBJ_SHIP){
1058                 Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp);
1059         } 
1060 }
1061
1062 // reset the timestamp appropriately for the passed in object
1063 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1064 {
1065         Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
1066 }
1067
1068 // reset the timestamp appropriately for the passed in object
1069 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1070 {
1071         Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
1072 }
1073
1074 // determine what needs to get sent for this player regarding the passed object, and when
1075 int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data)
1076 {
1077         ubyte oo_flags;
1078         int stamp;
1079         int player_index;
1080         vector player_eye;
1081         vector obj_dot;
1082         float eye_dot, dist;
1083         int in_cone;
1084         int range;
1085         ship *shipp;
1086         ship_info *sip;
1087         ushort cur_pos_chksum = 0;
1088         ushort cur_orient_chksum = 0;
1089
1090         // if the timestamp has elapsed for this guy, send stuff
1091         player_index = NET_PLAYER_INDEX(pl);
1092         if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1093                 return 0;
1094         }
1095
1096         // determine what the timestamp is for this object
1097         if(obj->type == OBJ_SHIP){
1098                 stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp;
1099         } else {
1100                 return 0;
1101         }
1102
1103         // stamp hasn't popped yet
1104         if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){
1105                 return 0;
1106         }
1107         
1108         // if we're supposed to update this guy 
1109
1110         // get the ship pointer
1111         shipp = &Ships[obj->instance];
1112
1113         // get ship info pointer
1114         sip = NULL;
1115         if(shipp->ship_info_index >= 0){
1116                 sip = &Ship_info[shipp->ship_info_index];
1117         }
1118         
1119         // check dot products           
1120         player_eye = pl->s_info.eye_orient.v.fvec;
1121         vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1122         vm_vec_normalize(&obj_dot);
1123         eye_dot = vm_vec_dot(&obj_dot, &player_eye);            
1124         in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0;                
1125                                                         
1126         // determine distance (near, medium, far)
1127         vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos);
1128         dist = vm_vec_mag(&obj_dot);            
1129         if(dist < OO_NEAR_DIST){
1130                 range = OO_NEAR;
1131         } else if(dist < OO_MIDRANGE_DIST){
1132                 range = OO_MIDRANGE;
1133         } else {
1134                 range = OO_FAR;
1135         }
1136
1137         // reset the timestamp for the next update for this guy
1138         multi_oo_reset_timestamp(pl, obj, range, in_cone);
1139
1140         // base oo_flags
1141         oo_flags = OO_POS_NEW | OO_ORIENT_NEW;
1142
1143         // if its a small ship, add weapon link info
1144         if((sip != NULL) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_STEALTH))){
1145                 // primary bank 0 or 1
1146                 if(shipp->weapons.current_primary_bank > 0){
1147                         oo_flags |= OO_PRIMARY_BANK;
1148                 }
1149
1150                 // linked or not
1151                 if(shipp->flags & SF_PRIMARY_LINKED){
1152                         oo_flags |= OO_PRIMARY_LINKED;
1153                 }
1154
1155                 // trigger down or not
1156                 if(shipp->flags & SF_TRIGGER_DOWN){
1157                         oo_flags |= OO_TRIGGER_DOWN;
1158                 }
1159         }       
1160                 
1161         // if the object's hull/shield timestamp has expired
1162         if((Ships[obj->instance].np_updates[player_index].status_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].status_update_stamp, OO_MAX_TIMESTAMP)){
1163                 oo_flags |= (OO_HULL_NEW);
1164
1165                 // reset the timestamp
1166                 multi_oo_reset_status_timestamp(obj, player_index);                     
1167         }
1168
1169         // if the object's hull/shield timestamp has expired
1170         if((Ships[obj->instance].np_updates[player_index].subsys_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].subsys_update_stamp, OO_MAX_TIMESTAMP)){
1171                 oo_flags |= OO_SUBSYSTEMS_AND_AI_NEW;
1172
1173                 // reset the timestamp
1174                 multi_oo_reset_subsys_timestamp(obj, player_index);
1175         }
1176
1177         // add info for a targeted object
1178         if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){
1179                 oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW);
1180         }
1181         // all other cases
1182         else {                                  
1183                 // add info which is contingent upon being "in front"                   
1184                 if(in_cone){
1185                         oo_flags |= OO_ORIENT_NEW;
1186                 }                                               
1187         }               
1188
1189         // get current position and orient checksums            
1190         cur_pos_chksum = cf_add_chksum_short(cur_pos_chksum, (char*)(&obj->pos), sizeof(vector));
1191         cur_orient_chksum = cf_add_chksum_short(cur_orient_chksum, (char*)(&obj->orient), sizeof(matrix));
1192
1193         // if position or orientation haven't changed   
1194         if((shipp->np_updates[player_index].pos_chksum != 0) && (shipp->np_updates[player_index].pos_chksum == cur_pos_chksum)){
1195                 // if we otherwise would have been sending it, keep track of it (debug only)
1196 #ifndef NDEBUG
1197                 if(oo_flags & OO_POS_NEW){
1198                         multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE);
1199                 }               
1200 #endif
1201                 oo_flags &= ~(OO_POS_NEW);
1202         }
1203         if((shipp->np_updates[player_index].orient_chksum != 0) && (shipp->np_updates[player_index].orient_chksum == cur_orient_chksum)){
1204                 // if we otherwise would have been sending it, keep track of it (debug only)
1205 #ifndef NDEBUG
1206                 if(oo_flags & OO_ORIENT_NEW){
1207                         multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE);
1208                 }               
1209 #endif
1210                 oo_flags &= ~(OO_ORIENT_NEW);
1211         }
1212         shipp->np_updates[player_index].pos_chksum = cur_pos_chksum;
1213         shipp->np_updates[player_index].orient_chksum = cur_orient_chksum;
1214
1215         // pack stuff only if we have to        
1216         int packed = multi_oo_pack_data(pl, obj, oo_flags ,data);       
1217
1218         // increment sequence #
1219         Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++;
1220
1221         // bytes packed
1222         return packed;
1223 }
1224
1225 // process all other objects for this player
1226 void multi_oo_process_all(net_player *pl)
1227 {
1228         ubyte data[MAX_PACKET_SIZE];
1229         ubyte data_add[MAX_PACKET_SIZE];
1230         ubyte stop;
1231         int add_size;   
1232         int packet_size = 0;
1233         int idx;
1234                 
1235         object *moveup;
1236
1237         // if the player has an invalid objnum..
1238         if(pl->player->objnum < 0){
1239                 return;
1240         }
1241
1242         object *targ_obj;       
1243
1244         // build the list of ships to check against
1245         multi_oo_build_ship_list(pl);
1246
1247         // do nothing if he has no object targeted, or if he has a weapon targeted
1248         if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1249                 // build the header
1250                 BUILD_HEADER(OBJECT_UPDATE);            
1251         
1252                 // get a pointer to the object
1253                 targ_obj = &Objects[pl->s_info.target_objnum];
1254         
1255                 // run through the maybe_update function
1256                 add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
1257
1258                 // copy in any relevant data
1259                 if(add_size){
1260                         stop = 0xff;                    
1261                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1262                         ADD_DATA(stop);
1263
1264                         memcpy(data + packet_size, data_add, add_size);
1265                         packet_size += add_size;                
1266                 }
1267         } else {
1268                 // just build the header for the rest of the function
1269                 BUILD_HEADER(OBJECT_UPDATE);            
1270         }
1271                 
1272         idx = 0;
1273         while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1274                 // if this guy is over his datarate limit, do nothing
1275                 if(multi_oo_rate_exceeded(pl)){
1276                         nprintf(("Network","Capping client\n"));
1277                         idx++;
1278
1279                         continue;
1280                 }                       
1281
1282                 // get the object
1283                 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1284
1285                 // maybe send some info         
1286                 add_size = multi_oo_maybe_update(pl, moveup, data_add);
1287
1288                 // if this data is too much for the packet, send off what we currently have and start over
1289                 if(packet_size + add_size > OO_MAX_SIZE){
1290                         stop = 0x00;                    
1291                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1292                         ADD_DATA(stop);
1293                                                                         
1294                         multi_io_send(pl, data, packet_size);
1295                         pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1296
1297                         BUILD_HEADER(OBJECT_UPDATE);                    
1298                 }
1299
1300                 if(add_size){
1301                         stop = 0xff;                    
1302                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1303                         ADD_DATA(stop);
1304
1305                         // copy in the data
1306                         memcpy(data + packet_size,data_add,add_size);
1307                         packet_size += add_size;
1308                 }
1309
1310                 // next ship
1311                 idx++;
1312         }
1313
1314         // if we have anything more than 3 byte in the packet, send the last one off
1315         if(packet_size > 3){
1316                 stop = 0x00;            
1317                 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1318                 ADD_DATA(stop);
1319                                                                 
1320                 multi_io_send(pl, data, packet_size);
1321                 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1322         }
1323 }
1324
1325 // process all object update details for this frame
1326 void multi_oo_process()
1327 {
1328         int idx;        
1329         
1330         // process each player
1331         for(idx=0; idx<MAX_PLAYERS; idx++){
1332                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) /*&& !MULTI_OBSERVER(Net_players[idx])*/ ){
1333                         // now process the rest of the objects
1334                         multi_oo_process_all(&Net_players[idx]);
1335
1336                         // do firing stuff for this player
1337                         if((Net_players[idx].player != NULL) && (Net_players[idx].player->objnum >= 0) && !(Net_players[idx].flags & NETINFO_FLAG_LIMBO) && !(Net_players[idx].flags & NETINFO_FLAG_RESPAWNING)){
1338                                 if((Objects[Net_players[idx].player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].player->objnum].flags & OF_SHOULD_BE_DEAD)){
1339                                         obj_player_fire_stuff( &Objects[Net_players[idx].player->objnum], Net_players[idx].player->ci );
1340                                 }
1341                         }
1342                 }
1343         }
1344 }
1345
1346 // process incoming object update data
1347 void multi_oo_process_update(ubyte *data, header *hinfo)
1348 {       
1349         ubyte stop;     
1350         int player_index;       
1351         int offset = HEADER_LENGTH;
1352         net_player *pl = NULL;  
1353
1354         // if this is processed on the server, its a client object update packet
1355         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1356                 // determine what player this came from 
1357                 player_index = find_player_id(hinfo->id);
1358                 if(player_index != -1){                                         
1359                         pl = &Net_players[player_index];
1360                 } else {                        
1361                         pl = NULL;
1362                 }
1363         }
1364         // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
1365         else {                                          
1366                 pl = Net_player;
1367         }
1368
1369         GET_DATA(stop);
1370         
1371         while(stop == 0xff){
1372                 // process the data
1373                 offset += multi_oo_unpack_data(pl, data + offset);
1374
1375                 GET_DATA(stop);
1376         }
1377         PACKET_SET_SIZE();
1378 }
1379
1380 // initialize all object update timestamps (call whenever entering gameplay state)
1381 void multi_oo_gameplay_init()
1382 {
1383         int cur, s_idx, idx;
1384         //ship_obj *so;                         
1385         ship *shipp;
1386         /*
1387         int num_ships = ship_get_num_ships();
1388
1389         if(num_ships <= 0){
1390                 return;
1391         }
1392         split = 3000 / num_ships;
1393         */
1394         //split = 1;
1395         //split = 150;
1396
1397         // server should setup initial update timestamps        
1398         // stagger initial updates over 3 seconds or so
1399         cur = 0;
1400         //for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1401                 //if(Objects[so->objnum].type == OBJ_SHIP){
1402         for(s_idx=0; s_idx<MAX_SHIPS; s_idx++){
1403                         shipp = &Ships[s_idx];
1404                 
1405                         // update the timestamps
1406                         for(idx=0;idx<MAX_PLAYERS;idx++){
1407                                 shipp->np_updates[idx].update_stamp = timestamp(cur);
1408                                 shipp->np_updates[idx].status_update_stamp = timestamp(cur);
1409                                 shipp->np_updates[idx].subsys_update_stamp = timestamp(cur);
1410                                 shipp->np_updates[idx].seq = 0;         
1411                                 shipp->np_updates[idx].pos_chksum = 0;
1412                                 shipp->np_updates[idx].orient_chksum = 0;
1413                         } 
1414                         
1415                         oo_arrive_time_count[shipp - Ships] = 0;                        
1416                         oo_interp_count[shipp - Ships] = 0;
1417
1418                         // increment the time
1419 //                      cur += split;                   
1420                 }
1421         //}                     
1422
1423         // reset datarate stamp now
1424         extern int OO_gran;
1425         for(idx=0; idx<MAX_PLAYERS; idx++){
1426                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1427         }
1428 }
1429
1430 // send control info for a client (which is basically a "reverse" object update)
1431 void multi_oo_send_control_info()
1432 {
1433         ubyte data[MAX_PACKET_SIZE], stop;
1434         ubyte data_add[MAX_PACKET_SIZE];
1435         ubyte oo_flags; 
1436         int add_size;
1437         int packet_size = 0;
1438
1439         // if I'm dying or my object type is not a ship, bail here
1440         if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1441                 return;
1442         }       
1443         
1444         // build the header
1445         BUILD_HEADER(OBJECT_UPDATE);            
1446
1447         // pos and orient always
1448         oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);                
1449
1450         // pack the appropriate info into the data
1451         add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1452
1453         // copy in any relevant data
1454         if(add_size){
1455                 stop = 0xff;            
1456                 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1457                 
1458                 ADD_DATA(stop);
1459
1460                 memcpy(data + packet_size, data_add, add_size);
1461                 packet_size += add_size;                
1462         }
1463
1464         // add the final stop byte
1465         stop = 0x0;     
1466         multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1467         ADD_DATA(stop);
1468
1469         // increment sequence #
1470         Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
1471
1472         // send to the server
1473         if(Netgame.server != NULL){                                                             
1474                 multi_io_send(Net_player, data, packet_size);
1475         }
1476 }
1477
1478 // display any oo info on the hud
1479 void multi_oo_display()
1480 {
1481 #ifndef NDEBUG  
1482 #endif
1483 }
1484
1485
1486 // ---------------------------------------------------------------------------------------------------
1487 // DATARATE DEFINES/VARS
1488 //
1489
1490 // low object update datarate limit
1491 #define OO_LIMIT_LOW                            1800
1492 #define OO_LIMIT_MED                            3400
1493
1494 // timestamp for sending control info (movement only - we'll send button info all the time)
1495 #define OO_CIRATE                                       85                                      // 15x a second
1496 int Multi_cirate_stamp                  = -1;                           // timestamp for waiting on control info time
1497 int Multi_cirate_can_send               = 1;                            // if we can send control info this frame
1498
1499 // global max rates
1500 int OO_server_rate = -1;                                                        // max _total_ bandwidth to send to all clients
1501 int OO_client_rate = -1;                                                        // max bandwidth to go to an individual client
1502
1503 // update timestamp for server datarate checking
1504 #define RATE_UPDATE_TIME                1250                            // in ms
1505 int OO_server_rate_stamp = -1;
1506
1507 // bandwidth granularity
1508 int OO_gran = 1;
1509 DCF(oog, "")
1510 {
1511         dc_get_arg(ARG_INT);
1512         OO_gran = Dc_arg_int;
1513 }
1514
1515 // process datarate limiting stuff for the server
1516 void multi_oo_server_process();
1517
1518 // process datarate limiting stuff for the client
1519 void multi_oo_client_process();
1520
1521 // update the server datarate
1522 void multi_oo_update_server_rate();
1523
1524
1525 // ---------------------------------------------------------------------------------------------------
1526 // DATARATE FUNCTIONS
1527 //
1528
1529 // process all object update datarate details
1530 void multi_oo_rate_process()
1531 {
1532         // if I have no valid player, drop out here
1533         if(Net_player == NULL){
1534                 return;
1535         }
1536
1537         // if we're not in mission, don't do anything
1538         if(!(Game_mode & GM_IN_MISSION)){
1539                 return;
1540         }
1541
1542         // if I'm the server of a game, process server stuff
1543         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1544                 multi_oo_server_process();
1545         }
1546         // otherwise process client-side stuff
1547         else {
1548                 multi_oo_client_process();
1549         }
1550 }
1551
1552 // process datarate limiting stuff for the server
1553 void multi_oo_server_process()
1554 {
1555         int idx;
1556         
1557         // go through all players
1558         for(idx=0;idx<MAX_PLAYERS;idx++){
1559                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1560                         // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1561                         if((Net_players[idx].s_info.rate_stamp == -1) || timestamp_elapsed_safe(Net_players[idx].s_info.rate_stamp, OO_MAX_TIMESTAMP) || (abs(timestamp_ticker - Net_players[idx].s_info.rate_stamp) >= (int)(1000.0f / (float)OO_gran)) ){
1562                                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1563                                 Net_players[idx].s_info.rate_bytes = 0;
1564                         }
1565                 }
1566         }
1567
1568         // determine if we should be updating the server datarate
1569         if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){
1570                 // reset the timestamp
1571                 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1572
1573                 // update the server datarate
1574                 multi_oo_update_server_rate();
1575
1576                 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1577         }       
1578 }
1579
1580 // process datarate limiting stuff for the client
1581 void multi_oo_client_process()
1582 {
1583         // if the timestamp is -1 or has elapsed, reset it
1584         if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){
1585                 Multi_cirate_can_send = 1;
1586                 Multi_cirate_stamp = timestamp(OO_CIRATE);
1587         }       
1588 }
1589
1590
1591 // datarate limiting system for server -------------------------------------
1592
1593 // initialize the rate limiting system for all players
1594 void multi_oo_rate_init_all()
1595 {
1596         int idx;
1597
1598         // if I don't have a net_player, bail here
1599         if(Net_player == NULL){
1600                 return;
1601         }
1602
1603         // if I'm the server of the game
1604         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
1605                 // go through all players
1606                 for(idx=0;idx<MAX_PLAYERS;idx++){
1607                         if(MULTI_CONNECTED(Net_players[idx])){
1608                                 multi_oo_rate_init(&Net_players[idx]);
1609                         }
1610                 }
1611
1612                 OO_server_rate_stamp = -1;
1613         }
1614         // if i'm the client, initialize my control info datarate stuff
1615         else {
1616                 Multi_cirate_stamp = -1;
1617                 Multi_cirate_can_send = 1;
1618         }
1619 }
1620
1621 // initialize the rate limiting for the passed in player
1622 void multi_oo_rate_init(net_player *pl)
1623 {
1624         // reinitialize his datarate timestamp
1625         pl->s_info.rate_stamp = -1;
1626         pl->s_info.rate_bytes = 0;
1627 }
1628
1629 // if the given net-player has exceeded his datarate limit
1630 int multi_oo_rate_exceeded(net_player *pl)
1631 {
1632         int rate_compare;
1633                 
1634         // check against the guy's object update level
1635         switch(pl->p_info.options.obj_update_level){
1636         // low update level
1637         case OBJ_UPDATE_LOW:
1638                 // the low object update limit
1639                 rate_compare = OO_LIMIT_LOW;
1640                 break;
1641
1642         // medium update level
1643         case OBJ_UPDATE_MEDIUM:         
1644                 // the low object update limit
1645                 rate_compare = OO_LIMIT_MED;
1646                 break;
1647
1648         // high update level - super high datarate (no capping, just intelligent updating)
1649         case OBJ_UPDATE_HIGH:
1650                 rate_compare = 100000000;
1651                 break;
1652
1653         // LAN - no rate max
1654         case OBJ_UPDATE_LAN:
1655                 return 0;
1656
1657         // default level
1658         default:
1659                 Int3();
1660                 rate_compare = OO_LIMIT_LOW;
1661                 break;
1662         }
1663
1664         // if the server global rate PER CLIENT (OO_client_rate) is actually lower
1665         if(OO_client_rate < rate_compare){
1666                 rate_compare = OO_client_rate;
1667         }
1668
1669         // compare his bytes sent against the allowable amount
1670         if(pl->s_info.rate_bytes >= rate_compare){
1671                 return 1;
1672         }
1673
1674         // we're allowed to send
1675         return 0;
1676 }
1677
1678 // if it is ok for me to send a control info (will be ~N times a second)
1679 int multi_oo_cirate_can_send()
1680 {
1681         // if we're allowed to send
1682         if(Multi_cirate_can_send){
1683                 Multi_cirate_can_send = 0;
1684                 return 1;
1685         } 
1686         
1687         return 0;               
1688 }
1689
1690 // dynamically update the server capped bandwidth rate
1691 void multi_oo_update_server_rate()
1692 {       
1693         int num_connections;    
1694         
1695         // bail conditions
1696         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1697                 return;
1698         }
1699
1700         // get the # of connections
1701         num_connections = multi_num_connections();
1702         if(!(Game_mode & GM_STANDALONE_SERVER)){
1703                 num_connections--;
1704         }
1705         // make sure we always pretend there's at least one guy available
1706         if(num_connections <= 0){
1707                 num_connections = 1;
1708         }
1709                 
1710         // set the data rate    
1711         switch(Net_player->p_info.options.obj_update_level){
1712         // LAN update level
1713         case OBJ_UPDATE_LAN:
1714                 // set to something super big so we don't limit anything
1715                 OO_server_rate = 500000000;
1716                 break;
1717
1718         // high update level
1719         case OBJ_UPDATE_HIGH:
1720                 // set to 0 so we don't limit anything
1721                 OO_server_rate = Multi_options_g.datarate_cap;
1722                 break;
1723
1724         // medium update level
1725         case OBJ_UPDATE_MEDIUM:
1726                 // set the rate to be "medium" update level
1727                 OO_server_rate = OO_LIMIT_MED;
1728                 break;
1729
1730         // low update level 
1731         case OBJ_UPDATE_LOW:
1732                 // set the rate to be the "low" update level
1733                 OO_server_rate = OO_LIMIT_LOW;
1734                 break;
1735
1736         default:
1737                 Int3();
1738                 return;
1739         }       
1740
1741         // set the individual client level
1742         OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
1743 }
1744
1745 // reset all sequencing info (obsolete for new object update stuff)
1746 void multi_oo_reset_sequencing()
1747 {               
1748 }
1749
1750 // is this object one which needs to go through the interpolation
1751 int multi_oo_is_interp_object(object *objp)
1752 {       
1753         // if not multiplayer, skip it
1754         if(!(Game_mode & GM_MULTIPLAYER)){
1755                 return 0;
1756         }
1757
1758         // if its not a ship, skip it
1759         if(objp->type != OBJ_SHIP){
1760                 return 0;
1761         }
1762
1763         // other bogus cases
1764         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1765                 return 0;
1766         }
1767
1768         // if I'm a client and this is not me, I need to interp it
1769         if(!MULTIPLAYER_MASTER){
1770                 if(objp != Player_obj){
1771                         return 1;
1772                 } else {
1773                         return 0;
1774                 }
1775         }
1776
1777         // servers only interpolate other player ships
1778         if(!(objp->flags & OF_PLAYER_SHIP)){
1779                 return 0;
1780         }
1781
1782         // here we know its a player ship - is it mine?
1783         if(objp == Player_obj){
1784                 return 0;
1785         }
1786
1787         // interp it
1788         return 1;
1789 }
1790
1791 // interp
1792 void multi_oo_interp(object *objp)
1793 {               
1794         // make sure its a valid ship
1795         SDL_assert(Game_mode & GM_MULTIPLAYER);
1796         if(objp->type != OBJ_SHIP){
1797                 return;
1798         }
1799         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1800                 return;
1801         }       
1802
1803         // increment his approx "next" time
1804         oo_arrive_time_next[objp->instance] += flFrametime;
1805
1806         // do stream weapon firing for this ship
1807         SDL_assert(objp != Player_obj);
1808         if(objp != Player_obj){
1809                 ship_fire_primary(objp, 1, 0);
1810         }
1811
1812         // if this ship doesn't have enough data points yet, skip it
1813         if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){
1814                 return;
1815         }
1816
1817         // store the magnitude of his velocity
1818         // float vel_mag = vm_vec_mag(&objp->phys_info.vel);
1819
1820         // determine how far along we are (0.0 to 1.0) until we should be getting the next packet
1821         float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance];
1822
1823         // gr_set_color_fast(&Color_bright);
1824         // gr_printf(100, 10, "%f\n", t);
1825         
1826         // we've overshot. hmm. just keep the sim running I guess       
1827         if(t > 1.0f){
1828                 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1829                 return;
1830         }       
1831
1832         // otherwise, blend the two curves together to get the new point
1833         float u = 0.5f + (t * 0.5f);
1834         vector p_bad, p_good;
1835         oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u);
1836         oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u);         
1837         vm_vec_scale(&p_good, t);
1838         vm_vec_scale(&p_bad, 1.0f - t);
1839         vm_vec_add(&objp->pos, &p_bad, &p_good);        
1840
1841         // set new velocity
1842         // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
1843         
1844         // run the sim for rotation     
1845         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1846
1847         // blend velocity vectors together with an average weight
1848         /*
1849         vector v_bad, v_good;
1850         oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0);
1851         oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0);     
1852
1853         // t -= 1.0f;
1854         vm_vec_scale(&v_good, t);
1855         vm_vec_scale(&v_bad, 1.0f - t);
1856         vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good);
1857         
1858         // run the sim
1859         physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1860         */
1861
1862         /*
1863         vector v_bad, v_good;
1864         oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0);
1865         oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0);     
1866
1867         // t -= 1.0f;
1868         vm_vec_scale(&v_good, t);
1869         vm_vec_scale(&v_bad, 1.0f - t);
1870         vm_vec_avg(&objp->pos, &v_bad, &v_good);        
1871         
1872         // run the sim
1873         // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1874         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1875         */
1876 }
1877
1878 float oo_error = 0.8f;
1879 DCF(oo_error, "")
1880 {
1881         dc_get_arg(ARG_FLOAT);
1882         oo_error = Dc_arg_float;
1883 }
1884
1885 void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info)
1886 {
1887         vector a, b, c;
1888         // vector da, db, dc;
1889         matrix m_copy;
1890         physics_info p_copy;
1891         vector *pts[3] = {&a, &b, &c};  
1892         // vector *d_pts[3] = {&da, &db, &dc};
1893         
1894         // average time between packets
1895         float avg_diff = oo_arrive_time_avg_diff[ship_index];   
1896
1897         // would this cause us to rubber-band?
1898         vector v_norm = cur_phys_info->vel;     
1899         vector v_dir;
1900         vm_vec_sub(&v_dir, new_pos, cur_pos);   
1901         if(!IS_VEC_NULL(&v_norm) && !IS_VEC_NULL(&v_dir)){
1902                 vm_vec_normalize(&v_dir);
1903                 vm_vec_normalize(&v_norm);      
1904                 if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){
1905                         *new_pos = *cur_pos;
1906                 }
1907         }
1908         
1909         // get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether
1910         a = oo_interp_points[ship_index][0];
1911         b = *cur_pos;
1912         c = *cur_pos;
1913         m_copy = *cur_orient;
1914         p_copy = *cur_phys_info;
1915         physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error);                 // next point, assuming we followed our current path
1916         oo_interp_splines[ship_index][0].bez_set_points(3, pts);
1917
1918         // get the spline representing where this new point tells us we'd be heading
1919         a = oo_interp_points[ship_index][0];
1920         b = oo_interp_points[ship_index][1];
1921         c = oo_interp_points[ship_index][1];
1922         m_copy = *new_orient;
1923         p_copy = *new_phys_info;
1924         physics_sim(&c, &m_copy, &p_copy, avg_diff);                    // next point, given this new info
1925         oo_interp_splines[ship_index][1].bez_set_points(3, pts);        
1926
1927         // get the spline for our "bad" movement
1928         /*
1929         a = oo_interp_points[ship_index][0];
1930         b = *cur_pos;
1931         da = oo_interp_vel[ship_index][0];
1932         db = cur_phys_info->vel;
1933         oo_interp_splines[ship_index][0].herm_set_points(2, pts, d_pts);
1934
1935         // get the spline for our "good" movement
1936         a = oo_interp_points[ship_index][0];
1937         b = oo_interp_points[ship_index][1];
1938         da = oo_interp_vel[ship_index][0];
1939         db = oo_interp_vel[ship_index][1];
1940         oo_interp_splines[ship_index][1].herm_set_points(2, pts, d_pts);
1941         */
1942
1943         // now we've got a spline representing our "new" path and where we would've gone had we been perfect before
1944         // we'll modify our velocity to move along a blend of these splines.
1945 }
1946
1947 #include "alphacolors.h"
1948
1949 void oo_update_time()
1950 {       
1951 }
1952
1953 int display_oo_bez = 0;
1954 DCF(bez, "")
1955 {
1956         display_oo_bez = !display_oo_bez;
1957         if(display_oo_bez){
1958                 dc_printf("Showing interp splines");
1959         } else {
1960                 dc_printf("Not showing interp splines");
1961         }
1962 }
1963
1964 void oo_display()
1965 {
1966 /*      int idx;        
1967
1968
1969         gr_set_color_fast(&Color_bright);
1970
1971         for(idx=0; idx<MAX_SHIPS; idx++){               
1972                 // invalid ship
1973                 if(Ships[idx].objnum < 0){
1974                         continue;
1975                 }
1976
1977                 // time between updates
1978                 if( (oo_arrive_time_count[idx] == 5) && (idx != (Player_ship - Ships)) ){
1979                         gr_printf(20, 40, "avg time between updates : %f", oo_arrive_time_avg_diff[idx]);                       
1980                 }                       
1981                 
1982                 // interpolation splines
1983                 if( (oo_interp_count[idx] == 2) && (display_oo_bez) ){
1984                         oo_interp_splines[idx][0].bez_render(10, &Color_bright_red);                    // bad path
1985                         oo_interp_splines[idx][1].bez_render(10, &Color_bright_green);          // good path
1986                 }
1987         }
1988         */
1989 }
1990