]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_obj.cpp
ryan's struct patch for gcc 2.95
[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         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                 Assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW));
377                 Assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW)));
378         } 
379         // server 
380         else {
381                 // 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                 // 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         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         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_DATA( 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_DATA(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_DATA( 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         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                 // get the data for the subsystems
878                 GET_DATA( n_subsystems );
879                 for ( i = 0; i < n_subsystems; i++ ){
880                         UNPACK_PERCENT( subsystem_percent[i] );
881                 }               
882                 
883                 // fill in the subsystem data
884                 subsys_count = 0;
885                 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
886                         int subsys_type;
887
888                         val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits;
889                         subsysp->current_hits = val;
890
891                         // add the value just generated (it was zero'ed above) into the array of generic system types
892                         subsys_type = subsysp->system_info->type;                                       // this is the generic type of subsystem
893                         Assert ( subsys_type < SUBSYSTEM_MAX );
894                         shipp->subsys_info[subsys_type].current_hits += val;
895                         subsys_count++;
896
897                         // if we've reached max subsystems for some reason, bail out
898                         if(subsys_count >= n_subsystems){
899                                 break;
900                         }
901                 }
902                 
903                 // recalculate all ship subsystems
904                 ship_recalc_subsys_strength( shipp );                   
905
906                 // ai mode info
907                 ubyte umode;
908                 short submode;
909                 ushort target_signature;
910                 object *target_objp;
911
912                 GET_DATA( umode );
913                 GET_DATA( submode );
914                 GET_DATA( target_signature );           
915
916                 if(shipp->ai_index >= 0){
917                         Ai_info[shipp->ai_index].mode = umode;
918                         Ai_info[shipp->ai_index].submode = submode;             
919
920                         // set this guys target objnum
921                         target_objp = multi_get_network_object( target_signature );
922                         if ( target_objp == NULL ){
923                                 Ai_info[shipp->ai_index].target_objnum = -1;
924                         } else {
925                                 Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp);
926                         }
927                 }               
928
929                 // primary weapon energy                
930                 float weapon_energy_pct;
931                 UNPACK_PERCENT(weapon_energy_pct);
932                 shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct;             
933         }       
934
935         // support ship extra info
936         ubyte support_extra;
937         GET_DATA(support_extra);
938         if(support_extra){
939                 ushort dock_sig;
940                 int ai_flags, ai_mode, ai_submode;
941
942                 // flag         
943                 GET_DATA(ai_flags);
944                 GET_DATA(ai_mode);
945                 GET_DATA(ai_submode);
946                 GET_DATA(dock_sig);             
947
948                 // valid ship?                                                  
949                 if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){
950                         Ai_info[shipp->ai_index].ai_flags = ai_flags;
951                         Ai_info[shipp->ai_index].mode = ai_mode;
952                         Ai_info[shipp->ai_index].submode = ai_submode;
953
954                         object *objp = multi_get_network_object( dock_sig );
955                         if(objp != NULL){
956                                 Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp);
957                         }
958                 }                       
959         } 
960
961         // afterburner info
962         if(oo_flags & OO_AFTERBURNER_NEW){
963                 // maybe turn them on
964                 if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){
965                         afterburners_start(pobjp);
966                 }
967         } else {
968                 // maybe turn them off
969                 if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){
970                         afterburners_stop(pobjp);
971                 }
972         }
973
974         // primary info (only clients care about this)
975         if( !MULTIPLAYER_MASTER && (shipp != NULL) ){
976                 // what bank
977                 if(oo_flags & OO_PRIMARY_BANK){
978                         shipp->weapons.current_primary_bank = 1;
979                 } else {
980                         shipp->weapons.current_primary_bank = 0;
981                 }
982
983                 // linked or not
984                 shipp->flags &= ~SF_PRIMARY_LINKED;
985                 if(oo_flags & OO_PRIMARY_LINKED){
986                         shipp->flags |= SF_PRIMARY_LINKED;
987                 }
988
989                 // trigger down or not - server doesn't care about this. he'll get it from clients anyway               
990                 shipp->flags &= ~SF_TRIGGER_DOWN;
991                 if(oo_flags & OO_TRIGGER_DOWN){
992                         shipp->flags |= SF_TRIGGER_DOWN;
993                 }               
994         }
995         
996         // if we're the multiplayer server, set eye position and orient
997         if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){
998                 pl->s_info.eye_pos = pobjp->pos;
999                 pl->s_info.eye_orient = pobjp->orient;
1000         }               
1001
1002         // update the sequence #
1003         shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num;
1004
1005         // flag the object as just updated
1006         // pobjp->flags |= OF_JUST_UPDATED;
1007         
1008         return offset;
1009 }
1010
1011 // reset the timestamp appropriately for the passed in object
1012 void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone)
1013 {
1014         int stamp = 0;  
1015
1016         // if this is the guy's target, 
1017         if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){
1018                 stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level];
1019         } else {
1020                 // reset the timestamp appropriately
1021                 if(in_cone){
1022                         // base it upon range
1023                         switch(range){
1024                         case OO_NEAR:
1025                                 stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level];
1026                                 break;
1027
1028                         case OO_MIDRANGE:
1029                                 stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level];
1030                                 break;
1031
1032                         case OO_FAR:
1033                                 stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level];
1034                                 break;
1035                         }
1036                 } else {
1037                         // base it upon range
1038                         switch(range){
1039                         case OO_NEAR:
1040                                 stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level];
1041                                 break;
1042
1043                         case OO_MIDRANGE:
1044                                 stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level];
1045                                 break;
1046
1047                         case OO_FAR:
1048                                 stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level];
1049                                 break;
1050                         }
1051                 }                                               
1052         }
1053
1054         // reset the timestamp for this object
1055         if(objp->type == OBJ_SHIP){
1056                 Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp);
1057         } 
1058 }
1059
1060 // reset the timestamp appropriately for the passed in object
1061 void multi_oo_reset_status_timestamp(object *objp, int player_index)
1062 {
1063         Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME);
1064 }
1065
1066 // reset the timestamp appropriately for the passed in object
1067 void multi_oo_reset_subsys_timestamp(object *objp, int player_index)
1068 {
1069         Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME);
1070 }
1071
1072 // determine what needs to get sent for this player regarding the passed object, and when
1073 int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data)
1074 {
1075         ubyte oo_flags;
1076         int stamp;
1077         int player_index;
1078         vector player_eye;
1079         vector obj_dot;
1080         float eye_dot, dist;
1081         int in_cone;
1082         int range;
1083         int ship_index;
1084         ship *shipp;
1085         ship_info *sip;
1086         ushort cur_pos_chksum = 0;
1087         ushort cur_orient_chksum = 0;
1088
1089         // if the timestamp has elapsed for this guy, send stuff
1090         player_index = NET_PLAYER_INDEX(pl);
1091         if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){
1092                 return 0;
1093         }
1094
1095         // determine what the timestamp is for this object
1096         if(obj->type == OBJ_SHIP){
1097                 stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp;
1098                 ship_index = &Ships[obj->instance] - Ships;
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         object *pobj;   
1237
1238         // if the player has an invalid objnum..
1239         if(pl->player->objnum < 0){
1240                 return;
1241         }
1242
1243         // get the player's object
1244         pobj = &Objects[pl->player->objnum];
1245
1246         object *targ_obj;       
1247
1248         // build the list of ships to check against
1249         multi_oo_build_ship_list(pl);
1250
1251         // do nothing if he has no object targeted, or if he has a weapon targeted
1252         if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){
1253                 // build the header
1254                 BUILD_HEADER(OBJECT_UPDATE);            
1255         
1256                 // get a pointer to the object
1257                 targ_obj = &Objects[pl->s_info.target_objnum];
1258         
1259                 // run through the maybe_update function
1260                 add_size = multi_oo_maybe_update(pl, targ_obj, data_add);
1261
1262                 // copy in any relevant data
1263                 if(add_size){
1264                         stop = 0xff;                    
1265                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1266                         ADD_DATA(stop);
1267
1268                         memcpy(data + packet_size, data_add, add_size);
1269                         packet_size += add_size;                
1270                 }
1271         } else {
1272                 // just build the header for the rest of the function
1273                 BUILD_HEADER(OBJECT_UPDATE);            
1274         }
1275                 
1276         idx = 0;
1277         while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){
1278                 // if this guy is over his datarate limit, do nothing
1279                 if(multi_oo_rate_exceeded(pl)){
1280                         nprintf(("Network","Capping client\n"));
1281                         idx++;
1282
1283                         continue;
1284                 }                       
1285
1286                 // get the object
1287                 moveup = &Objects[Ships[OO_ship_index[idx]].objnum];
1288
1289                 // maybe send some info         
1290                 add_size = multi_oo_maybe_update(pl, moveup, data_add);
1291
1292                 // if this data is too much for the packet, send off what we currently have and start over
1293                 if(packet_size + add_size > OO_MAX_SIZE){
1294                         stop = 0x00;                    
1295                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1296                         ADD_DATA(stop);
1297                                                                         
1298                         multi_io_send(pl, data, packet_size);
1299                         pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1300
1301                         packet_size = 0;
1302                         BUILD_HEADER(OBJECT_UPDATE);                    
1303                 }
1304
1305                 if(add_size){
1306                         stop = 0xff;                    
1307                         multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1308                         ADD_DATA(stop);
1309
1310                         // copy in the data
1311                         memcpy(data + packet_size,data_add,add_size);
1312                         packet_size += add_size;
1313                 }
1314
1315                 // next ship
1316                 idx++;
1317         }
1318
1319         // if we have anything more than 3 byte in the packet, send the last one off
1320         if(packet_size > 3){
1321                 stop = 0x00;            
1322                 multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1);
1323                 ADD_DATA(stop);
1324                                                                 
1325                 multi_io_send(pl, data, packet_size);
1326                 pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE;
1327         }
1328 }
1329
1330 // process all object update details for this frame
1331 void multi_oo_process()
1332 {
1333         int idx;        
1334         
1335         // process each player
1336         for(idx=0; idx<MAX_PLAYERS; idx++){
1337                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) /*&& !MULTI_OBSERVER(Net_players[idx])*/ ){
1338                         // now process the rest of the objects
1339                         multi_oo_process_all(&Net_players[idx]);
1340
1341                         // do firing stuff for this player
1342                         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)){
1343                                 if((Objects[Net_players[idx].player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].player->objnum].flags & OF_SHOULD_BE_DEAD)){
1344                                         obj_player_fire_stuff( &Objects[Net_players[idx].player->objnum], Net_players[idx].player->ci );
1345                                 }
1346                         }
1347                 }
1348         }
1349 }
1350
1351 // process incoming object update data
1352 void multi_oo_process_update(ubyte *data, header *hinfo)
1353 {       
1354         ubyte stop;     
1355         int player_index;       
1356         int offset = HEADER_LENGTH;
1357         net_player *pl = NULL;  
1358
1359         // if this is processed on the server, its a client object update packet
1360         player_index = -1;
1361         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1362                 // determine what player this came from 
1363                 player_index = find_player_id(hinfo->id);
1364                 if(player_index != -1){                                         
1365                         pl = &Net_players[player_index];
1366                 } else {                        
1367                         pl = NULL;
1368                 }
1369         }
1370         // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player
1371         else {                                          
1372                 pl = Net_player;
1373         }
1374
1375         GET_DATA(stop);
1376         
1377         while(stop == 0xff){
1378                 // process the data
1379                 offset += multi_oo_unpack_data(pl, data + offset);
1380
1381                 GET_DATA(stop);
1382         }
1383         PACKET_SET_SIZE();
1384 }
1385
1386 // initialize all object update timestamps (call whenever entering gameplay state)
1387 void multi_oo_gameplay_init()
1388 {
1389         int cur, s_idx, idx;
1390         //ship_obj *so;                         
1391         ship *shipp;
1392         /*
1393         int num_ships = ship_get_num_ships();
1394
1395         if(num_ships <= 0){
1396                 return;
1397         }
1398         split = 3000 / num_ships;
1399         */
1400         //split = 1;
1401         //split = 150;
1402
1403         // server should setup initial update timestamps        
1404         // stagger initial updates over 3 seconds or so
1405         cur = 0;
1406         //for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1407                 //if(Objects[so->objnum].type == OBJ_SHIP){
1408         for(s_idx=0; s_idx<MAX_SHIPS; s_idx++){
1409                         shipp = &Ships[s_idx];
1410                 
1411                         // update the timestamps
1412                         for(idx=0;idx<MAX_PLAYERS;idx++){
1413                                 shipp->np_updates[idx].update_stamp = timestamp(cur);
1414                                 shipp->np_updates[idx].status_update_stamp = timestamp(cur);
1415                                 shipp->np_updates[idx].subsys_update_stamp = timestamp(cur);
1416                                 shipp->np_updates[idx].seq = 0;         
1417                                 shipp->np_updates[idx].pos_chksum = 0;
1418                                 shipp->np_updates[idx].orient_chksum = 0;
1419                         } 
1420                         
1421                         oo_arrive_time_count[shipp - Ships] = 0;                        
1422                         oo_interp_count[shipp - Ships] = 0;
1423
1424                         // increment the time
1425 //                      cur += split;                   
1426                 }
1427         //}                     
1428
1429         // reset datarate stamp now
1430         extern int OO_gran;
1431         for(idx=0; idx<MAX_PLAYERS; idx++){
1432                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1433         }
1434 }
1435
1436 // send control info for a client (which is basically a "reverse" object update)
1437 void multi_oo_send_control_info()
1438 {
1439         ubyte data[MAX_PACKET_SIZE], stop;
1440         ubyte data_add[MAX_PACKET_SIZE];
1441         ubyte oo_flags; 
1442         int add_size;
1443         int packet_size = 0;
1444
1445         // if I'm dying or my object type is not a ship, bail here
1446         if((Player_obj != NULL) && (Player_ship->flags & SF_DYING)){
1447                 return;
1448         }       
1449         
1450         // build the header
1451         BUILD_HEADER(OBJECT_UPDATE);            
1452
1453         // pos and orient always
1454         oo_flags = (OO_POS_NEW | OO_ORIENT_NEW);                
1455
1456         // pack the appropriate info into the data
1457         add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add);
1458
1459         // copy in any relevant data
1460         if(add_size){
1461                 stop = 0xff;            
1462                 multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1463                 
1464                 ADD_DATA(stop);
1465
1466                 memcpy(data + packet_size, data_add, add_size);
1467                 packet_size += add_size;                
1468         }
1469
1470         // add the final stop byte
1471         stop = 0x0;     
1472         multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1);
1473         ADD_DATA(stop);
1474
1475         // increment sequence #
1476         Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++;
1477
1478         // send to the server
1479         if(Netgame.server != NULL){                                                             
1480                 multi_io_send(Net_player, data, packet_size);
1481         }
1482 }
1483
1484 // display any oo info on the hud
1485 void multi_oo_display()
1486 {
1487 #ifndef NDEBUG  
1488 #endif
1489 }
1490
1491
1492 // ---------------------------------------------------------------------------------------------------
1493 // DATARATE DEFINES/VARS
1494 //
1495
1496 // low object update datarate limit
1497 #define OO_LIMIT_LOW                            1800
1498 #define OO_LIMIT_MED                            3400
1499
1500 // timestamp for sending control info (movement only - we'll send button info all the time)
1501 #define OO_CIRATE                                       85                                      // 15x a second
1502 int Multi_cirate_stamp                  = -1;                           // timestamp for waiting on control info time
1503 int Multi_cirate_can_send               = 1;                            // if we can send control info this frame
1504
1505 // global max rates
1506 int OO_server_rate = -1;                                                        // max _total_ bandwidth to send to all clients
1507 int OO_client_rate = -1;                                                        // max bandwidth to go to an individual client
1508
1509 // update timestamp for server datarate checking
1510 #define RATE_UPDATE_TIME                1250                            // in ms
1511 int OO_server_rate_stamp = -1;
1512
1513 // bandwidth granularity
1514 int OO_gran = 1;
1515 DCF(oog, "")
1516 {
1517         dc_get_arg(ARG_INT);
1518         OO_gran = Dc_arg_int;
1519 }
1520
1521 // process datarate limiting stuff for the server
1522 void multi_oo_server_process();
1523
1524 // process datarate limiting stuff for the client
1525 void multi_oo_client_process();
1526
1527 // update the server datarate
1528 void multi_oo_update_server_rate();
1529
1530
1531 // ---------------------------------------------------------------------------------------------------
1532 // DATARATE FUNCTIONS
1533 //
1534
1535 // process all object update datarate details
1536 void multi_oo_rate_process()
1537 {
1538         // if I have no valid player, drop out here
1539         if(Net_player == NULL){
1540                 return;
1541         }
1542
1543         // if we're not in mission, don't do anything
1544         if(!(Game_mode & GM_IN_MISSION)){
1545                 return;
1546         }
1547
1548         // if I'm the server of a game, process server stuff
1549         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1550                 multi_oo_server_process();
1551         }
1552         // otherwise process client-side stuff
1553         else {
1554                 multi_oo_client_process();
1555         }
1556 }
1557
1558 // process datarate limiting stuff for the server
1559 void multi_oo_server_process()
1560 {
1561         int idx;
1562         
1563         // go through all players
1564         for(idx=0;idx<MAX_PLAYERS;idx++){
1565                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_SERVER(Net_players[idx])){
1566                         // if his timestamp is -1 or has expired, reset it and zero his rate byte count
1567                         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)) ){
1568                                 Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) );
1569                                 Net_players[idx].s_info.rate_bytes = 0;
1570                         }
1571                 }
1572         }
1573
1574         // determine if we should be updating the server datarate
1575         if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){
1576                 // reset the timestamp
1577                 OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME);
1578
1579                 // update the server datarate
1580                 multi_oo_update_server_rate();
1581
1582                 // nprintf(("Network","UPDATING SERVER DATARATE\n"));
1583         }       
1584 }
1585
1586 // process datarate limiting stuff for the client
1587 void multi_oo_client_process()
1588 {
1589         // if the timestamp is -1 or has elapsed, reset it
1590         if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){
1591                 Multi_cirate_can_send = 1;
1592                 Multi_cirate_stamp = timestamp(OO_CIRATE);
1593         }       
1594 }
1595
1596
1597 // datarate limiting system for server -------------------------------------
1598
1599 // initialize the rate limiting system for all players
1600 void multi_oo_rate_init_all()
1601 {
1602         int idx;
1603
1604         // if I don't have a net_player, bail here
1605         if(Net_player == NULL){
1606                 return;
1607         }
1608
1609         // if I'm the server of the game
1610         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ 
1611                 // go through all players
1612                 for(idx=0;idx<MAX_PLAYERS;idx++){
1613                         if(MULTI_CONNECTED(Net_players[idx])){
1614                                 multi_oo_rate_init(&Net_players[idx]);
1615                         }
1616                 }
1617
1618                 OO_server_rate_stamp = -1;
1619         }
1620         // if i'm the client, initialize my control info datarate stuff
1621         else {
1622                 Multi_cirate_stamp = -1;
1623                 Multi_cirate_can_send = 1;
1624         }
1625 }
1626
1627 // initialize the rate limiting for the passed in player
1628 void multi_oo_rate_init(net_player *pl)
1629 {
1630         // reinitialize his datarate timestamp
1631         pl->s_info.rate_stamp = -1;
1632         pl->s_info.rate_bytes = 0;
1633 }
1634
1635 // if the given net-player has exceeded his datarate limit
1636 int multi_oo_rate_exceeded(net_player *pl)
1637 {
1638         int rate_compare;
1639                 
1640         // check against the guy's object update level
1641         switch(pl->p_info.options.obj_update_level){
1642         // low update level
1643         case OBJ_UPDATE_LOW:
1644                 // the low object update limit
1645                 rate_compare = OO_LIMIT_LOW;
1646                 break;
1647
1648         // medium update level
1649         case OBJ_UPDATE_MEDIUM:         
1650                 // the low object update limit
1651                 rate_compare = OO_LIMIT_MED;
1652                 break;
1653
1654         // high update level - super high datarate (no capping, just intelligent updating)
1655         case OBJ_UPDATE_HIGH:
1656                 rate_compare = 100000000;
1657                 break;
1658
1659         // LAN - no rate max
1660         case OBJ_UPDATE_LAN:
1661                 return 0;
1662
1663         // default level
1664         default:
1665                 Int3();
1666                 rate_compare = OO_LIMIT_LOW;
1667                 break;
1668         }
1669
1670         // if the server global rate PER CLIENT (OO_client_rate) is actually lower
1671         if(OO_client_rate < rate_compare){
1672                 rate_compare = OO_client_rate;
1673         }
1674
1675         // compare his bytes sent against the allowable amount
1676         if(pl->s_info.rate_bytes >= rate_compare){
1677                 return 1;
1678         }
1679
1680         // we're allowed to send
1681         return 0;
1682 }
1683
1684 // if it is ok for me to send a control info (will be ~N times a second)
1685 int multi_oo_cirate_can_send()
1686 {
1687         // if we're allowed to send
1688         if(Multi_cirate_can_send){
1689                 Multi_cirate_can_send = 0;
1690                 return 1;
1691         } 
1692         
1693         return 0;               
1694 }
1695
1696 // dynamically update the server capped bandwidth rate
1697 void multi_oo_update_server_rate()
1698 {       
1699         int num_connections;    
1700         
1701         // bail conditions
1702         if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1703                 return;
1704         }
1705
1706         // get the # of connections
1707         num_connections = multi_num_connections();
1708         if(!(Game_mode & GM_STANDALONE_SERVER)){
1709                 num_connections--;
1710         }
1711         // make sure we always pretend there's at least one guy available
1712         if(num_connections <= 0){
1713                 num_connections = 1;
1714         }
1715                 
1716         // set the data rate    
1717         switch(Net_player->p_info.options.obj_update_level){
1718         // LAN update level
1719         case OBJ_UPDATE_LAN:
1720                 // set to something super big so we don't limit anything
1721                 OO_server_rate = 500000000;
1722                 break;
1723
1724         // high update level
1725         case OBJ_UPDATE_HIGH:
1726                 // set to 0 so we don't limit anything
1727                 OO_server_rate = Multi_options_g.datarate_cap;
1728                 break;
1729
1730         // medium update level
1731         case OBJ_UPDATE_MEDIUM:
1732                 // set the rate to be "medium" update level
1733                 OO_server_rate = OO_LIMIT_MED;
1734                 break;
1735
1736         // low update level 
1737         case OBJ_UPDATE_LOW:
1738                 // set the rate to be the "low" update level
1739                 OO_server_rate = OO_LIMIT_LOW;
1740                 break;
1741
1742         default:
1743                 Int3();
1744                 return;
1745         }       
1746
1747         // set the individual client level
1748         OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections);
1749 }
1750
1751 // reset all sequencing info (obsolete for new object update stuff)
1752 void multi_oo_reset_sequencing()
1753 {               
1754 }
1755
1756 // is this object one which needs to go through the interpolation
1757 int multi_oo_is_interp_object(object *objp)
1758 {       
1759         // if not multiplayer, skip it
1760         if(!(Game_mode & GM_MULTIPLAYER)){
1761                 return 0;
1762         }
1763
1764         // if its not a ship, skip it
1765         if(objp->type != OBJ_SHIP){
1766                 return 0;
1767         }
1768
1769         // other bogus cases
1770         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1771                 return 0;
1772         }
1773
1774         // if I'm a client and this is not me, I need to interp it
1775         if(!MULTIPLAYER_MASTER){
1776                 if(objp != Player_obj){
1777                         return 1;
1778                 } else {
1779                         return 0;
1780                 }
1781         }
1782
1783         // servers only interpolate other player ships
1784         if(!(objp->flags & OF_PLAYER_SHIP)){
1785                 return 0;
1786         }
1787
1788         // here we know its a player ship - is it mine?
1789         if(objp == Player_obj){
1790                 return 0;
1791         }
1792
1793         // interp it
1794         return 1;
1795 }
1796
1797 // interp
1798 void multi_oo_interp(object *objp)
1799 {               
1800         // make sure its a valid ship
1801         Assert(Game_mode & GM_MULTIPLAYER);
1802         if(objp->type != OBJ_SHIP){
1803                 return;
1804         }
1805         if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1806                 return;
1807         }       
1808
1809         // increment his approx "next" time
1810         oo_arrive_time_next[objp->instance] += flFrametime;
1811
1812         // do stream weapon firing for this ship
1813         Assert(objp != Player_obj);
1814         if(objp != Player_obj){
1815                 ship_fire_primary(objp, 1, 0);
1816         }
1817
1818         // if this ship doesn't have enough data points yet, skip it
1819         if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){
1820                 return;
1821         }
1822
1823         // store the magnitude of his velocity
1824         // float vel_mag = vm_vec_mag(&objp->phys_info.vel);
1825
1826         // determine how far along we are (0.0 to 1.0) until we should be getting the next packet
1827         float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance];
1828
1829         // gr_set_color_fast(&Color_bright);
1830         // gr_printf(100, 10, "%f\n", t);
1831         
1832         // we've overshot. hmm. just keep the sim running I guess       
1833         if(t > 1.0f){
1834                 physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1835                 return;
1836         }       
1837
1838         // otherwise, blend the two curves together to get the new point
1839         float u = 0.5f + (t * 0.5f);
1840         vector p_bad, p_good;
1841         oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u);
1842         oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u);         
1843         vm_vec_scale(&p_good, t);
1844         vm_vec_scale(&p_bad, 1.0f - t);
1845         vm_vec_add(&objp->pos, &p_bad, &p_good);        
1846
1847         // set new velocity
1848         // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos);
1849         
1850         // run the sim for rotation     
1851         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1852
1853         // blend velocity vectors together with an average weight
1854         /*
1855         vector v_bad, v_good;
1856         oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0);
1857         oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0);     
1858
1859         // t -= 1.0f;
1860         vm_vec_scale(&v_good, t);
1861         vm_vec_scale(&v_bad, 1.0f - t);
1862         vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good);
1863         
1864         // run the sim
1865         physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1866         */
1867
1868         /*
1869         vector v_bad, v_good;
1870         oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0);
1871         oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0);     
1872
1873         // t -= 1.0f;
1874         vm_vec_scale(&v_good, t);
1875         vm_vec_scale(&v_bad, 1.0f - t);
1876         vm_vec_avg(&objp->pos, &v_bad, &v_good);        
1877         
1878         // run the sim
1879         // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime);
1880         physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime);
1881         */
1882 }
1883
1884 float oo_error = 0.8f;
1885 DCF(oo_error, "")
1886 {
1887         dc_get_arg(ARG_FLOAT);
1888         oo_error = Dc_arg_float;
1889 }
1890
1891 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)
1892 {
1893         vector a, b, c;
1894         // vector da, db, dc;
1895         matrix m_copy;
1896         physics_info p_copy;
1897         vector *pts[3] = {&a, &b, &c};  
1898         // vector *d_pts[3] = {&da, &db, &dc};
1899         
1900         // average time between packets
1901         float avg_diff = oo_arrive_time_avg_diff[ship_index];   
1902
1903         // would this cause us to rubber-band?
1904         vector v_norm = cur_phys_info->vel;     
1905         vector v_dir;
1906         vm_vec_sub(&v_dir, new_pos, cur_pos);   
1907         if(!IS_VEC_NULL(&v_norm) && !IS_VEC_NULL(&v_dir)){
1908                 vm_vec_normalize(&v_dir);
1909                 vm_vec_normalize(&v_norm);      
1910                 if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){
1911                         *new_pos = *cur_pos;
1912                 }
1913         }
1914         
1915         // get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether
1916         a = oo_interp_points[ship_index][0];
1917         b = *cur_pos;
1918         c = *cur_pos;
1919         m_copy = *cur_orient;
1920         p_copy = *cur_phys_info;
1921         physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error);                 // next point, assuming we followed our current path
1922         oo_interp_splines[ship_index][0].bez_set_points(3, pts);
1923
1924         // get the spline representing where this new point tells us we'd be heading
1925         a = oo_interp_points[ship_index][0];
1926         b = oo_interp_points[ship_index][1];
1927         c = oo_interp_points[ship_index][1];
1928         m_copy = *new_orient;
1929         p_copy = *new_phys_info;
1930         physics_sim(&c, &m_copy, &p_copy, avg_diff);                    // next point, given this new info
1931         oo_interp_splines[ship_index][1].bez_set_points(3, pts);        
1932
1933         // get the spline for our "bad" movement
1934         /*
1935         a = oo_interp_points[ship_index][0];
1936         b = *cur_pos;
1937         da = oo_interp_vel[ship_index][0];
1938         db = cur_phys_info->vel;
1939         oo_interp_splines[ship_index][0].herm_set_points(2, pts, d_pts);
1940
1941         // get the spline for our "good" movement
1942         a = oo_interp_points[ship_index][0];
1943         b = oo_interp_points[ship_index][1];
1944         da = oo_interp_vel[ship_index][0];
1945         db = oo_interp_vel[ship_index][1];
1946         oo_interp_splines[ship_index][1].herm_set_points(2, pts, d_pts);
1947         */
1948
1949         // now we've got a spline representing our "new" path and where we would've gone had we been perfect before
1950         // we'll modify our velocity to move along a blend of these splines.
1951 }
1952
1953 #include "alphacolors.h"
1954
1955 void oo_update_time()
1956 {       
1957 }
1958
1959 int display_oo_bez = 0;
1960 DCF(bez, "")
1961 {
1962         display_oo_bez = !display_oo_bez;
1963         if(display_oo_bez){
1964                 dc_printf("Showing interp splines");
1965         } else {
1966                 dc_printf("Not showing interp splines");
1967         }
1968 }
1969
1970 void oo_display()
1971 {
1972 /*      int idx;        
1973
1974
1975         gr_set_color_fast(&Color_bright);
1976
1977         for(idx=0; idx<MAX_SHIPS; idx++){               
1978                 // invalid ship
1979                 if(Ships[idx].objnum < 0){
1980                         continue;
1981                 }
1982
1983                 // time between updates
1984                 if( (oo_arrive_time_count[idx] == 5) && (idx != (Player_ship - Ships)) ){
1985                         gr_printf(20, 40, "avg time between updates : %f", oo_arrive_time_avg_diff[idx]);                       
1986                 }                       
1987                 
1988                 // interpolation splines
1989                 if( (oo_interp_count[idx] == 2) && (display_oo_bez) ){
1990                         oo_interp_splines[idx][0].bez_render(10, &Color_bright_red);                    // bad path
1991                         oo_interp_splines[idx][1].bez_render(10, &Color_bright_green);          // good path
1992                 }
1993         }
1994         */
1995 }
1996