src/tron/gCycle.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023   
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "eEventNotification.h"
00029 #include "gCycle.h"
00030 #include "nConfig.h"
00031 #include "rModel.h"
00032 //#include "eTess.h"
00033 #include "eGrid.h"
00034 #include "rTexture.h"
00035 #include "eTimer.h"
00036 #include "tInitExit.h"
00037 #include "tRecorder.h"
00038 #include "rScreen.h"
00039 #include "rFont.h"
00040 #include "gSensor.h"
00041 #include "gJoystick.h"
00042 #include "ePlayer.h"
00043 
00044 #include "eSoundMixer.h"
00045 
00046 #include "eGrid.h"
00047 #include "eFloor.h"
00048 #include "gSparks.h"
00049 #include "gExplosion.h"
00050 #include "gWall.h"
00051 #include "nKrawall.h"
00052 #include "gAIBase.h"
00053 #include "eDebugLine.h"
00054 #include "eLagCompensation.h"
00055 #include "gArena.h"
00056 #include "gStatistics.h"
00057 
00058 #include "cCockpit.h"
00059 
00060 #include "tMath.h"
00061 #include <stdlib.h>
00062 #include <fstream>
00063 #include <memory>
00064 
00065 #ifndef DEDICATED
00066 #define DONTDOIT
00067 #include "rRender.h"
00068 #endif
00069 
00070 // TODO: get rid of this
00071 #include "tDirectories.h"
00072 
00073 // also used in gWall.cpp
00074 bool sg_gnuplotDebug = false;
00075 
00076 #define GNUPLOT_DEBUG
00077 // #define DELAYEDTURN_DEBUG
00078 
00079 #ifdef GNUPLOT_DEBUG
00080 static tSettingItem<bool> sg_("DEBUG_GNUPLOT",sg_gnuplotDebug);
00081 #endif
00082 
00083 static REAL sg_minDropInterval=0.05;
00084 static tSettingItem< REAL > sg_minDropIntervalConf( "CYCLE_MIN_WALLDROP_INTERVAL", sg_minDropInterval );
00085 
00086 static bool sg_predictWalls=true;
00087 static tSettingItem< bool > sg_predictWallsConf( "PREDICT_WALLS", sg_predictWalls );
00088 
00089 //  *****************************************************************
00090 
00091 static nNOInitialisator<gCycle> cycle_init(320,"cycle");
00092 
00093 //  *****************************************************************
00094 
00095 // static nVersionFeature sg_DoubleSpeed( 1 );
00096 
00097 //tCONTROLLED_PTR(ePlayerNetID)   lastEnemyInfluence;   // the last enemy wall we encountered
00098 //REAL                                                  lastTime;                               // the time it was drawn at
00099 bool headlights=0;
00100 bool cycleprograminited=0;
00101 
00102 static float sg_cycleSyncSmoothTime = .1f;
00103 static tSettingItem<float> conf_smoothTime ("CYCLE_SMOOTH_TIME", sg_cycleSyncSmoothTime);
00104 
00105 static float sg_cycleSyncSmoothMinSpeed = .2f;
00106 static tSettingItem<float> conf_smoothMinSpeed ("CYCLE_SMOOTH_MIN_SPEED", sg_cycleSyncSmoothMinSpeed);
00107 
00108 static float sg_cycleSyncSmoothThreshold = .2f;
00109 static tSettingItem<float> conf_smoothThreshold ("CYCLE_SMOOTH_THRESHOLD", sg_cycleSyncSmoothThreshold);
00110 
00111 static REAL sg_enemyChatbotTimePenalty = 30.0f;   
00112 static tSettingItem<REAL> sg_enemyChatbotTimePenaltyConf( "ENEMY_CHATBOT_PENALTY", sg_enemyChatbotTimePenalty );
00113 extern REAL sg_suicideTimeout;
00114 
00115 REAL            gCycle::wallsStayUpDelay=8.0f;  // the time the cycle walls stay up ( negative values: they stay up forever )
00116 
00117 REAL            gCycle::wallsLength=-1.0f;              // the maximum total length of the walls
00118 REAL            gCycle::explosionRadius=4.0f;   // the radius of the holes blewn in by an explosion
00119 
00120 static          nSettingItem<REAL> *c_pwsud = NULL, *c_pwl = NULL, *c_per = NULL;
00121 
00122 void gCycle::PrivateSettings()
00123 {
00124     static nSettingItem<REAL> c_wsud("CYCLE_WALLS_STAY_UP_DELAY",wallsStayUpDelay);
00125     static nSettingItem<REAL> c_wl("CYCLE_WALLS_LENGTH",wallsLength);
00126     static nSettingItem<REAL> c_er("CYCLE_EXPLOSION_RADIUS",explosionRadius);
00127 
00128     c_pwsud=&c_wsud;
00129     c_pwl  =&c_wl;
00130     c_per  =&c_er;
00131 }
00132 
00133 // sound speed divisor
00134 static REAL sg_speedCycleSound=15;
00135 static nSettingItem<REAL> c_ss("CYCLE_SOUND_SPEED",
00136                                sg_speedCycleSound);
00137 
00138 // time after spawning it takes the cycle to start building a wall
00139 static REAL sg_cycleWallTime=0.0;
00140 static nSettingItemWatched<REAL>
00141 sg_cycleWallTimeConf("CYCLE_WALL_TIME",
00142                      sg_cycleWallTime,
00143                      nConfItemVersionWatcher::Group_Bumpy,
00144                      14);
00145 
00146 // time after spawning during which a cycle can't be killed
00147 static REAL sg_cycleInvulnerableTime=0.0;
00148 static nSettingItemWatched<REAL>
00149 sg_cycleInvulnerableTimeConf("CYCLE_INVULNERABLE_TIME",
00150                              sg_cycleInvulnerableTime,
00151                              nConfItemVersionWatcher::Group_Bumpy,
00152                              12);
00153 
00154 // time after spawning during which a cycle can't be killed
00155 static bool sg_cycleFirstSpawnProtection=false;
00156 static nSettingItemWatched<bool>
00157 sg_cycleFirstSpawnProtectionConf("CYCLE_FIRST_SPAWN_PROTECTION",
00158                                  sg_cycleFirstSpawnProtection,
00159                                  nConfItemVersionWatcher::Group_Bumpy,
00160                                  12);
00161 
00162 // time intevals between server-client syncs
00163 static REAL sg_syncIntervalEnemy=1;
00164 static tSettingItem<REAL> c_sie( "CYCLE_SYNC_INTERVAL_ENEMY",
00165                                  sg_syncIntervalEnemy );
00166 
00167 static REAL sg_syncIntervalSelf=.1;
00168 static tSettingItem<REAL> c_sis( "CYCLE_SYNC_INTERVAL_SELF",
00169                                  sg_syncIntervalSelf );
00170 
00171 // flag controlling case-by-case-niceness for older clients
00172 static bool sg_avoidBadOldClientSync=true;
00173 static tSettingItem<bool> c_sbs( "CYCLE_AVOID_OLDCLIENT_BAD_SYNC",
00174                                  sg_avoidBadOldClientSync );
00175 
00176 // fast forward factor for extrapolating sync
00177 static REAL sg_syncFF=10;
00178 static tSettingItem<REAL> c_sff( "CYCLE_SYNC_FF",
00179                                  sg_syncFF );
00180 
00181 static int sg_syncFFSteps=1;
00182 static tSettingItem<int> c_sffs( "CYCLE_SYNC_FF_STEPS",
00183                                  sg_syncFFSteps );
00184 
00185 // client side bugfix: local tunneling is avoided on syncs
00186 static nVersionFeature sg_NoLocalTunnelOnSync( 4 );
00187 
00188 static REAL sg_GetSyncIntervalSelf( gCycle* cycle )
00189 {
00190     if ( sg_NoLocalTunnelOnSync.Supported( cycle->Owner() ) )
00191         return sg_syncIntervalSelf;
00192     else
00193         return sg_syncIntervalEnemy * 10;
00194 }
00195 
00196 // moviepack hack
00197 //static bool moviepack_hack=false;       // do we use it?
00198 //static tSettingItem<bool> ump("MOVIEPACK_HACK",moviepack_hack);
00199 
00200 static int score_hole=0;
00201 static tSettingItem<int> s_h("SCORE_HOLE",score_hole);
00202 
00203 static int score_survive=0;
00204 static tSettingItem<int> s_sur("SCORE_SURVIVE",score_survive);
00205 
00206 static int score_die=-2;
00207 static tSettingItem<int> s_d("SCORE_DIE",score_die);
00208 
00209 static int score_kill=3;
00210 static tSettingItem<int> s_k("SCORE_KILL",score_kill);
00211 
00212 static int score_suicide=-4;
00213 static tSettingItem<int> s_s("SCORE_SUICIDE",score_suicide);
00214 
00215 // input control
00216 
00217 uActionPlayer gCycle::s_brake("CYCLE_BRAKE", -5);
00218 static uActionPlayer s_brakeToggle("CYCLE_BRAKE_TOGGLE", -5);
00219 
00220 // a class of textures where the transparent part of the
00221 // image is replaced by the player color
00222 class gTextureCycle: public rSurfaceTexture
00223 {
00224     gRealColor color_; // player color
00225     bool wheel; // wheel or body
00226 public:
00227     gTextureCycle(rSurface const & surface, const gRealColor& color,bool repx=0,bool repy=0,bool wheel=false);
00228 
00229     virtual void ProcessImage(SDL_Surface *im);
00230 
00231     virtual void OnSelect(bool enforce);
00232 };
00233 
00234 gTextureCycle::gTextureCycle(rSurface const & surface, const gRealColor& color,bool repx,bool repy,bool w)
00235         :rSurfaceTexture(rTextureGroups::TEX_OBJ,surface,repx,repy),
00236         color_(color),wheel(w)
00237 {
00238     Select();
00239 }
00240 
00241 void gTextureCycle::ProcessImage(SDL_Surface *im)
00242 {
00243 #ifndef DEDICATED
00244     // blend transparent texture parts with cycle color
00245     tVERIFY(im->format->BytesPerPixel == 4);
00246     GLubyte R=int(color_.r*255);
00247     GLubyte G=int(color_.g*255);
00248     GLubyte B=int(color_.b*255);
00249 
00250     GLubyte *pixels =reinterpret_cast<GLubyte *>(im->pixels);
00251 
00252     for(int i=im->w*im->h-1;i>=0;i--){
00253         GLubyte alpha=pixels[4*i+3];
00254         pixels[4*i  ] = (alpha * pixels[4*i  ] + (255-alpha)*R) >> 8;
00255         pixels[4*i+1] = (alpha * pixels[4*i+1] + (255-alpha)*G) >> 8;
00256         pixels[4*i+2] = (alpha * pixels[4*i+2] + (255-alpha)*B) >> 8;
00257         pixels[4*i+3] = 255;
00258     }
00259 #endif
00260 }
00261 
00262 void gTextureCycle::OnSelect(bool enforce){
00263 #ifndef DEDICATED
00264     rISurfaceTexture::OnSelect(enforce);
00265 
00266     if(rTextureGroups::TextureMode[rTextureGroups::TEX_OBJ]<0){
00267         REAL R=color_.r,G=color_.g,B=color_.b;
00268         if(wheel){
00269             R*=.7;
00270             G*=.7;
00271             B*=.7;
00272         }
00273         glColor3f(R,G,B);
00274         GLfloat color[4]={R,G,B,1};
00275 
00276         glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,color);
00277         glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color);
00278     }
00279 #endif
00280 }
00281 
00282 // from gCycleMovement.cpp
00283 extern void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness );
00284 extern REAL sg_brakeCycle;
00285 extern REAL sg_cycleBrakeDeplete;
00286 
00287 // in release mode, default values should always be used. in debug mode, we want to experiment :)
00288 #ifdef DEBUG
00289 #ifndef DEDICATED
00290 #define DEBUGCHATBOT
00291 #endif
00292 #endif
00293 
00294 #ifdef DEBUGCHATBOT
00295 typedef tSettingItem<REAL> gChatBotSetting;
00296 typedef tSettingItem<bool> gChatBotSwitch;
00297 #else
00298 typedef nSettingItem<REAL> gChatBotSetting;
00299 typedef nSettingItem<bool> gChatBotSwitch;
00300 #endif
00301 
00302 static bool sg_chatBotAlwaysActive = false;
00303 static gChatBotSwitch sg_chatBotAlwaysActiveConf( "CHATBOT_ALWAYS_ACTIVE", sg_chatBotAlwaysActive );
00304 
00305 static REAL sg_chatBotNewWallBlindness = .3;
00306 static gChatBotSetting sg_chatBotNewWallBlindnessConf( "CHATBOT_NEW_WALL_BLINDNESS",
00307         sg_chatBotNewWallBlindness );
00308 
00309 static REAL sg_chatBotMinTimestep = .3;
00310 static gChatBotSetting sg_chatBotMinTimestepConf( "CHATBOT_MIN_TIMESTEP",
00311         sg_chatBotMinTimestep );
00312 
00313 static REAL sg_chatBotDelay = .5;
00314 static gChatBotSetting sg_chatBotDelayConf( "CHATBOT_DELAY",
00315         sg_chatBotDelay );
00316 
00317 static REAL sg_chatBotRange = 1;
00318 static gChatBotSetting sg_chatBotRangeConf( "CHATBOT_RANGE",
00319         sg_chatBotRange );
00320 
00321 static REAL sg_chatBotDecay = .02;
00322 static gChatBotSetting sg_chatBotDecayConf( "CHATBOT_DECAY",
00323         sg_chatBotDecay );
00324 
00325 class gCycleChatBot
00326 {
00327     gCycleChatBot();
00328 public:
00329 class Sensor: public gSensor
00330     {
00331     public:
00332         Sensor(gCycle *o,const eCoord &start,const eCoord &d)
00333                 : gSensor(o,start,d)
00334                 , hitOwner_( 0 )
00335                 , hitTime_ ( 0 )
00336                 , hitDistance_( o->MaxWallsLength() )
00337                 , lrSuggestion_( 0 )
00338                 , windingNumber_( 0 )
00339         {
00340             if ( hitDistance_ <= 0 )
00341                 hitDistance_ = o->GetDistance();
00342         }
00343 
00344         /*
00345         // do detection and additional stuff
00346         void detect( REAL range )
00347         {
00348             gSensor::detect( range );
00349         }
00350         */
00351 
00352         virtual void PassEdge(const eWall *ww,REAL time,REAL a,int r)
00353         {
00354             try{
00355                 gSensor::PassEdge(ww,time,a,r);
00356             }
00357             catch( eSensorFinished & e )
00358             {
00359                 if ( DoExtraDetectionStuff() )
00360                     throw;
00361             }
00362         }
00363 
00364         bool DoExtraDetectionStuff()
00365         {
00366             // move towards the beginning of a wall
00367             lrSuggestion_ = -lr;
00368 
00369             switch ( type )
00370             {
00371             case gSENSOR_NONE:
00372             case gSENSOR_RIM:
00373                 lrSuggestion_ = 0;
00374                 return true;
00375             default:
00376                 // unless it is an enemy, follow his wall instead (uncomment for a nasty cowardy campbot)
00377                 // lrSuggestion *= -1;
00378             case gSENSOR_SELF:
00379                 {
00380                     // determine whether we're hitting the front or back half of his wall
00381                     if ( !ehit )
00382                         return true;
00383                     eWall * wall = ehit->GetWall();
00384                     if ( !wall )
00385                         return true;
00386                     gPlayerWall * playerWall = dynamic_cast< gPlayerWall * >( wall );
00387                     if ( !playerWall )
00388                         return true;
00389                     hitOwner_ = playerWall->Cycle();
00390                     if ( !hitOwner_ )
00391                         return true;
00392 
00393                     // gCycleChatBot & enemyChatBot = Get( hitOwner_ );
00394 
00395                     REAL wallAlpha = playerWall->Edge()->Ratio( before_hit );
00396                     // that's an unreliable source
00397                     if ( wallAlpha < 0 )
00398                         wallAlpha = 0;
00399                     if ( wallAlpha > 1 )
00400                         wallAlpha = 1;
00401                     hitDistance_   = hitOwner_->GetDistance() - playerWall->Pos( wallAlpha );
00402                     hitTime_       = playerWall->Time( wallAlpha );
00403                     windingNumber_ = playerWall->WindingNumber();
00404 
00405                     // don't see new walls
00406                     if ( hitTime_ > hitOwner_->LastTime() - sg_chatBotNewWallBlindness && hitOwner_ != owned )
00407                     {
00408                         ehit = false;
00409                         hit = 1E+40;
00410                         return false;
00411                     }
00412 
00413                     // REAL cycleDistance = hitOwner_->GetDistance();
00414 
00415                     // REAL wallStart = 0;
00416 
00417                     /*
00418                     if ( gCycle::WallsLength() > 0 )
00419                     {
00420                         wallStart = cyclePos - playerWall->Cycle()->ThisWallsLength();
00421                         if ( wallStart < 0 )
00422                             wallStart = 0;
00423                     }
00424                     */
00425                 }
00426             }
00427 
00428             return true;
00429         }
00430 
00431         // check how far the hit wall extends straight into the given direction
00432         REAL HitWallExtends( eCoord const & dir, eCoord const & origin )
00433         {
00434             if ( !ehit || !ehit->Other() )
00435             {
00436                 return 1E+30;
00437             }
00438 
00439             REAL ret = -1E+30;
00440             eCoord ends[2] = { *ehit->Point(), *ehit->Other()->Point() };
00441             for ( int i = 1; i>=0; --i )
00442             {
00443                 REAL newRet = eCoord::F( dir, ends[i]-origin );
00444                 if ( newRet > ret )
00445                     ret = newRet;
00446             }
00447 
00448             return ret;
00449         }
00450 
00451         gCycle * hitOwner_;     // the owner of the hit wall
00452         REAL     hitTime_;      // the time the hit wall was built at
00453         REAL     hitDistance_;  // the distance of the wall to the cycle that built it
00454         short    lrSuggestion_; // sensor's oppinon on whether moving to the left or right of the hit wall is recommended (-1 for left, +1 for right)
00455         int      windingNumber_; // the number of turns (with sign) the cycle has taken
00456     };
00457 
00458     gCycleChatBot( gCycle * owner )
00459             : nextChatAI_( 0 )
00460             , timeOnChatAI_( 0 )
00461             , lastTurn_( 0 )
00462             , nextTurn_ ( 0 )
00463             , turnedRecently_ ( 0 )
00464             , owner_ ( owner )
00465     {
00466     }
00467 
00468     // describes walls we like. We like enemy walls. We like to go between them.
00469     class WallHug
00470     {
00471     public:
00472         gCycle const * owner_;  // the cycle the walls we like belong to
00473         REAL lastTimeSeen_;    // the last time we saw such a wall
00474 
00475         WallHug()
00476                 : owner_ ( NULL )
00477                 , lastTimeSeen_ ( 0 )
00478         {
00479         }
00480     };
00481 
00482     // promote seen walls to possible wallhug replacements
00483     void FindHugReplacement( Sensor const & sensor )
00484     {
00485         gCycle const * owner = sensor.hitOwner_;
00486         if (!owner)
00487             return;
00488 
00489         // store as possible replacement
00490         if ( !hugReplacement_.owner_ && sensor.type != gSENSOR_SELF &&
00491                 owner != hugLeft_.owner_ &&
00492                 owner != hugRight_.owner_ )
00493         {
00494             hugReplacement_.owner_ = sensor.hitOwner_;
00495             hugReplacement_.lastTimeSeen_ = nextChatAI_;
00496         }
00497 
00498         // update timestamps
00499         if ( owner == hugLeft_.owner_ )
00500             hugLeft_.lastTimeSeen_ = nextChatAI_;
00501         if ( owner == hugRight_.owner_ )
00502             hugRight_.lastTimeSeen_ = nextChatAI_;
00503     }
00504 
00505     // determines the distance between two sensors; the size should give the likelyhood
00506     // to survive if you pass through a gap between the two selected walls
00507     REAL Distance( Sensor const & a, Sensor const & b )
00508     {
00509         // make sure a is left from b
00510         if ( a.Direction() * b.Direction() < 0 )
00511             return Distance( b, a );
00512 
00513         bool self = a.type == gSENSOR_SELF || b.type == gSENSOR_SELF;
00514         bool rim  = a.type == gSENSOR_RIM || b.type == gSENSOR_RIM;
00515 
00516         // avoid. own. walls.
00517         REAL selfHatred = 1;
00518         if ( a.type == gSENSOR_SELF )
00519         {
00520             selfHatred *= .5;
00521             if ( a.lr > 0 )
00522             {
00523                 selfHatred *= .5;
00524                 if ( b.type == gSENSOR_RIM )
00525                     selfHatred *= .25;
00526             }
00527         }
00528         if ( b.type == gSENSOR_SELF )
00529         {
00530             selfHatred *= .5;
00531             if ( b.lr < 0 )
00532             {
00533                 selfHatred *= .5;
00534                 if ( a.type == gSENSOR_RIM )
00535                     selfHatred *= .25;
00536             }
00537         }
00538 
00539         // some big distance to return if we don't know anything better
00540         REAL bigDistance = owner_->MaxWallsLength();
00541         if ( bigDistance <= 0 )
00542             bigDistance = owner_->GetDistance();
00543 
00544         if ( a.hitOwner_ != b.hitOwner_ )
00545         {
00546             // different owners? Great, there has to be a way through!
00547             REAL ret =
00548                 a.hitDistance_ + b.hitDistance_;
00549 
00550             if ( rim )
00551             {
00552                 ret = bigDistance * .001 + ret * .01 + ( a.before_hit - b.before_hit).Norm();
00553 
00554                 // we love going between the rim and enemies
00555                 if ( !self )
00556                     ret = bigDistance * 2;
00557             }
00558 
00559             // minimal factor should be 1, this path should never return something smaller than the
00560             // paths where only one cycle's walls are hit
00561             ret *= 16;
00562 
00563             // or empty space
00564             if ( a.type == gSENSOR_NONE || b.type == gSENSOR_NONE )
00565                 ret *= 2;
00566 
00567             return ret * selfHatred;
00568         }
00569         else if ( rim )
00570         {
00571             // at least one rim wall? Take the distance between the hit positions.
00572             return ( a.before_hit - b.before_hit).Norm() * selfHatred;
00573         }
00574         else if ( a.type == gSENSOR_NONE && b.type == gSENSOR_NONE )
00575         {
00576             // empty space! Woo!
00577             return owner_->GetDistance() * 256;
00578         }
00579         else if ( a.lr != b.lr )
00580         {
00581             // different directions? Also great!
00582             return ( fabsf( a.hitDistance_ - b.hitDistance_ ) + .25 * bigDistance ) * selfHatred;
00583         }
00584         /*
00585         else if ( - 2 * a.lr * (a.windingNumber_ - b.windingNumber_ ) > owner_->Grid()->WindingNumber() )
00586         {
00587             // this looks like a way out to me
00588             return fabsf( a.hitDistance_ - b.hitDistance_ ) * 10 * selfHatred;
00589         }
00590         */
00591         else
00592         {
00593             // well, the longer the wall segment between the two points, the better.
00594             return fabsf( a.hitDistance_ - b.hitDistance_ ) * selfHatred;
00595         }
00596 
00597         // default: hit distance
00598         return ( a.before_hit - b.before_hit).Norm() * selfHatred;
00599     }
00600 
00601     static gCycleChatBot & Get( gCycle * cycle )
00602     {
00603         tASSERT( cycle );
00604 
00605         // create
00606         if ( &(*cycle->chatBot_) == 0 )
00607             cycle->chatBot_ = std::auto_ptr< gCycleChatBot >( new gCycleChatBot( cycle ) );
00608 
00609         return *cycle->chatBot_;
00610     }
00611 
00612     bool CanMakeTurn( uActionPlayer * action )
00613     {
00614         return owner_->CanMakeTurn( ( action == &gCycle::se_turnRight ) ? 1 : -1 );
00615     }
00616 
00617     // does the main thinking
00618     void Activate( REAL currentTime )
00619     {
00620         // is it already time for activation?
00621         if ( currentTime < nextChatAI_ )
00622             return;
00623 
00624         REAL lookahead = sg_chatBotRange;  // seconds to plan ahead
00625         REAL minstep   = sg_chatBotMinTimestep; // minimum timestep between thoughts in seconds
00626         REAL maxstep   = sg_chatBotMinTimestep * 4 * ( 1 + .1 * tReproducibleRandomizer::GetInstance().Get() );  // maximum timestep between thoughts in seconds
00627 
00628         // chat AI wasn't active yet, so don't start immediately
00629         if ( nextChatAI_ <= EPS )
00630         {
00631             nextChatAI_ = sg_chatBotDelay + currentTime;
00632             return;
00633         }
00634 
00635         timeOnChatAI_ += currentTime - nextChatAI_;
00636 
00637         // cylce data
00638         REAL speed = owner_->Speed();
00639         eCoord dir = owner_->Direction();
00640         eCoord pos = owner_->Position();
00641 
00642         // make chat AI worse over time
00643         if ( sn_GetNetState() != nSTANDALONE )
00644         {
00645             REAL qualityFactor = ( timeOnChatAI_ * sg_chatBotDecay );
00646             if ( qualityFactor > 1 )
00647             {
00648                 minstep *= qualityFactor;
00649                 // maxstep *= qualityFactor;
00650             }
00651         }
00652 
00653         REAL range= speed * lookahead;
00654         eCoord scanDir = dir * range;
00655 
00656         REAL frontFactor = .5;
00657 
00658         Sensor front(owner_,pos,scanDir);
00659         front.detect(frontFactor);
00660         owner_->enemyInfluence.AddSensor( front, sg_enemyChatbotTimePenalty, owner_ );
00661 
00662         REAL minMoveOn = 0, maxMoveOn = 0, moveOn = 0;
00663 
00664         // get extra time we get through rubber usage
00665         REAL rubberGranted, rubberEffectiveness;
00666         sg_RubberValues( owner_->player, speed, rubberGranted, rubberEffectiveness );
00667         REAL rubberTime = ( rubberGranted - owner_->GetRubber() )*rubberEffectiveness/speed;
00668         REAL rubberRatio = owner_->GetRubber()/rubberGranted;
00669 
00670         if ( front.ehit )
00671         {
00672             turnedRecently_ = false;
00673 
00674             // these checks can hit our last wall and fail. Temporarily set it to NULL.
00675             tJUST_CONTROLLED_PTR< gNetPlayerWall > lastWall = owner_->lastWall;
00676             owner_->lastWall = NULL;
00677 
00678             REAL narrowFront = 1;
00679 
00680             // cast four diagonal rays
00681             Sensor forwardLeft ( owner_, pos, scanDir.Turn(+1,+1 ) );
00682             Sensor backwardLeft( owner_, pos, scanDir.Turn(-1,+narrowFront) );
00683             forwardLeft.detect(1);
00684             backwardLeft.detect(1);
00685             Sensor forwardRight ( owner_, pos, scanDir.Turn(+1,-1 ) );
00686             Sensor backwardRight( owner_, pos, scanDir.Turn(-1,-narrowFront) );
00687             forwardRight.detect(1);
00688             backwardRight.detect(1);
00689 
00690             // do we have a hug replacement candiate? If so, take it.
00691             if ( hugReplacement_.owner_ && !hugLeft_.owner_ && !hugRight_.owner_ )
00692             {
00693                 // first time hugging? let the status quo decide.
00694                 int lr = 0;
00695                 if ( backwardLeft.hitOwner_ == hugReplacement_.owner_ )
00696                     lr--;
00697                 if ( forwardLeft.hitOwner_ == hugReplacement_.owner_ )
00698                     lr--;
00699                 if ( backwardRight.hitOwner_ == hugReplacement_.owner_ )
00700                     lr++;
00701                 if ( forwardRight.hitOwner_ == hugReplacement_.owner_ )
00702                     lr++;
00703 
00704                 if ( lr > 0 )
00705                     hugRight_ = hugReplacement_;
00706                 if ( lr < 0 )
00707                     hugLeft_ = hugReplacement_;
00708 
00709                 hugReplacement_.owner_ = 0;
00710             }
00711 
00712             if ( hugReplacement_.owner_ )
00713             {
00714                 if( hugLeft_.lastTimeSeen_ < hugRight_.lastTimeSeen_ )
00715                 {
00716                     if ( hugReplacement_.lastTimeSeen_ > hugLeft_.lastTimeSeen_ )
00717                         hugLeft_ = hugReplacement_;
00718                 }
00719                 else
00720                 {
00721                     if ( hugReplacement_.lastTimeSeen_ > hugRight_.lastTimeSeen_ )
00722                         hugRight_ = hugReplacement_;
00723                 }
00724                 hugReplacement_.owner_ = 0;
00725             }
00726 
00727             FindHugReplacement( front );
00728             FindHugReplacement( forwardLeft );
00729             FindHugReplacement( forwardRight );
00730             FindHugReplacement( backwardLeft );
00731             FindHugReplacement( backwardRight );
00732 
00733             // determine survival chances in the four directions
00734             REAL frontOpen = Distance ( forwardLeft, forwardRight );
00735             REAL leftOpen  = Distance ( forwardLeft, backwardLeft );
00736             REAL rightOpen = Distance ( forwardRight, backwardRight );
00737             REAL rearOpen = Distance ( backwardLeft, backwardRight );
00738 
00739             Sensor self( owner_, pos, scanDir.Turn(-1, 0) );
00740             // fake entries
00741             self.before_hit = pos;
00742             self.windingNumber_ = owner_->windingNumber_;
00743             self.type = gSENSOR_SELF;
00744             self.hitDistance_ = 0;
00745             self.hitOwner_ = owner_;
00746             self.hitTime_ = currentTime;
00747             self.lr = -1;
00748             REAL rearLeftOpen = Distance( backwardLeft, self );
00749             self.lr = 1;
00750             REAL rearRightOpen = Distance( backwardRight, self );
00751 
00752             /*
00753             // override: don't camp (too much)
00754             if ( forwardRight.type == gSENSOR_SELF &&
00755                     forwardLeft.type == gSENSOR_SELF &&
00756                     backwardRight.type == gSENSOR_SELF &&
00757                     backwardLeft.type == gSENSOR_SELF &&
00758                     front.type == gSENSOR_SELF &&
00759                     forwardRight.lr == front.lr &&
00760                     forwardLeft.lr == front.lr &&
00761                     backwardRight.lr == front.lr &&
00762                     backwardLeft.lr == front.lr &&
00763                     frontOpen + leftOpen + rightOpen < owner_->GetDistance() * .5 )
00764             {
00765                 turnedRecently_ = true;
00766                 if ( front.lr > 0 )
00767                 {
00768                     if ( leftOpen > minstep * speed )
00769                         // force a turn to the left
00770                         rightOpen = 0;
00771                     else if ( front.hit * range < 2 * minstep )
00772                         // force a preliminary turn to the right that will allow us to reverse
00773                         frontOpen = 0;
00774                 }
00775                 else
00776                 {
00777                     if ( rightOpen > minstep * speed )
00778                         // force a turn to the right
00779                         leftOpen = 0;
00780                     else if ( front.hit * range < 2 * minstep )
00781                         // force a preliminary turn to the left that will allow us to reverse
00782                         frontOpen = 0;
00783                 }
00784             }
00785             */
00786 
00787             // override rim hugging
00788             if ( forwardRight.type == gSENSOR_SELF &&
00789                     forwardLeft.type == gSENSOR_RIM &&
00790                     backwardRight.type == gSENSOR_SELF &&
00791                     backwardLeft.type == gSENSOR_RIM &&
00792                     // backwardLeft.hit < .1 &&
00793                     forwardRight.lr == -1 &&
00794                     backwardRight.lr == -1 )
00795             {
00796                 turnedRecently_ = true;
00797                 if ( rightOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) )
00798                 {
00799                     owner_->Act( &gCycle::se_turnRight, 1 );
00800                     owner_->Act( &gCycle::se_turnRight, 1 );
00801                 }
00802                 else
00803                 {
00804                     owner_->Act( &gCycle::se_turnLeft, 1 );
00805                     owner_->Act( &gCycle::se_turnLeft, 1 );
00806                 }
00807             }
00808 
00809             if ( forwardLeft.type == gSENSOR_SELF &&
00810                     forwardRight.type == gSENSOR_RIM &&
00811                     backwardLeft.type == gSENSOR_SELF &&
00812                     backwardRight.type == gSENSOR_RIM &&
00813                     // backwardRight.hit < .1 &&
00814                     forwardLeft.lr == 1 &&
00815                     backwardLeft.lr == 1 )
00816             {
00817                 turnedRecently_ = true;
00818                 if ( leftOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) )
00819                 {
00820                     owner_->Act( &gCycle::se_turnLeft, 1 );
00821                     owner_->Act( &gCycle::se_turnLeft, 1 );
00822                 }
00823                 else
00824                 {
00825                     owner_->Act( &gCycle::se_turnRight, 1 );
00826                     owner_->Act( &gCycle::se_turnRight, 1 );
00827                 }
00828             }
00829 
00830             // get the best turn direction
00831             uActionPlayer * bestAction = ( leftOpen > rightOpen ) ? &gCycle::se_turnLeft : &gCycle::se_turnRight;
00832             int             bestDir      = ( leftOpen > rightOpen ) ? 1 : -1;
00833             REAL            bestOpen     = ( leftOpen > rightOpen ) ? leftOpen : rightOpen;
00834             Sensor &        bestForward  = ( leftOpen > rightOpen ) ? forwardLeft : forwardRight;
00835             Sensor &        bestBackward = ( leftOpen > rightOpen ) ? backwardLeft : backwardRight;
00836 
00837             Sensor direct ( owner_, pos, scanDir.Turn( 0, bestDir) );
00838             direct.detect( 1 );
00839 
00840             // restore last wall
00841             owner_->lastWall = lastWall;
00842 
00843             // only turn if the hole has a shape that allows better entry after we do a zig-zag, or if we're past the good turning point
00844             // see how the survival chance is distributed between forward and backward half
00845             REAL forwardHalf  = Distance ( direct, bestForward );
00846             REAL backwardHalf = Distance ( direct, bestBackward );
00847 
00848             REAL forwardOverhang  = bestForward.HitWallExtends( bestForward.Direction(), pos );
00849             REAL backwardOverhang  = bestBackward.HitWallExtends( bestForward.Direction(), pos );
00850 
00851             // we have to move forward this much before we can hope to turn
00852             minMoveOn = bestBackward.HitWallExtends( dir, pos );
00853 
00854             // maybe the direct to the side sensor is better?
00855             REAL minMoveOnOther = direct.HitWallExtends( dir, pos );
00856 
00857             // determine how far we can drive on
00858             maxMoveOn      = bestForward.HitWallExtends( dir, pos );
00859             REAL maxMoveOnOther = front.HitWallExtends( dir, pos );
00860             if ( maxMoveOn > maxMoveOnOther )
00861                 maxMoveOn = maxMoveOnOther;
00862 
00863             if ( maxMoveOn > minMoveOnOther && forwardHalf > backwardHalf && direct.hitOwner_ == bestBackward.hitOwner_ )
00864             {
00865                 backwardOverhang  = direct.HitWallExtends( bestForward.Direction(), pos );
00866                 minMoveOn = minMoveOnOther;
00867             }
00868 
00869             // best place to turn
00870             moveOn = .5 * ( minMoveOn * ( 1 + rubberRatio ) + maxMoveOn * ( 1 - rubberRatio ) );
00871 
00872             // hit the brakes before you hit anything and if it's worth it
00873             bool brake = sg_brakeCycle > 0 &&
00874                          front.hit * lookahead * sg_cycleBrakeDeplete < owner_->GetBrakingReservoir() &&
00875                          sg_brakeCycle * front.hit * lookahead < 2 * speed * owner_->GetBrakingReservoir() &&
00876                          ( maxMoveOn - minMoveOn ) > 0 &&
00877                          owner_->GetBrakingReservoir() * ( maxMoveOn - minMoveOn ) < speed * owner_->GetTurnDelay();
00878             if ( frontOpen < bestOpen &&
00879                     ( forwardOverhang <= backwardOverhang || ( minMoveOn < 0 && moveOn < minstep * speed ) ) )
00880             {
00881                 // FindHugReplacement( direct );
00882                 // REAL expectedBackwardHalf = ( direct.before_hit - bestBackward.before_hit ).Norm();
00883 
00884                 // if ( ( ( forwardHalf + backwardHalf > bestOpen * 2 || backwardHalf > frontOpen * 10 || backwardHalf > expectedBackwardHalf * 1.01 ) && frontOpen < bestOpen ) ||
00885                 // rubberTime * .5 + minspace * lookahead < minstep )
00886                 //                {
00887                 turnedRecently_ = true;
00888 
00889                 minMoveOn = maxMoveOn = moveOn = 0;
00890 
00891                 /*
00892                 if (
00893                     ( ( ( bestBackward.type == gSENSOR_ENEMY || bestBackward.type == gSENSOR_TEAMMATE ) && bestBackward.hitDistance_ < bestBackward.hit * lookahead * speed ) ||
00894                       direct.hit * lookahead + rubberTime < owner_->GetTurnDelay() ) &&
00895                     ( bestBackward.hit * lookahead + rubberTime < owner_->GetTurnDelay() ||
00896                       bestForward.hit * lookahead + rubberTime < owner_->GetTurnDelay() )
00897                 )
00898                 {
00899                     // override: stupid turn into certain death, turn it around if that makes it less stupid
00900                     uActionPlayer * newBestAction = ( leftOpen > rightOpen ) ? &gCycle::se_turnLeft : &gCycle::se_turnRight;
00901                     Sensor newDirect ( owner_, pos, scanDir.Turn( 0, -bestDir) );
00902                     newDirect.detect( 1 );
00903                     if ( newDirect.hit > direct.hit ||
00904                             newDirect.hit * lookahead + rubberTime > owner_->GetTurnDelay() )
00905                         owner_->Act( newBestAction, 1 );
00906                 }
00907                 else
00908                 */
00909                 {
00910                     if ( !CanMakeTurn( bestAction ) )
00911                     {
00912                         nextChatAI_ = currentTime;
00913                         return;
00914                     }
00915 
00916                     owner_->Act( bestAction, 1 );
00917                 }
00918 
00919                 brake = false;
00920             }
00921             else
00922             {
00923                 // the best
00924                 REAL bestSoFar = frontOpen > bestOpen ? frontOpen : bestOpen;
00925                 bestSoFar *= ( 10 * ( 1 - rubberRatio ) + 1 );
00926 
00927                 if ( rearOpen > bestSoFar && ( rearLeftOpen > bestSoFar || rearRightOpen > bestSoFar ) )
00928                 {
00929                     brake = false;
00930                     turnedRecently_ = true;
00931 
00932                     bool goLeft = rearLeftOpen > rearRightOpen;
00933 
00934                     // dead end. reverse into the opposite direction of the front wall
00935                     uActionPlayer * bestAction = goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight;
00936                     uActionPlayer * otherAction = !goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight;
00937                     Sensor &        bestForward  = goLeft ? forwardLeft : forwardRight;
00938                     Sensor &        bestBackward  = goLeft ? backwardLeft : backwardRight;
00939                     Sensor &        otherForward  = !goLeft ? forwardLeft : forwardRight;
00940                     Sensor &        otherBackward  = !goLeft ? backwardLeft : backwardRight;
00941 
00942                     // space in the two directions available for turns
00943                     REAL bestHit = bestForward.hit > bestBackward.hit ? bestBackward.hit : bestForward.hit;
00944                     REAL otherHit = otherForward.hit > otherBackward.hit ? otherBackward.hit : otherForward.hit;
00945 
00946                     bool wait = false;
00947 
00948                     if ( !CanMakeTurn( bestAction ) )
00949                     {
00950                         nextChatAI_ = currentTime;
00951                         return;
00952                     }
00953 
00954                     // well, after a short turn to the right if space is tight
00955                     if ( bestHit * lookahead < owner_->GetTurnDelay() + rubberTime )
00956                     {
00957                         if ( otherHit < bestForward.hit * 2 && front.hit * lookahead > owner_->GetTurnDelay() * 2 )
00958                         {
00959                             // wait a bit, perhaps there will be a better spot
00960                             wait = true;
00961                         }
00962                         else
00963                         {
00964                             if ( !CanMakeTurn( otherAction ) )
00965                             {
00966                                 nextChatAI_ = currentTime;
00967                                 return;
00968                             }
00969 
00970                             owner_->Act( otherAction, 1 );
00971 
00972                             // there needs to be space ahead to finish the maneuver correctly
00973                             if ( maxMoveOn < speed * owner_->GetTurnDelay() )
00974                             {
00975                                 // there isn't. oh well, turn into the wrong direction completely, see if I care
00976                                 owner_->Act( otherAction, 1 );
00977                                 wait = true;
00978                             }
00979                         }
00980                     }
00981 
00982                     if ( !wait )
00983                     {
00984                         owner_->Act( bestAction, 1 );
00985                         owner_->Act( bestAction, 1 );
00986                     }
00987 
00988                     minMoveOn = maxMoveOn = moveOn = 0;
00989                 }
00990             }
00991 
00992             // execute brake command
00993             owner_->Act( &gCycle::s_brake, brake ? 1 : -1 );
00994 
00995             // swap hugged walls if we're in fact grinding them the other way round
00996             if ( hugLeft_.owner_ == backwardRight.hitOwner_ ||
00997                     hugRight_.owner_ == backwardLeft.hitOwner_ )
00998             {
00999                 WallHug swap = hugRight_;
01000                 hugRight_ = hugLeft_;
01001                 hugLeft_ = swap;
01002             }
01003         }
01004 
01005         // REAL mintime = minspace * lookahead;
01006 
01007         // try again soon
01008         //        REAL newmintime = mintime * .5 - minstep * .2 * tReproducibleRandomizer::GetInstance().Get();
01009 
01010         // clamp
01011         // if ( newmintime < minstep )
01012         // newmintime = minstep;
01013 
01014         // add slack, acceleration and rubber
01015         // if ( owner_->acceleration > 0 )
01016         // mintime -= owner_->acceleration * mintime * mintime / speed;
01017         // mintime -= .1 * minstep - rubberTime * .3;
01018 
01019         // if the next step gets us too close to the wall to do anything useful,
01020         // bring us really close right away.
01021         // if ( mintime - newmintime > minstep )
01022         // {
01023         // mintime = newmintime;
01024         // }
01025 
01026         REAL space = moveOn;
01027         REAL minTime = space/speed;
01028 
01029         if ( turnedRecently_ )
01030             minTime = owner_->GetTurnDelay();
01031 
01032         if ( minTime < minstep )
01033             minTime = minstep;
01034         if ( minTime > maxstep + minstep * 1.5 )
01035         {
01036             minTime = maxstep;
01037         }
01038         // minTime = 0;
01039 
01040         nextChatAI_ = currentTime + minTime;
01041         timeOnChatAI_ += minTime;
01042     }
01043 
01044     REAL nextChatAI_;        
01045 private:
01046     REAL timeOnChatAI_;      
01047     short lastTurn_;         
01048     REAL nextTurn_;          
01049     bool turnedRecently_;    
01050     gCycle * owner_;         
01051 
01052     WallHug hugLeft_;              
01053     WallHug hugRight_;             
01054     WallHug hugReplacement_;       
01055 };
01056 
01057 //  *****************************************************************
01058 
01059 static void sg_ArchiveCoord( eCoord & coord, int level )
01060 {
01061     static char const * section = "_COORD";
01062     tRecorderSync< eCoord >::Archive( section, level, coord );
01063 }
01064 
01065 static void sg_ArchiveReal( REAL & real, int level )
01066 {
01067     static char const * section = "_REAL";
01068     tRecorderSync< REAL >::Archive( section, level, real );
01069 }
01070 
01071 //  *****************************************************************
01072 
01073 
01074 
01075 //  *****************************************************************
01076 
01077 
01078 // take pos,dir and time from a cycle
01079 gDestination::gDestination(const gCycleMovement &c)
01080         :chatting(false)
01081         ,turns(0)
01082         ,hasBeenUsed(false)
01083         ,messageID(1)
01084         ,missable(true)
01085         ,next(NULL)
01086         ,list(NULL)
01087 {
01088     CopyFrom( c );
01089 }
01090 
01091 gDestination::gDestination(const gCycle &c)
01092         :chatting(false)
01093         ,turns(0)
01094         ,hasBeenUsed(false)
01095         ,messageID(1)
01096         ,missable(true)
01097         ,next(NULL)
01098         ,list(NULL)
01099 {
01100     CopyFrom( c );
01101 }
01102 
01103 // or from a message
01104 gDestination::gDestination(nMessage &m, unsigned short & cycle_id )
01105         :gameTime(0),distance(0),speed(0),
01106         hasBeenUsed(false),
01107         messageID(1),
01108         missable(true),
01109 next(NULL),list(NULL){
01110     m >> position;
01111     m >> direction;
01112     m >> distance;
01113 
01114     unsigned short flags;
01115     m >> flags;
01116     braking  = flags & 0x01;
01117     chatting = flags & 0x02;
01118 
01119     messageID = m.MessageID();
01120 
01121     turns = 0;
01122 
01123     m.Read( cycle_id );
01124 
01125     if ( !m.End() )
01126         m >> gameTime;
01127     else
01128         gameTime = -1000;
01129 
01130     if ( !m.End() )
01131     {
01132         m.Read( turns );
01133     }
01134 }
01135 
01136 void gDestination::CopyFrom(const gCycleMovement &other)
01137 {
01138     position    = other.Position();
01139     direction   = other.Direction();
01140     gameTime    = other.LastTime();
01141     distance    = other.GetDistance();
01142     speed               = other.Speed();
01143     braking     = other.GetBraking();
01144     turns               = other.GetTurns();
01145 
01146 #ifdef DEBUG
01147     if (!finite(gameTime) || !finite(speed) || !finite(distance))
01148         st_Breakpoint();
01149 #endif
01150     if ( other.Owner() && other.Player() )
01151         chatting = other.Player()->IsChatting();
01152 }
01153 
01154 void gDestination::CopyFrom(const gCycle &other)
01155 {
01156     CopyFrom( static_cast<const gCycleMovement&>(other) );
01157 
01158     // correct distance
01159     distance    += other.correctDistanceSmooth;
01160 }
01161 
01162 // *******************************************************************************************
01163 // *
01164 // *    CompareWith
01165 // *
01166 // *******************************************************************************************
01171 // *******************************************************************************************
01172 
01173 int gDestination::CompareWith( const gDestination & other ) const
01174 {
01175     // compare distances, but use the network message ID ( they are always increasing, the distance may stagnate or in extreme cases run backwards ) as main sort criterion
01176 
01177     // compare message IDs with overflow ( if both are at good values )
01178     if ( messageID > 1 && other.messageID > 1 )
01179     {
01180         short messageIDDifference = messageID - other.messageID;
01181         if ( messageIDDifference < 0 )
01182             return -1;
01183         if ( messageIDDifference > 0 )
01184             return 1;
01185     }
01186 
01187     // compare travelled distance
01188     if ( distance < other.distance )
01189         return -1;
01190     else if ( distance > other.distance )
01191         return 1;
01192 
01193     // no relevant difference found
01194     return 0;
01195 }
01196 
01197 class gFloatCompressor
01198 {
01199 public:
01200     gFloatCompressor( REAL min, REAL max )
01201             : min_( min ), max_( max ){}
01202 
01203     // write compressed float to message
01204     void Write( nMessage& m, REAL value ) const
01205     {
01206         clamp( value, min_, max_ );
01207         unsigned short compressed = static_cast< unsigned short > ( maxShort_ * ( value - min_ )/( max_ - min_ ) );
01208         m.Write( compressed );
01209     }
01210 
01211     REAL Read( nMessage& m ) const
01212     {
01213         unsigned short compressed;
01214         m.Read( compressed );
01215 
01216         return  min_ + compressed * ( max_ - min_ )/maxShort_;
01217     }
01218 private:
01219     REAL min_, max_;  // minimal and maximal expected value
01220 
01221     static const unsigned short maxShort_;
01222 
01223     gFloatCompressor();
01224 };
01225 
01226 const unsigned short gFloatCompressor::maxShort_ = 0xFFFF;
01227 
01228 static gFloatCompressor compressZeroOne( 0, 1 );
01229 
01230 // write all the data into a nMessage
01231 void gDestination::WriteCreate(nMessage &m, unsigned short cycle_id ){
01232     m << position;
01233     m << direction;
01234     m << distance;
01235 
01236     unsigned short flags = 0;
01237     if ( braking )
01238         flags |= 0x01;
01239     if ( chatting )
01240         flags |= 0x02;
01241     m << flags;
01242 
01243     // store message ID for later reference
01244     messageID = m.MessageID();
01245 
01246     m.Write( cycle_id );
01247     m << gameTime;
01248     m.Write( turns );
01249 }
01250 
01251 gDestination *gDestination::RightBefore(gDestination *list, REAL dist){
01252     if (!list || list->distance > dist)
01253         return NULL;
01254 
01255     gDestination *ret=list;
01256     while (ret && ret->next && ret->next->distance < dist)
01257         ret=ret->next;
01258 
01259     return ret;
01260 }
01261 
01262 gDestination *gDestination::RightAfter(gDestination *list, REAL dist){
01263     if (!list)
01264         return NULL;
01265 
01266     gDestination *ret=list;
01267     while (ret && ret->distance < dist)
01268         ret=ret->next;
01269 
01270     return ret;
01271 }
01272 
01273 // insert yourself into a list ordered by gameTime
01274 void gDestination::InsertIntoList(gDestination **l){
01275     if (!l)
01276         return;
01277 
01278     RemoveFromList();
01279 
01280     // let message find its place
01281     while (l && *l && CompareWith( **l ) > 0 )
01282         l = &((*l)->next);
01283 
01284     list=l;
01285 
01286     tASSERT(l);
01287 
01288     next=*l;
01289     *l=this;
01290 }
01291 
01292 
01293 
01294 void gDestination::RemoveFromList(){
01295     /*
01296        if (!list)
01297            return;
01298 
01299        while (list && *list && *list != this)
01300            list = &((*list)->next);
01301 
01302        tASSERT(list);
01303        tASSERT(*list);
01304 
01305        (*list) = next;
01306        next=NULL;
01307        list=NULL;
01308        
01309       */
01310 
01311     // z-man: HUH? I don't understand the code above any more and it seems to be leaky.
01312     // This simple alternative sounds better:
01313 
01314     if(list)
01315         *list = next;
01316     if(next)
01317         next->list = list;
01318 
01319     next = 0;
01320     list = 0;
01321 }
01322 
01323 // *******************************************************************************************
01324 // *
01325 // *    GetGameTime
01326 // *
01327 // *******************************************************************************************
01331 // *******************************************************************************************
01332 
01333 REAL gDestination::GetGameTime( void ) const
01334 {
01335     return this->gameTime;
01336 }
01337 
01338 // *******************************************************************************************
01339 // *
01340 // *    GetGameTime
01341 // *
01342 // *******************************************************************************************
01347 // *******************************************************************************************
01348 
01349 gDestination const & gDestination::GetGameTime( REAL & gameTime ) const
01350 {
01351     gameTime = this->gameTime;
01352     return *this;
01353 }
01354 
01355 // *******************************************************************************************
01356 // *
01357 // *    SetGameTime
01358 // *
01359 // *******************************************************************************************
01364 // *******************************************************************************************
01365 
01366 gDestination & gDestination::SetGameTime( REAL gameTime )
01367 {
01368     this->gameTime = gameTime;
01369     return *this;
01370 }
01371 
01372 // ********************************************************
01373 
01374 static void new_destination_handler(nMessage &m)
01375 {
01376     // read the destination
01377     unsigned short cycle_id;
01378     gDestination *dest=new gDestination(m, cycle_id );
01379 
01380     // and the ID of the cycle the destination is added to
01381     nNetObject *o=nNetObject::ObjectDangerous(cycle_id);
01382     if (o && &o->CreatorDescriptor() == &cycle_init){
01383         if ((sn_GetNetState() == nSERVER) && (m.SenderID() != o->Owner()))
01384         {
01385             Cheater(m.SenderID());
01386         }
01387         else
01388             if(o->Owner() != ::sn_myNetID)
01389             {
01390                 gCycle* c = dynamic_cast<gCycle *>(o);
01391                 if (c)
01392                 {
01393                     if ( c->Player() && !dest->Chatting() )
01394                         c->Player()->Activity();
01395 
01396                     // fill default gametime
01397                     if ( dest->GetGameTime() < -100 )
01398                         dest->SetGameTime( se_GameTime()+c->Lag()*3 );
01399 
01400                     c->AddDestination(dest);
01401                     dest = 0;
01402                 }
01403             }
01404     }
01405 
01406     // delete the destination if it has not been used
01407     // TODO: exception safety!
01408     delete dest;
01409 }
01410 
01411 static nDescriptor destination_descriptor(321,&new_destination_handler,"destinaton");
01412 
01413 static void BroadCastNewDestination(gCycleMovement *c, gDestination *dest){
01414     nMessage *m=new nMessage(destination_descriptor);
01415     dest->WriteCreate(*m, c->ID() );
01416     m->BroadCast();
01417 }
01418 
01419 bool gCycle::IsMe( eGameObject const * other ) const
01420 {
01421     return other == this || other == extrapolator_;
01422 }
01423 
01424 #ifdef DELAYEDTURN_DEBUG
01425 double sg_turnReceivedTime = 0;
01426 #endif
01427 
01428 void gCycle::OnNotifyNewDestination( gDestination* dest )
01429 {
01430 #ifdef DELAYEDTURN_DEBUG
01431     sg_turnReceivedTime = tSysTimeFloat();
01432 #endif
01433 
01434 #ifdef GNUPLOT_DEBUG
01435     if ( sg_gnuplotDebug && Player() )
01436     {
01437         std::ofstream f( Player()->GetUserName() + "_sync", std::ios::app );
01438         f << dest->position.x << " " << dest->position.y << "\n";
01439     }
01440 #endif
01441 
01442     gCycleMovement::OnNotifyNewDestination( dest );
01443 
01444     //  if (sn_GetNetState()==nSERVER || ::sn_myNetID == owner)
01445     if (sn_GetNetState()==nCLIENT && ::sn_myNetID == Owner())
01446         BroadCastNewDestination(this,dest);
01447 
01448     if ( extrapolator_ )
01449     {
01450         // add destination and try to squeeze it into the schedule.
01451         extrapolator_->NotifyNewDestination( dest );
01452     }
01453 
01454     // start a new simulation in any case. The server may simulate the movement a bit differently at the turn.
01455     // resimulate_ = ( sn_GetNetState() == nCLIENT );
01456 
01457     // detect lag slides
01458     if( sn_GetNetState() == nSERVER )
01459     {
01460         // see how far we should be simulated in an ideal world
01461         REAL simTime=se_GameTime() - Lag();
01462 
01463         REAL lag = simTime - dest->gameTime;  // the real lag
01464         REAL lagOffset = simTime - lastTime;  // difference between real lag and practical lag (what we need to compensate)
01465         if ( lag > 0 && sn_GetNetState() == nSERVER )
01466         {
01467             eLag::Report( Owner(), lag );
01468             if ( currentWall && currentWall->Wall() && rubberSpeedFactor >= 1-EPS )
01469             {
01470                 lag -= lagOffset; // switch to practical lag
01471 
01472                 // no compensation? Just quit.
01473                 if ( lag < 0 )
01474                     return;
01475 
01476                 // see how much we can go back
01477                 REAL minDist   = currentWall->Wall()->Pos(0);
01478                 REAL maxGoBack = ( distance - minDist ) * .8;
01479 
01480                 // see how much we should go back
01481                 REAL stepBack = distance - dest->distance;
01482 
01483                 if ( rubberSpeedFactor < 1-EPS )
01484                 {
01485                     // make the correction distance based so we don't loosen a grind
01486                     maxGoBack = stepBack;
01487                 }
01488 
01489                 // clamp so we don't go back too far
01490                 if ( stepBack > maxGoBack )
01491                 {
01492                     stepBack = maxGoBack;
01493                     lag = rubberSpeedFactor*stepBack/verletSpeed_;
01494                 }
01495 
01496                 // ask lag compensation how much we are allowed to go back; switch to real lag
01497                 // for the credit
01498                 lag = eLag::TakeCredit( Owner(), lag + lagOffset ) - lagOffset;
01499 
01500                 // no compensation? Just quit.
01501                 if ( lag < 0 )
01502                     return;
01503 
01504                 // go back in time
01505                 if ( rubberSpeedFactor >= 1-EPS )
01506                 {
01507                     // rubber is inactive, basic timestep is enough
01508                     TimestepCore( lastTime - lag );
01509                 }
01510                 else if ( 0 )
01511                 {
01512                     // rubber is active. Take care!
01513 
01514                     // just extrapolate the movement backwards
01515                     REAL step = lag * verletSpeed_;
01516                     lastTime -= lag;
01517 
01518                     // rubber is a bit tricker, we need the effectiveness
01519                     REAL rubberGranted, rubberEffectiveness;
01520                     sg_RubberValues( player, verletSpeed_, rubberGranted, rubberEffectiveness );
01521                     if ( rubberEffectiveness > 0 )
01522                         rubber -= step/rubberEffectiveness;
01523 
01524                     if ( rubber < 0 )
01525                         rubber = 0;
01526 
01527                     // position and distance are easy
01528                     step *= rubberSpeedFactor;
01529                     distance -= step;
01530                     pos = pos - dirDrive * step;
01531 
01532                     // undo acceleration
01533                     verletSpeed_ -= acceleration * lag;
01534                 }
01535 
01536                 // see if we went back too far (should almost never happen)
01537                 if ( distance < minDist )
01538                 {
01539                     st_Breakpoint();
01540                     TimestepCore( lastTime + ( minDist - distance )/verletSpeed_ );
01541                 }
01542             }
01543         }
01544     }
01545 }
01546 
01547 
01548 // *******************************************************************************************
01549 // *
01550 // *    OnDropTempWall
01551 // *
01552 // *******************************************************************************************
01558 // *******************************************************************************************
01559 
01560 void gCycle::OnDropTempWall( gPlayerWall * wall, eCoord const & position, eCoord const & dir )
01561 {
01562     tASSERT( wall );
01563 
01564     unsigned short idrec = ID();
01565     tRecorderSync< unsigned short >::Archive( "_ON_DROP_WALL", 8, idrec );
01566 
01567     // determine if the grinded wall is current enough
01568     bool wallRight = ( currentWall && ( wall->NetWall() == currentWall || wall->NetWall() == currentWall ) );
01569 
01570     // don't drop if we already dropped a short time ago
01571     if ( wallRight && currentWall->Edge()->Vec().NormSquared() < verletSpeed_ * verletSpeed_ * sg_minDropInterval * sg_minDropInterval )
01572         wallRight = false;
01573 
01574     tRecorderSync< bool >::Archive( "_ON_DROP_WALL_RIGHT", 8, wallRight );
01575 
01576     // drop the current wall if eiter this or the last wall is grinded
01577     // gNetPlayerWall * nw = wall->NetWall();
01578     if ( wallRight )
01579     {
01580         // calculate relative position of grinding in wall; if alpha is positive, it's already too late
01581         REAL alpha = currentWall->Edge()->Edge(0)->Ratio( position );
01582         tRecorderSync< REAL >::Archive( "_ON_DROP_WALL_ALPHA", 8, alpha );
01583         if ( alpha > -.5 )
01584         {
01585             unsigned short idrec = ID();
01586             tRecorderSync< unsigned short >::Archive( "_ON_DROP_WALL_DROP", 8, idrec );
01587 
01588             // just request the drop, Timestep() will execute it later
01589             dropWallRequested_ = true;
01590 
01591             // bend last driving direction to -dir. That way, should the grinder overtake this cycle,
01592             // it will end up on the right side of his wall.
01593             lastDirDrive = -dir;
01594         }
01595     }
01596 }
01597 
01598 // ************************************************************
01599 
01600 
01601 
01602 
01603 // ************************************************************
01604 
01605 
01606 // the time the cycle walls stay up ( negative values: they stay up forever )
01607 void    gCycle::SetWallsStayUpDelay     ( REAL delay )
01608 {
01609     c_pwsud->Set( delay );
01610 }
01611 
01612 // how much rubber usage shortens the walls
01613 static REAL sg_cycleRubberWallShrink = 0;
01614 static nSettingItemWatched<REAL>
01615 sg_cycleRubberWallShrinkConf("CYCLE_RUBBER_WALL_SHRINK",
01616                              sg_cycleRubberWallShrink,
01617                              nConfItemVersionWatcher::Group_Bumpy,
01618                              12);
01619 
01620 // make walls grow with distance traveled
01621 static REAL sg_cycleDistWallShrink = 0;
01622 static nSettingItemWatched<REAL>
01623 sg_cycleDistWallShrinkConf("CYCLE_DIST_WALL_SHRINK",
01624                            sg_cycleDistWallShrink,
01625                            nConfItemVersionWatcher::Group_Bumpy,
01626                            12);
01627 
01628 static REAL sg_cycleDistWallShrinkOffset = 0;
01629 static nSettingItemWatched<REAL>
01630 sg_cycleDistWallShrinkOffsetConf("CYCLE_DIST_WALL_SHRINK_OFFSET",
01631                                  sg_cycleDistWallShrinkOffset,
01632                                  nConfItemVersionWatcher::Group_Bumpy,
01633                                  12);
01634 
01635 // calculates the effect of driving distance to wall length
01636 static REAL sg_CycleWallLengthFromDist( REAL distance )
01637 {
01638     REAL len = gCycle::WallsLength();
01639 
01640     // make base length longer or shorter, depending on the sign of sg_cycleDistWallShrink
01641     REAL d = sg_cycleDistWallShrinkOffset - distance;
01642     if ( d > 0 )
01643         len -= sg_cycleDistWallShrink * d;
01644 
01645     return len;
01646 }
01647 
01648 // the current length of the walls of this cycle
01649 REAL gCycle::ThisWallsLength() const
01650 {
01651     // get distance influence
01652     REAL len = sg_CycleWallLengthFromDist( distance );
01653 
01654     // apply rubber shortening
01655     return len - GetRubber() * sg_cycleRubberWallShrink;
01656 }
01657 
01658 
01659 // the speed the end of the trail currently receeds with
01660 REAL gCycle::WallEndSpeed() const
01661 {
01662     REAL rubberMax, rubberEffectiveness;
01663     sg_RubberValues( player, Speed(), rubberMax, rubberEffectiveness );
01664 
01665     // basic speed from cycle movement
01666     REAL speed = rubberSpeedFactor * Speed();
01667 
01668     // take distance shrinking into account
01669     REAL d = sg_cycleDistWallShrinkOffset - distance;
01670     if ( d > 0 )
01671         speed *= ( 1 - sg_cycleDistWallShrink );
01672 
01673     // speed from rubber usage and shringing
01674     if ( rubberEffectiveness > 0 )
01675         speed += Speed() * ( 1 - rubberSpeedFactor ) * sg_cycleRubberWallShrink / rubberEffectiveness;
01676 
01677     return speed;
01678 }
01679 
01680 // the maximum total length of the walls
01681 REAL gCycle::MaxWallsLength() const
01682 {
01683     REAL len = sg_CycleWallLengthFromDist( distance );
01684 
01685     // exception: if the wall grows faster than it receeds, take the maximum, because the wall will
01686     // grow backwards
01687     if ( sg_cycleDistWallShrink > 1 )
01688     {
01689         len = wallsLength;
01690     }
01691 
01692     // if the wall grows from rubber use, add the maximal growth
01693     if ( sg_cycleRubberWallShrink >= 0 || sg_rubberCycle < 0 )
01694         return len;
01695     else
01696         return len - sg_cycleRubberWallShrink * sg_rubberCycle;
01697 }
01698 
01699 // the maximum total length of the walls
01700 void    gCycle::SetWallsLength                  ( REAL length)
01701 {
01702     c_pwl->Set( length );
01703 }
01704 
01705 // the radius of the holes blewn in by an explosion
01706 void    gCycle::SetExplosionRadius              ( REAL radius)
01707 {
01708     c_per->Set( radius );
01709 }
01710 
01711 //  *****************************************************************
01712 
01713 // copies relevant info from other cylce
01714 void gCycleExtrapolator::CopyFrom( const gCycleMovement& other )
01715 {
01716     // delegate
01717     gCycleMovement::CopyFrom( other );
01718 
01719     // set parent
01720     parent_ = &other;
01721 
01722     // copy distance
01723     trueDistance_ = GetDistance();
01724 }
01725 
01726 // copies relevant info from sync data and everything else from other cycle
01727 void gCycleExtrapolator::CopyFrom( const SyncData& sync, const gCycle& other )
01728 {
01729     // delegate
01730     gCycleMovement::CopyFrom( sync, other );
01731 
01732     //eFace* face1 = currentFace;
01733     MoveSafely( other.lastGoodPosition_, sync.time, sync.time );
01734     //eFace* face2 = currentFace;
01735     MoveSafely( sync.pos, sync.time, sync.time );
01736 
01737 #ifdef DEBUG_X
01738     if ( face1 != face2 || face1 != currentFace )
01739     {
01740         currentFace = face1;
01741         MoveSafely( sync.lastTurn *.01 + sync.pos * .99, sync.time, sync.time );
01742         MoveSafely( sync.pos, sync.time, sync.time );
01743     }
01744 #endif
01745 
01746     // set parent
01747     parent_ = &other;
01748 
01749     // set last turn
01750     lastTurnPos_ = sync.lastTurn;
01751 
01752     // copy distance
01753     trueDistance_ = GetDistance();
01754 }
01755 
01756 gCycleExtrapolator::gCycleExtrapolator(eGrid *grid, const eCoord &pos,const eCoord &dir,ePlayerNetID *p,bool autodelete)
01757         :gCycleMovement( grid, pos, dir, p, autodelete )
01758         ,trueDistance_( 0 )
01759         ,parent_( 0 )
01760 {
01761     // an extrapolator should not be visible as a gameobject from the outside
01762     eFace * currentFaceBack = currentFace;
01763     RemoveFromList();
01764     currentFace = currentFaceBack;
01765     if ( !currentFace )
01766         currentFace = grid->FindSurroundingFace( pos, currentFace );
01767 }
01768 
01769 // gCycleExtrapolator::gCycleExtrapolator(nMessage &m);
01770 gCycleExtrapolator::~gCycleExtrapolator()
01771 {
01772 }
01773 
01774 /*
01775 // returns the current destination
01776 gDestination* gCycleExtrapolator::GetCurrentDestination() const
01777 {
01778     return currentDestination;
01779 
01780     // the code below does not appear to be such a good idea after all...
01781     if ( currentDestination )
01782     {
01783         return currentDestination;
01784     }
01785     else
01786     {
01787         tASSERT( parent_ );
01788 
01789         // return a destination with the current parent position
01790         static gDestination parentPos( *parent_ );
01791         parentPos.missable = false;
01792         parentPos.CopyFrom( *parent_ );
01793         return &parentPos;
01794     }
01795 }
01796 */
01797 
01798 bool gCycleExtrapolator::EdgeIsDangerous(const eWall *ww, REAL time, REAL alpha ) const
01799 {
01800     const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(ww);
01801     if (w)
01802     {
01803         gNetPlayerWall* nw = w->NetWall();
01804 
01805         // get time the wall was built
01806         REAL builtTime = w->Time(alpha);
01807 
01808         // is the wall built in the future ( happens during extrapolation )
01809         if ( builtTime > time )
01810             return false;
01811 
01812         // ignore temporary walls in some cases, they may not be real
01813         if ( nw && nw->Preliminary() && w->Cycle() == parent_ && fabs( dirDrive * w->Vec() ) < EPS )
01814             return false;
01815 
01816         // ignore recent walls of parent cycle
01817         gCycle *otherPlayer=w->Cycle();
01818         if (otherPlayer==parent_ &&
01819                 ( time < builtTime + 2 * GetTurnDelay() || GetDistance() < w->Pos( alpha ) + .01 * sg_delayCycle*Speed()/SpeedMultiplier()  )
01820            )
01821             return false;
01822     }
01823 
01824     // delegate
01825     return bool(parent_) && parent_->EdgeIsDangerous( ww, time, alpha ) && gCycleMovement::EdgeIsDangerous( ww, time, alpha );
01826 }
01827 
01828 void gCycleExtrapolator::PassEdge(const eWall *ww,REAL time,REAL a,int){
01829     {
01830         if (!EdgeIsDangerous(ww,time,a) || !Alive() )
01831         {
01832             return;
01833         }
01834         else
01835         {
01836             eCoord collPos = ww->Point( a );
01837             throw gCycleDeath( collPos );
01838         }
01839     }
01840 }
01841 
01842 bool gCycleExtrapolator::TimestepCore(REAL currentTime, bool calculateAcceleration)
01843 {
01844     // determine a suitable next destination
01845     gDestination destDefault( *parent_ ), *dest=&destDefault;
01846     if ( GetCurrentDestination() )
01847     {
01848         dest = GetCurrentDestination();
01849     }
01850 
01851     // correct distance
01852     // distance = dest->distance - DistanceToDestination( *dest );
01853     // REAL distanceBefore = GetDistance();
01854     tASSERT(finite(distance));
01855 
01856     // delegate
01857     bool ret = false;
01858     try{
01859         ret = gCycleMovement::TimestepCore( currentTime, calculateAcceleration );
01860     }
01861     catch( gCycleDeath & )
01862     {
01863         return false;
01864     }
01865 
01866     // update true distance
01867     // trueDistance_ += GetDistance() - distanceBefore;
01868     trueDistance_ = distance;
01869 
01870     return ret;
01871 }
01872 
01873 /*
01874 bool gCycleExtrapolator::DoTurn( int dir )
01875 {
01876     // delegate
01877     return gCycleMovement::DoTurn( dir );
01878 }
01879 */
01880 
01881 nDescriptor &gCycleExtrapolator::CreatorDescriptor() const{
01882     // should never be called
01883     tASSERT( 0 );
01884     return cycle_init;
01885 }
01886 
01887 //  *****************************************************************
01888 
01889 const eTempEdge* gCycle::Edge(){
01890     if (currentWall)
01891         return currentWall->Edge();
01892     else
01893         return NULL;
01894 }
01895 
01896 const gPlayerWall* gCycle::CurrentWall(){
01897     if (currentWall)
01898         return currentWall->Wall();
01899     else
01900         return NULL;
01901 }
01902 
01903 /*
01904 const gPlayerWall* gCycle::LastWall(){
01905     if (lastWall)
01906         return lastWall->Wall();
01907     else
01908         return NULL;
01909 }
01910 */
01911 
01912 /*
01913 static tString lala_bodyTexture("Anonymous/original/textures/cycle_body.png");
01914 static nSettingItem<tString> gg_tex("TEXTURE_CYCLE_BODY", lala_bodyTexture);
01915 //static tConfItemLine g_tex("CYCLE_BODY", sg_bodyTexture);
01916 //static tSettingItem<tString> gg_tex("TEXTURE_CYCLE_BODY", sg_bodyTexture);
01917 static tString lala_wheelTexture("Anonymous/original/textures/cycle_wheel.png");
01918 static nSettingItem<tString> gg_wheelTexture("TEXTURE_CYCLE_WHEEL", lala_wheelTexture);
01919 
01920 static tString lala_bikeTexture("Anonymous/original/moviepack/bike.png");
01921 static nSettingItem<tString> lalala_bikeTexture("TEXTURE_MP_BIKE", lala_bikeTexture);
01922 */
01923 
01924 // HACK! Flexible model loader that eats anything a moviepack currently can throw at it.
01925 #ifndef DEDICATED
01926 struct gCycleVisuals
01927 {
01928     rModel *customModel, *bodyModel, *frontModel, *rearModel; // cycle models
01929     gTextureCycle *customTexture, *bodyTexture, *wheelTexture; // cycle textures
01930     gRealColor color; // cycle color
01931     bool mpType;      // true if moviepack type model/texture can be used
01932     int  mpPreference; // the user preference for the texture/model search path
01933 
01934     gCycleVisuals( gRealColor const & a_color )
01935     {
01936         customModel = bodyModel = frontModel = rearModel = 0;
01937         customTexture = bodyTexture = wheelTexture = 0;
01938 
01939         color = a_color;
01940 
01941         mpType = false;
01942         mpPreference = 0;
01943     }
01944 
01945     ~gCycleVisuals()
01946     {
01947         delete customModel;
01948         delete bodyModel;
01949         delete frontModel;
01950         delete rearModel;
01951         delete customTexture;
01952         delete bodyTexture;
01953         delete wheelTexture;
01954     }
01955 
01956     enum Slot{ SLOT_CUSTOM=0, SLOT_BODY=1, SLOT_WHEEL=2, SLOT_MAX=3 };
01957 
01958     // loads a specific texture from a specific folder
01959     static rSurface * LoadTextureSafe2( Slot slot, int mp )
01960     {
01961         static std::auto_ptr<rSurface> cache[SLOT_MAX][2];
01962         std::auto_ptr<rSurface> & surface = cache[slot][mp];
01963         if ( surface.get() == NULL )
01964         {
01965             static char const * names[SLOT_MAX]={"bike.png","cycle_body.png", "cycle_wheel.png"};
01966             char const * name = names[slot];
01967 
01968             char const * folder = mp ? "moviepack" : "textures";
01969             tString file = tString(folder) + tString("/") + tString( name );
01970 
01971             surface = std::auto_ptr<rSurface> ( tNEW( rSurface( file ) ) );
01972         }
01973 
01974         if ( surface->GetSurface() )
01975             return surface.get();
01976         else
01977             return NULL;
01978     }
01979 
01980     // load one texture from the prefered folder (or the other one as fallback)
01981     gTextureCycle * LoadTextureSafe( Slot slot, bool wheel )
01982     {
01983         rSurface * surface = LoadTextureSafe2( slot, mpPreference );
01984         if ( !surface )
01985             surface = LoadTextureSafe2( slot, 1-mpPreference );
01986 
01987         if ( surface )
01988             return tNEW( gTextureCycle )( *surface, color, 0, 0, wheel );
01989 
01990         return NULL;
01991     }
01992 
01993     // load textures from the specified folder (or the other one) and the format the model has just been read for
01994     bool LoadTextures()
01995     {
01996         if ( mpType )
01997         {
01998             if ( !customTexture )
01999                 customTexture = LoadTextureSafe( SLOT_CUSTOM, false );
02000 
02001             return customTexture;
02002         }
02003         else
02004         {
02005             if ( !bodyTexture )
02006                 bodyTexture = LoadTextureSafe( SLOT_BODY, false );
02007             if ( !wheelTexture )
02008                 wheelTexture = LoadTextureSafe( SLOT_WHEEL, true );
02009 
02010             return bodyTexture && wheelTexture;
02011         }
02012     }
02013 
02014     // loads a model, checking before if the file exists
02015     static rModel * LoadModelSafe( char const * filename )
02016     {
02017         std::ifstream in;
02018         if ( tDirectories::Data().Open( in, filename ) )
02019         {
02020             return tNEW(rModel( filename ));
02021         }
02022         return 0;
02023     }
02024 
02025     // load a model of specified type from a specified directory
02026     bool LoadModel( bool a_mpType, bool mpFolder )
02027     {
02028         mpType = a_mpType;
02029         char const * folder = mpFolder ? "moviepack" : "models";
02030 
02031         if ( mpType )
02032         {
02033             tString base = tString(folder);
02034             base << "/cycle";
02035             if (!customModel) customModel = LoadModelSafe( base + ".ASE" );
02036             if (!customModel) customModel = LoadModelSafe( base + ".ase" );
02037 
02038             return customModel && LoadTextures();
02039         }
02040         else
02041         {
02042             tString base = tString(folder) + "/cycle_";
02043 
02044             if (!bodyModel) bodyModel = LoadModelSafe( base + "body.mod" );
02045             if (!frontModel) frontModel = LoadModelSafe( base + "front.mod" );
02046             if (!rearModel) rearModel = LoadModelSafe( base + "rear.mod" );
02047 
02048             return bodyModel && frontModel && rearModel && LoadTextures();
02049         }
02050     }
02051 
02052     // tries to load everything from the given data folder
02053     bool LoadModel2( bool mp )
02054     {
02055         // first, try the right type, then the 'unnatural' choice
02056         return LoadModel( mp, mp ) || LoadModel( !mp, mp );
02057     }
02058 
02059     // top level load function: tries to load all variations, starting with passed moviepack folder flag
02060     bool LoadModel( bool mp )
02061     {
02062         mpPreference = mp ? 1 : 0;
02063 
02064         // delegate to try loading both formats from both directories
02065         return LoadModel2( mp ) || LoadModel2( !mp );
02066     }
02067 };
02068 #endif
02069 
02070 #ifndef DEDICATED
02071 // renders a cycle even after it died
02072 class gCycleWallRenderer: public eReferencableGameObject
02073 {
02074 public:
02075     gCycleWallRenderer( gCycle * cycle )
02076     : eReferencableGameObject( cycle->Grid(), cycle->Position(), cycle->Direction(), cycle->CurrentFace(), true )
02077     , cycle_( cycle )
02078     {
02079         AddToList();
02080     }
02081 
02082 #if 0 // not required
02083     virtual ~gCycleWallRenderer()
02084     {
02085     }
02086 
02087     virtual void OnRemoveFromGame()
02088     {
02089         eReferencableGameObject::OnRemoveFromGame();
02090     }
02091 #endif
02092 private:
02093     virtual void Render( eCamera const * camera )
02094     {
02095         cycle_->displayList_.RenderAll( camera, cycle_ );
02096     }
02097 
02098     virtual bool Timestep( REAL currentTime )
02099     {
02100         if ( !cycle_ )
02101         {
02102             return true;
02103         }
02104 
02105         Move( cycle_->Position(), lastTime, currentTime );
02106 
02107         return !cycle_->Alive() && !cycle_->displayList_.Walls();
02108     }
02109 
02110     tJUST_CONTROLLED_PTR< gCycle > cycle_;
02111 };
02112 #endif
02113 
02114 void gCycle::MyInitAfterCreation(){
02115 #ifndef DEDICATED
02116     joystick_ = tNEW( gJoystick( this ) );
02117 #else
02118     joystick_ = NULL;
02119 #endif
02120 
02121 // create wall renderer
02122 #ifndef DEDICATED
02123     new gCycleWallRenderer( this );
02124 #endif
02125 
02126     dropWallRequested_ = false;
02127     lastGoodPosition_ = pos;
02128 
02129 #ifdef DEBUG
02130     // con << "creating cycle.\n";
02131 #endif
02132     eSoundMixer* mixer = eSoundMixer::GetMixer();
02133     mixer->PlayContinuous(CYCLE_MOTOR, this);
02134 
02135     //correctDistSmooth=correctTimeSmooth=correctSpeedSmooth=0;
02136     correctDistanceSmooth = 0;
02137 
02138     resimulate_ = false;
02139 
02140     mp=sg_MoviePack();
02141 
02142     lastTimeAnim = 0;
02143     timeCameIntoView = 0;
02144 
02145     customModel = NULL;
02146     body = NULL;
02147     front = NULL;
02148     rear = NULL;
02149     wheelTex = NULL;
02150     bodyTex = NULL;
02151     customTexture = NULL;
02152 
02153     correctPosSmooth=eCoord(0,0);
02154 
02155     rotationFrontWheel=rotationRearWheel=eCoord(1,0);
02156 
02157     skew=skewDot=0;
02158 
02159     {
02160         dir=dirDrive;
02161         REAL dirLen=dir.NormSquared();
02162         if ( dirLen > 0 )
02163         {
02164             dir = dir * (1/sqrt( dirLen ));
02165         }
02166     }
02167 
02168     if (sn_GetNetState()!=nCLIENT){
02169         if(!player)
02170         { // distribute AI colors
02171             // con << current_ai << ':' << take_ai << ':' << maxmindist <<  "\n\n\n";
02172             color_.r=1;
02173             color_.g=1;
02174             color_.b=1;
02175 
02176             trailColor_.r=1;
02177             trailColor_.g=1;
02178             trailColor_.b=1;
02179         }
02180         else
02181         {
02182             player->Color(color_.r,color_.g,color_.b);
02183             player->TrailColor(trailColor_.r,trailColor_.g,trailColor_.b);
02184         }
02185 
02186         se_MakeColorValid( color_.r, color_.g, color_.b, 1.0f );
02187         se_MakeColorValid( trailColor_.r, trailColor_.g, trailColor_.b, .5f );
02188     }
02189 
02190     // load model and texture
02191 #ifndef DEDICATED
02192     gCycleVisuals visuals( color_ );
02193     if ( !visuals.LoadModel( mp ) )
02194     {
02195         tERR_ERROR( "Neither classic style nor moviepack style model and textures found. "
02196                     "The folders \"textures\" and \"moviepack\" need to contain either "
02197                     "cycle.ase and bike.png or body.mod, front.mod, rear.mod, cycle_body.png and cycle_wheel.png." );
02198     }
02199 
02200     mp = visuals.mpType;
02201 
02202     // transfer models and textures
02203     if ( mp )
02204     {
02205         // use moviepack style body and texture
02206         customModel = visuals.customModel;
02207         visuals.customModel = 0;
02208         customTexture = visuals.customTexture;
02209         visuals.customTexture = 0;
02210     }
02211     else
02212     {
02213         // use classic style body and texture
02214         body = visuals.bodyModel;
02215         visuals.bodyModel = 0;
02216         front = visuals.frontModel;
02217         visuals.frontModel = 0;
02218         rear = visuals.rearModel;
02219         visuals.rearModel = 0;
02220         bodyTex = visuals.bodyTexture;
02221         visuals.bodyTexture = 0;
02222         wheelTex = visuals.wheelTexture;
02223         visuals.wheelTexture = 0;
02224 
02225         tASSERT ( body && front && rear && bodyTex && wheelTex );
02226 
02227         mp = false;
02228     }
02229 #endif // DEDICATED
02230 
02231     /*
02232       the old, less flexible, loading code
02233 
02234     if (mp)
02235     {
02236         customModel=new rModel("moviepack/cycle.ASE","moviepack/cycle.ase");
02237     }
02238     else{
02239         body=new rModel("models/cycle_body.mod");
02240         front=new rModel("models/cycle_front.mod");
02241         rear=new rModel("models/cycle_rear.mod");
02242     }
02243 
02244     if (mp)
02245     {
02246         // static rSurface bike(lala_bikeTexture);
02247         static rSurface bike("moviepack/bike.png");
02248         customTexture=new gTextureCycle(bike,color_,0,0);
02249     }
02250     else{
02251         static rSurface body("textures/cycle_body.png");
02252         static rSurface wheel("textures/cycle_wheel.png");
02253         //static rSurface body((const char*) lala_bodyTexture);
02254         //static rSurface wheel((const char*) lala_wheelTexture);
02255         wheelTex=new gTextureCycle(wheel,color_,0,0,true);
02256         bodyTex=new gTextureCycle(body,color_,0,0);
02257     }
02258     */
02259 
02260 #ifdef DEBUG
02261     con << "Created cycle.\n";
02262 #endif
02263     nextSyncOwner=nextSync=tSysTimeFloat()-1;
02264 
02265     if (sn_GetNetState()!=nCLIENT)
02266         RequestSync();
02267 
02268     grid->AddGameObjectInteresting(this);
02269 
02270     spawnTime_=lastTimeAnim=lastTime;
02271     // set spawn time to infinite past if this is the first spawn
02272     if ( !sg_cycleFirstSpawnProtection && spawnTime_ <= 1.0 )
02273     {
02274         spawnTime_ = -1E+20;
02275     }
02276     dirSpawn = dirDrive;
02277 
02278     // add to game grid
02279     this->AddToList();
02280 
02281     predictPosition_ = pos;
02282 
02283 #ifdef GNUPLOT_DEBUG
02284     if ( sg_gnuplotDebug && Player() )
02285     {
02286         std::ofstream f( Player()->GetUserName() + "_step" );
02287         f << pos.x << " " << pos.y << "\n";
02288         std::ofstream g( Player()->GetUserName() + "_sync" );
02289         g << pos.x << " " << pos.y << "\n";
02290         std::ofstream h( Player()->GetUserName() + "_turn" );
02291         h << pos.x << " " << pos.y << "\n";
02292     }
02293 #endif
02294 }
02295 
02296 void gCycle::InitAfterCreation(){
02297 #ifdef DEBUG
02298     if (!finite(Speed()))
02299         st_Breakpoint();
02300 #endif
02301     gCycleMovement::InitAfterCreation();
02302 #ifdef DEBUG
02303     if (!finite(Speed()))
02304         st_Breakpoint();
02305 #endif
02306     MyInitAfterCreation();
02307 }
02308 
02309 gCycle::gCycle(eGrid *grid, const eCoord &pos,const eCoord &d,ePlayerNetID *p)
02310         :gCycleMovement(grid, pos,d,p,false),
02311         skew(0),skewDot(0),
02312         rotationFrontWheel(1,0),rotationRearWheel(1,0),heightFrontWheel(0),heightRearWheel(0),
02313         currentWall(NULL),
02314         lastWall(NULL)
02315 {
02316     windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dirDrive);
02317     dirDrive = Grid()->GetDirection(windingNumberWrapped_);
02318     dir = dirDrive;
02319 
02320     deathTime=0;
02321 
02322     lastNetWall=lastWall=currentWall=NULL;
02323 
02324     MyInitAfterCreation();
02325 
02326     sg_ArchiveCoord( this->dirDrive, 1 );
02327     sg_ArchiveCoord( this->dir, 1 );
02328     sg_ArchiveCoord( this->pos, 1 );
02329     sg_ArchiveReal( this->verletSpeed_, 1 );
02330 }
02331 
02332 gCycle::~gCycle(){
02333 #ifdef DEBUG
02334     //  con << "deleting cylce...\n";
02335 #endif
02336     // clear the destination list
02337 
02338     eSoundMixer* mixer = eSoundMixer::GetMixer();
02339     mixer->RemoveContinuous(CYCLE_MOTOR, this);
02340 
02341     this->RemoveFromGame();
02342 
02343     if (mp){
02344         delete customModel;
02345         delete customTexture;
02346     }
02347     else{
02348         delete body;
02349         delete front;
02350         delete rear;
02351         delete wheelTex;
02352         delete bodyTex;
02353     }
02354 #ifdef DEBUG
02355     //con << "Deleted cycle.\n";
02356 #endif
02357     /*
02358       delete currentPos;
02359       delete last_turn;
02360     */
02361 }
02362 
02363 void gCycle::OnRemoveFromGame()
02364 {
02365     // keep this cycle alive during this function
02366     tJUST_CONTROLLED_PTR< gCycle > keep;
02367 
02368     if ( this->GetRefcount() > 0 )
02369     {
02370         keep = this;
02371 
02372         this->Turn(0);
02373 
02374         if ( sn_GetNetState() == nSERVER )
02375             RequestSync();
02376     }
02377 
02378     // make sure we're dead, so our walls know they need to time out.
02379     if ( Alive() )
02380     {
02381         Die( lastTime );
02382     }
02383 
02384     if (currentWall)
02385         currentWall->CopyIntoGrid(0);
02386     currentWall=NULL;
02387     lastWall=NULL;
02388 
02389     gCycleMovement::OnRemoveFromGame();
02390 
02391     delete joystick_;
02392     joystick_ = NULL;
02393 }
02394 
02395 // called when the round ends
02396 void gCycle::OnRoundEnd()
02397 {
02398     // give survival bonus
02399     if ( Alive() && player )
02400     {
02401         Player()->AddScore( score_survive, tOutput("$player_win_survive"), tOutput() );
02402     }
02403 }
02404 
02405 
02406 static inline void rotate(eCoord &r,REAL angle){
02407     REAL x=r.x;
02408     r.x+=r.y*angle;
02409     r.y-=x*angle;
02410     r=r*(1/sqrt(r.NormSquared()));
02411 }
02412 
02413 #ifdef MACOSX
02414 // Sparks have a large performance problem on Macs. See http://guru3.sytes.net/viewtopic.php?t=2167
02415 bool crash_sparks=false;
02416 #else
02417 bool crash_sparks=true;
02418 #endif
02419 
02420 //static bool forceTime=false;
02421 
02422 // from nNetwork.C
02423 extern REAL planned_rate_control[MAXCLIENTS+2];
02424 
02426 struct gPredictPositionData
02427 {
02428     REAL maxSpaceReport; 
02429 #ifdef DEDICATED
02430     REAL rubberStart;    
02431 #endif
02432 };
02433 
02434 // *******************************************************************************************
02435 // *
02436 // *    PreparePredictPosition()
02437 // *
02438 // *******************************************************************************************
02442 // *******************************************************************************************
02443 void gCycle::PreparePredictPosition( gPredictPositionData & data )
02444 {
02445     // don't cast a ray by default
02446     data.maxSpaceReport = 0;
02447 #ifdef DEDICATED
02448     if ( sg_predictWalls )
02449     {
02450         // predict to the maximum time anyone else may be simulated up to
02451         REAL maxTime = lastTime + MaxSimulateAhead() + GetMaxLazyLag();
02452         REAL predictDT = maxTime - lastTime;
02453         REAL lookAhead = predictDT * verletSpeed_;
02454 
02455         // see how far we can go before rubber kicks in
02456         data.rubberStart = verletSpeed_ / RubberSpeed();
02457 
02458         // store max lookahead plus rubber safety
02459         data.maxSpaceReport = lookAhead + data.rubberStart;
02460     }
02461 #else
02462     data.maxSpaceReport = verletSpeed_ * se_PredictTime() * rubberSpeedFactor;
02463 #endif
02464 
02465     // request a raycast of the right length
02466     maxSpaceMaxCast_ = data.maxSpaceReport;
02467 }
02468 
02469 // *******************************************************************************************
02470 // *
02471 // *    CalculatePredictPosition()
02472 // *
02473 // *******************************************************************************************
02478 // *******************************************************************************************
02479 
02480 REAL gCycle::CalculatePredictPosition( gPredictPositionData & data )
02481 {
02482     // predict position
02483     REAL predictTime = lastTime;
02484     {
02485 #ifdef DEDICATED
02486         predictPosition_ = pos;
02487 
02488         if ( sg_predictWalls )
02489         {
02490             REAL spaceAhead = GetMaxSpaceAhead( data.maxSpaceReport );
02491             spaceAhead -= data.rubberStart;
02492 
02493             if ( spaceAhead > 0 )
02494             {
02495                 // store consistent prediction position and  time
02496                 predictPosition_ = pos + dirDrive * spaceAhead;
02497                 predictTime = lastTime + spaceAhead/verletSpeed_;
02498             }
02499         }
02500 #else
02501         // predict half a frame time
02502         predictTime += se_PredictTime();
02503         predictPosition_ = pos+correctPosSmooth + dirDrive * GetMaxSpaceAhead( data.maxSpaceReport );
02504 #endif
02505     }
02506 
02507     return predictTime;
02508 }
02509 
02510 bool gCycle::Timestep(REAL currentTime){
02511     // clear out dangerous info when we're done
02512     gMaxSpaceAheadHitInfoClearer hitInfoClearer( maxSpaceHit_ );
02513 
02514     // if ( Owner() == sn_myNetID )
02515     //    con << pos << ',' << distance << ',' << eCoord::F( dirDrive, pos ) - distance << '\n';
02516 
02517     // request the right space ahead for wall extrapolation
02518     gPredictPositionData predictPositionData;
02519     PreparePredictPosition( predictPositionData );
02520 
02521     // drop current wall if it was requested
02522     if ( dropWallRequested_ )
02523     {
02524         // but don't do so too often globally (performance)
02525         static double nextDrop = 0;
02526         double time = tSysTimeFloat();
02527         if ( time >= nextDrop )
02528         {
02529             unsigned short idrec = ID();
02530             tRecorderSync< unsigned short >::Archive( "_PARTIAL_COPY_GRID", 8, idrec );
02531 
02532             nextDrop = time + sg_minDropInterval;
02533             if ( currentWall )
02534             {
02535                 currentWall->Update(lastTime,pos);
02536                 currentWall->PartialCopyIntoGrid( grid );
02537             }
02538             dropWallRequested_ = false;
02539         }
02540     }
02541 
02542 #ifdef GNUPLOT_DEBUG
02543     if ( sg_gnuplotDebug && Player() )
02544     {
02545         std::ofstream f( Player()->GetUserName() + "_step", std::ios::app );
02546         f << pos.x << " " << pos.y << "\n";
02547     }
02548 #endif
02549     // timewarp test debug code
02550     //if ( Player() && Player()->IsHuman() )
02551     //    currentTime -= .1;
02552 
02553     // don't timestep when you're dead
02554     if ( !Alive() )
02555     {
02556         // die completely
02557         Die( lastTime );
02558 
02559         // and let yourself be removed from the lists so we don't have to go
02560         // through this again.
02561         return true;
02562     }
02563 
02564     // Debug archive position and speed
02565     sg_ArchiveCoord( pos, 7 );
02566     sg_ArchiveReal( verletSpeed_, 7 );
02567 
02568     if ( !destinationList && sn_GetNetState() == nCLIENT )
02569     {
02570         // insert emergency first destination without broadcasting it
02571         gDestination* dest = tNEW(gDestination)(*this);
02572         dest->messageID = 0;
02573         dest->distance = -.001;
02574         dest->InsertIntoList(&destinationList);
02575     }
02576 
02577     // start new extrapolation
02578     if ( !extrapolator_ && resimulate_ )
02579         ResetExtrapolator();
02580 
02581     // extrapolate state from server and copy state when finished
02582     if ( extrapolator_ )
02583     {
02584         REAL dt = ( currentTime - lastTime ) * sg_syncFF / sg_syncFFSteps;
02585 #ifdef DEBUG
02586         // dt *= 10.101;
02587         //if ( !resimulate_ )
02588         //      dt *= .1;
02589 #endif
02590         for ( int i = sg_syncFFSteps - 1; i>= 0; --i )
02591         {
02592             if ( Extrapolate( dt ) )
02593             {
02594                 SyncFromExtrapolator();
02595                 break;
02596             }
02597         }
02598     }
02599 
02600     bool ret = false;
02601 
02602     // nothing special if simulating backwards
02603     if (currentTime < lastTime)
02604     {
02605         ret = gCycleMovement::Timestep(currentTime);
02606     }
02607     // no targets are given
02608     else if ( !currentDestination && pendingTurns.empty() )
02609     {
02610         // chatting? activate chatbot
02611         if ( bool(player) &&
02612                 player->IsHuman() &&
02613                 ( sg_chatBotAlwaysActive || player->IsChatting() ) &&
02614                 player->Owner() == sn_myNetID )
02615         {
02616             gCycleChatBot & bot = gCycleChatBot::Get( this );
02617             bot.Activate( currentTime );
02618         }
02619         else if ( &(*chatBot_) )
02620         {
02621             chatBot_->nextChatAI_ = 0;
02622         }
02623 
02624         bool simulate=Alive();
02625 
02626         if ( !pendingTurns.empty() || currentDestination )
02627             simulate=true;
02628 
02629         if (simulate)
02630         {
02631             try
02632             {
02633                 ret = gCycleMovement::Timestep(currentTime);
02634             }
02635             catch ( gCycleDeath const & death )
02636             {
02637                 KillAt( death.pos_ );
02638                 return false;
02639             }
02640         }
02641         else
02642             ret = !Alive();
02643     }
02644     else
02645     {
02646         // just basic movement to do: let base class handle that.
02647         try
02648         {
02649             gCycleMovement::Timestep(currentTime);
02650         }
02651         catch ( gCycleDeath const & death )
02652         {
02653             KillAt( death.pos_ );
02654             return false;
02655         }
02656     }
02657 
02658     // do the rest of the timestep
02659     try
02660     {
02661         if ( currentTime > lastTime )
02662             ret = gCycleMovement::Timestep(currentTime);
02663     }
02664     catch ( gCycleDeath const & death )
02665     {
02666         KillAt( death.pos_ );
02667         return false;
02668     }
02669 
02670     REAL predictTime = CalculatePredictPosition( predictPositionData );
02671 
02672     if ( Alive() && currentWall )
02673     {
02674         // z-man: the next two lines are a very bad idea. This lets walls stick out on the other side while you're using up your rubber.
02675         //if ( sn_GetNetState() != nSERVER )
02676         //    wallEndPos = pos + dirDrive * ( verletSpeed_ * se_PredictTime() );
02677 
02678         // but using the predicted position which halts at walls may work
02679         currentWall->Update(predictTime, PredictPosition() );
02680     }
02681 
02682     return ret;
02683 }
02684 
02685 // lets a value decay smoothly
02686 static void DecaySmooth( REAL& smooth, REAL relSpeed, REAL minSpeed, REAL clamp )
02687 {
02688     if ( fabs(smooth) > .01 )
02689     {
02690         int x;
02691         x = 1;
02692     }
02693 
02694     // increase correction speed if the value is out of bounds
02695     if ( clamp > 0 )
02696         relSpeed *= ( 1 + smooth * smooth / ( clamp * clamp ) );
02697 
02698     // nothing to do
02699     if ( smooth == 0 )
02700     {
02701         return;
02702     }
02703 
02704     // calculate speed from relative speed
02705     REAL speed = smooth * relSpeed;
02706 
02707     // apply minimal correction
02708     if ( fabs( speed ) < minSpeed )
02709         speed = copysign ( minSpeed , smooth );
02710 
02711     // don't overshoot
02712     if ( fabs( speed ) > fabs( smooth ) )
02713         smooth = 0;
02714     else
02715         smooth -= speed;
02716 }
02717 
02718 // clamps a cycle displacement to non-confusing values
02719 static REAL ClampDisplacement( gCycle* cycle, eCoord& displacement, const eCoord& lookout, const eCoord& pos )
02720 {
02721     gSensor sensor( cycle, pos, lookout );
02722     sensor.detect(1);
02723     if ( sensor.ehit && sensor.hit >= 0 && sensor.hit < 1 )
02724     {
02725 #ifdef DEBUG_X
02726         // repeat sensor
02727         gSensor sensor( cycle, pos, lookout );
02728         sensor.detect(1);
02729 #endif
02730         displacement = displacement * sensor.hit;
02731     }
02732     return sensor.hit;
02733 }
02734 
02735 // from gCycleMovement.cpp
02736 REAL sg_GetSparksDistance();
02737 
02738 
02739 bool gCycle::TimestepCore(REAL currentTime, bool calculateAcceleration ){
02740     if (!finite(skew))
02741         skew=0;
02742     if (!finite(skewDot))
02743         skewDot=0;
02744 
02745     eCoord oldpos=pos;
02746 
02747     // let the joystick execute delayed turns
02748     if ( joystick_ )
02749     {
02750         joystick_->Turn();
02751     }
02752 
02753     // archive rubber speed for later comparison
02754     REAL rubberSpeedFactorBack = rubberSpeedFactor;
02755 
02756     REAL ts=(currentTime-lastTime);
02757 
02758     clamp(ts, -10, 10);
02759     //clamp(correctTimeSmooth, -100, 100);
02760     clamp(correctDistanceSmooth, -100, 100);
02761     //clamp(correctSpeedSmooth, -100, 100);
02762 
02763     // scale factor for smoothing. It is always used as
02764     // value += smooth * smooth_correction;
02765     // smooth_correction -= smooth * smooth_correction;
02766 
02767     REAL smooth = 0;
02768 
02769     if ( ts > 0 )
02770     {
02771         // go a bit of the way
02772         smooth = 1 - 1/( 1 + ts / sg_cycleSyncSmoothTime );
02773     }
02774 
02775     //if ( smooth > 0)
02776     //{
02777     //    REAL scd = correctDistanceSmooth * smooth;
02778     //    distance += scd;
02779     //    correctDistanceSmooth -= scd;
02780     // }
02781 
02782     // handle distance correction
02783     TransferPositionCorrectionToDistanceCorrection();
02784 
02785     // apply smooth position correction
02786     // smooth = .5f;
02787     //correctPosSmooth = eCoord(0,0);
02788 
02789     // correctPosSmooth = correctPosSmooth * ( 1 - smooth );
02790 
02791     //if ( 0 )
02792 
02793     REAL animts=currentTime-lastTimeAnim;
02794     if (animts<0 || !finite(animts))
02795         animts=0;
02796     else
02797         lastTimeAnim=currentTime;
02798 
02799     // handle decaying of smooth position correction
02800     {
02801         // let components of smooth position correction decay
02802         REAL minSpeed = sg_cycleSyncSmoothMinSpeed * Speed() * animts;
02803         REAL clamp    = sg_cycleSyncSmoothThreshold * Speed();
02804 
02805         DecaySmooth( correctPosSmooth.x, smooth, minSpeed, clamp );
02806         DecaySmooth( correctPosSmooth.y, smooth, minSpeed, clamp );
02807 
02808         // do sanity checks
02809         if ( correctPosSmooth.NormSquared() > EPS )
02810         {
02811             // cast ray to make sure corrected position lies on the right side of walls
02812             ClampDisplacement( this, correctPosSmooth, correctPosSmooth * 2, pos );
02813 
02814             // same for negative direction, players should see it when they are close to a wall
02815             ClampDisplacement( this, correctPosSmooth, -correctPosSmooth, pos );
02816 
02817             // cast another some rays into the future
02818             eCoord lookahead = dirDrive * ( 2 * correctPosSmooth.Norm() );
02819             ClampDisplacement( this, correctPosSmooth, lookahead, pos );
02820         }
02821     }
02822 
02823     if (animts>.2)
02824         animts=.2;
02825 
02826     rotate(rotationFrontWheel,2*verletSpeed_*animts/.43);
02827     rotate(rotationRearWheel,2*verletSpeed_*animts/.73);
02828 
02829     REAL sparksDistance = sg_GetSparksDistance();
02830     REAL extension = .25;
02831     if ( extension < sparksDistance )
02832         extension = sparksDistance;
02833 
02834     //    REAL step=speed*ts; // +.5*acceleration*ts*ts;
02835 
02836     // animate cycle direction
02837     {
02838         // move it a bit closer to dirDrive
02839         dir=dir+dirDrive*animts*verletSpeed_*3;
02840 
02841         // if it's too far away from dirDrive, clamp it
02842         eCoord dirDistance = dir - dirDrive;
02843         REAL dist = dirDistance.NormSquared();
02844         const REAL maxDist = .8;
02845         if (dirDistance.NormSquared() > maxDist)
02846             dir = dirDrive + dirDistance* (1/sqrt(dist/maxDist));
02847 
02848         // normalize
02849         dir=dir*(1/sqrt(dir.NormSquared()));
02850     }
02851 
02852     {
02853         eCoord oldPos = pos;
02854 
02855         if ( Alive() ){
02856             // delegate core work to base class
02857             try
02858             {
02859                 // start building wall
02860                 REAL startBuildWallAt = spawnTime_ + sg_cycleWallTime;
02861                 if ( !currentWall && currentTime > startBuildWallAt  )
02862                 {
02863                     // simulate right to the spot where the wall should begin
02864                     if ( currentTime < startBuildWallAt )
02865                         if ( gCycleMovement::TimestepCore( startBuildWallAt, calculateAcceleration ) )
02866                             return true;
02867                     calculateAcceleration = true;
02868 
02869                     // build the wall, modifying the spawn time to make sure it happens
02870                     REAL lastSpawn = spawnTime_;
02871                     spawnTime_ += -1E+20;
02872                     DropWall();
02873                     spawnTime_ = lastSpawn;
02874                     dirSpawn = dirDrive;
02875                     lastTurnPos_ = pos; // hack last turn position to generate good wall
02876                 }
02877 
02878                 // simulate rest of frame
02879                 if ( gCycleMovement::TimestepCore( currentTime, calculateAcceleration ) )
02880                     return true;
02881             }
02882             catch ( gCycleDeath const & death )
02883             {
02884                 KillAt( death.pos_ );
02885 
02886                 // death exceptions are precise; we can safely take over the position from it
02887                 oldPos = death.pos_;
02888             }
02889         }
02890 
02891         // die where you started
02892         if ( !Alive() )
02893         {
02894             MoveSafely(oldPos,currentTime,currentTime);
02895             Die( currentTime );
02896         }
02897     }
02898 
02899     // Debug archive position and speed
02900     sg_ArchiveCoord( pos, 7 );
02901     sg_ArchiveReal( verletSpeed_, 7 );
02902 
02903     if (Alive()){
02904 #ifndef DEDICATED
02905         // animate skew
02906         gSensor fl(this,pos,dirDrive.Turn(1,1));
02907         gSensor fr(this,pos,dirDrive.Turn(1,-1));
02908 
02909         fl.detect(extension*4);
02910         fr.detect(extension*4);
02911 
02912         if (fl.hit > extension)
02913             fl.hit = extension;
02914 
02915         if (fr.hit > extension)
02916             fr.hit = extension;
02917 
02918         REAL lr=(fl.hit-fr.hit)/extension;
02919 
02920         // ODE for skew
02921         const REAL skewrelax=24;
02922         skewDot-=128*(skew+lr/2)*animts;
02923         skewDot/=1+skewrelax*animts;
02924         if (skew*skewDot>4) skewDot=4/skew;
02925         skew+=skewDot*animts;
02926 
02927         REAL fac = 0.5f;
02928         if ( skew > fr.hit * fac )
02929         {
02930             skew = fr.hit * fac;
02931         }
02932         if ( skew < -fl.hit * fac )
02933         {
02934             skew = -fl.hit * fac;
02935         }
02936 
02937         // generate sparks
02938         eCoord sparkpos,sparkdir;
02939 
02940         if (fl.ehit && fl.hit<=sparksDistance){
02941             sparkpos=pos+dirDrive.Turn(1,1)*fl.hit;
02942             sparkdir=dirDrive.Turn(0,-1);
02943         }
02944         if (fr.ehit && fr.hit<=sparksDistance){
02945             sparkpos=pos+dirDrive.Turn(1,-1)*fr.hit;
02946             sparkdir=dirDrive.Turn(0,1);
02947         }
02948 
02949         if (fabs(skew)<fabs(lr*.8) ){
02950             skewDot-=lr*1000*animts;
02951             if (crash_sparks && animts>0)
02952             {
02953                 gPlayerWall *tmpplayerWall=0;
02954 
02955                 if(fl.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fl.ehit->GetWall());
02956                 if(fr.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fr.ehit->GetWall());
02957 
02958                 if(tmpplayerWall) {
02959                     gCycle *tmpcycle = tmpplayerWall->Cycle();
02960 
02961                     if( tmpcycle )
02962                         new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,tmpcycle->color_.r,tmpcycle->color_.g,tmpcycle->color_.b);
02963                 }
02964                 else
02965                     new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,1,1,1);
02966             }
02967         }
02968 
02969         if (fabs(skew)<fabs(lr*.9) ){
02970             skewDot-=lr*100*animts;
02971             if (crash_sparks && animts>0)
02972             {
02973                 gPlayerWall *tmpplayerWall=0;
02974 
02975                 if(fl.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fl.ehit->GetWall());
02976                 if(fr.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fr.ehit->GetWall());
02977 
02978                 if(tmpplayerWall) {
02979                     gCycle *tmpcycle = tmpplayerWall->Cycle();
02980 
02981                     if( tmpcycle )
02982                         new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,tmpcycle->color_.r,tmpcycle->color_.g,tmpcycle->color_.b);
02983                 }
02984                 else
02985                     new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,1,1,1);
02986             }
02987         }
02988 #endif
02989         /*
02990           if (fl.hit+fr.hit<extension*.4)
02991           Kill();
02992         */
02993     }
02994 
02995     // clamp skew
02996     if (skew>.5){
02997         skew=.5;
02998         skewDot=0;
02999     }
03000 
03001     if (skew<-.5){
03002         skew=-.5;
03003         skewDot=0;
03004     }
03005 
03006     // checkpoint wall when rubber starts to get used
03007     if ( currentWall )
03008     {
03009         if ( rubberSpeedFactor >= .99 && rubberSpeedFactorBack < .99 )
03010         {
03011             currentWall->Checkpoint();
03012         }
03013 
03014         if ( rubberSpeedFactor < .99 && rubberSpeedFactorBack >= .99 )
03015         {
03016             currentWall->Checkpoint();
03017         }
03018 
03019         if ( rubberSpeedFactor < .1 && rubberSpeedFactorBack >= .1 )
03020         {
03021             currentWall->Checkpoint();
03022         }
03023 
03024         if ( rubberSpeedFactor < .01 && rubberSpeedFactorBack >= .01 )
03025         {
03026             currentWall->Checkpoint();
03027         }
03028     }
03029 
03030 
03031     if ( sn_GetNetState()==nSERVER )
03032     {
03033         // do an emergency sync when rubber starts to get used, it may come unexpected to clients
03034         if ( rubberSpeedFactor < .99 && rubberSpeedFactorBack >= .99 )
03035         {
03036             RequestSyncOwner();
03037         }
03038 
03039         if (nextSync < tSysTimeFloat() )
03040         {
03041             // delay syncs for old clients when there is a wall ahead; they would tunnel locally
03042             REAL lookahead = Speed() * sg_syncIntervalEnemy*.5;
03043             if ( !sg_avoidBadOldClientSync || sg_NoLocalTunnelOnSync.Supported( Owner() ) || GetMaxSpaceAhead( lookahead ) >= lookahead )
03044             {
03045                 RequestSync(false);
03046 
03047                 // checkpoint wall for better collision accuracy, only required on the server
03048                 if ( currentWall )
03049                     currentWall->Checkpoint();
03050             }
03051 
03052             nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
03053             nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
03054         }
03055         else if ( nextSyncOwner < tSysTimeFloat() &&
03056                   Owner() != 0 &&
03057                   sn_Connections[Owner()].bandwidthControl_.Control( nBandwidthControl::Usage_Planning ) > 200 )
03058         {
03059             // sync only to the owner (provided there is enough bandwidth available)
03060             RequestSyncOwner();
03061         }
03062     }
03063 
03064     return (!Alive());
03065 }
03066 
03067 
03068 void gCycle::InteractWith(eGameObject *target,REAL,int){
03069     /*
03070       if (alive && target->type()==ArmageTron_CYCLE){
03071          gCycle *c=(gCycle *)target;
03072          if (c->alive){
03073       const eEdge *current_eEdge=Edge();
03074       const eEdge *other_eEdge=c->Edge();
03075       if(current_eEdge && other_eEdge){
03076       ePoint *meet=current_eEdge->IntersectWith(other_eEdge);
03077       if (meet){ // somebody is going to die!
03078       REAL ratio=current_eEdge->Ratio(*meet);
03079       REAL time=CurrentWall()->Time(ratio);
03080       PassEdge(other_eEdge,time,other_eEdge->Ratio(*meet),0);
03081       }
03082       }
03083          }
03084       }
03085     */
03086 }
03087 
03088 // *******************************************************************************************
03089 // *
03090 // *    Die
03091 // *
03092 // *******************************************************************************************
03096 // *******************************************************************************************
03097 
03098 void gCycle::Die( REAL time )
03099 {
03100     if ( sn_GetNetState() == nSERVER )
03101     {
03102         // request one last sync
03103         RequestSync( true );
03104     }
03105 
03106     gCycleMovement::Die( time );
03107 
03108     // reset smoothing
03109     correctPosSmooth = eCoord();
03110     TransferPositionCorrectionToDistanceCorrection();
03111     predictPosition_ = pos;
03112 
03113     // delete all temporary walls of this cycle
03114     for ( int i = sg_netPlayerWalls.Len()-1; i >= 0; --i )
03115     {
03116         gNetPlayerWall * wall = sg_netPlayerWalls(i);
03117         if ( wall->Cycle() == this && wall->Preliminary() )
03118         {
03119             wall->real_CopyIntoGrid(grid);
03120         }
03121     }
03122 }
03123 
03124 void gCycle::KillAt( const eCoord& deathPos){
03125     // don't kill invulnerable cycles
03126     if ( !Vulnerable() )
03127     {
03128         MoveSafely( deathPos, lastTime, lastTime );
03129         return;
03130     }
03131 
03132     // find the killer from the enemy influence storage
03133     ePlayerNetID const * constHunter = enemyInfluence.GetEnemy();
03134     ePlayerNetID * hunter = Player();
03135 
03136     // cast away const the safe way
03137     if ( constHunter && constHunter->Object() )
03138         hunter = constHunter->Object()->Player();
03139 
03140     // only take it if it is not too old
03141     if ( LastTime() - enemyInfluence.GetTime() > sg_suicideTimeout )
03142         hunter = NULL;
03143 
03144     // suicide?
03145     if ( !hunter )
03146         hunter = Player();
03147 
03148 
03149     if (!Alive() || sn_GetNetState()==nCLIENT)
03150         return;
03151 
03152 #ifdef KRAWALL_SERVER_LEAGUE
03153     if (    hunter           && Player()          &&
03154             !dynamic_cast<gAIPlayer*>(hunter)     &&
03155             !dynamic_cast<gAIPlayer*>(Player())             &&
03156             hunter->IsAuth() && Player()->IsAuth())
03157         nKrawall::ServerFrag(hunter->GetUserName(), Player()->GetUserName());
03158 #endif
03159 
03160     if (hunter==Player())
03161     {
03162         if (hunter)
03163         {
03164             tString ladderLog;
03165             ladderLog << "DEATH_SUICIDE " << hunter->GetUserName() << "\n";
03166             se_SaveToLadderLog( ladderLog );
03167             tString notificationMessage(hunter->GetUserName());
03168             notificationMessage << " commited suicide";
03169             se_sendEventNotification(tString("Death suicide"), notificationMessage);
03170 
03171             if ( score_suicide )
03172                 hunter->AddScore(score_suicide, tOutput(), "$player_lose_suicide" );
03173             else
03174             {
03175                 tColoredString hunterName;
03176                 hunterName << *hunter << tColoredString::ColorString(1,1,1);
03177                 sn_ConsoleOut( tOutput( "$player_free_suicide", hunterName ) );
03178             }
03179         }
03180     }
03181     else{
03182         if (hunter)
03183         {
03184             tOutput lose;
03185             tOutput win;
03186             if (Player())
03187             {
03188                 tColoredString preyName;
03189                 preyName << *Player();
03190                 preyName << tColoredString::ColorString(1,1,1);
03191                 if (Player()->CurrentTeam() != hunter->CurrentTeam()) {
03192                     tString ladderLog;
03193                     ladderLog << "DEATH_FRAG " << Player()->GetUserName() << " " << hunter->GetUserName()  << "\n";
03194                     se_SaveToLadderLog( ladderLog );
03195                     tString notificationMessage(hunter->GetUserName());
03196                     notificationMessage << " core dumped " << Player()->GetUserName();
03197                     se_sendEventNotification(tString("Death frag"), notificationMessage);
03198 
03199                     win.SetTemplateParameter(3, preyName);
03200                     win << "$player_win_frag";
03201                     if ( score_kill != 0 )
03202                         hunter->AddScore(score_kill, win, lose );
03203                     else
03204                     {
03205                         tColoredString hunterName;
03206                         hunterName << *hunter << tColoredString::ColorString(1,1,1);
03207                         sn_ConsoleOut( tOutput( "$player_free_frag", hunterName, preyName ) );
03208                     }
03209                 }
03210                 else {
03211                     tString ladderLog;
03212                     ladderLog << "DEATH_TEAMKILL " << Player()->GetUserName() << " " << hunter->GetUserName()  << "\n";
03213                     se_SaveToLadderLog( ladderLog );
03214                     tString notificationMessage(hunter->GetUserName());
03215                     notificationMessage << " teamkilled " << Player()->GetUserName();
03216                     se_sendEventNotification(tString("Death teamkill"), notificationMessage);
03217 
03218                     tColoredString hunterName;
03219                     hunterName << *hunter << tColoredString::ColorString(1,1,1);
03220                     sn_ConsoleOut( tOutput( "$player_teamkill", hunterName, preyName ) );
03221                 }
03222             }
03223             else
03224             {
03225                 win << "$player_win_frag_ai";
03226                 hunter->AddScore(score_kill, win, lose);
03227             }
03228         }
03229         //      if (prey->player && (prey->player->CurrentTeam() != hunter->player->CurrentTeam()))
03230         if (Player())
03231             Player()->AddScore(score_die,tOutput(),"$player_lose_frag");
03232     }
03233 
03234     //collect stats! huzzah!
03235     if (Player() && hunter && gStats)
03236     {
03237         //always a death
03238         if (Player()->IsHuman())
03239         {
03240             gStats->deaths->add(Player()->GetName(), 1);
03241         }
03242         if (hunter != Player() && hunter->IsHuman()) //but a kill too?
03243         {
03244             gStats->kills->add(hunter->GetName(), 1);
03245         }
03246     }
03247 
03248     // set position to death position so the explosion is at the right place (I dimly remember this caused problems in an earlier attempt)
03249     this->pos = deathPos;
03250 
03251     Kill();
03252 }
03253 
03254 class gJustChecking
03255 {
03256 public:
03257     static bool justChecking;
03258 
03259     gJustChecking(){ justChecking = false; }
03260     ~gJustChecking(){ justChecking = true; }
03261 };
03262 
03263 bool gJustChecking::justChecking = true;
03264 
03265 bool gCycle::EdgeIsDangerous(const eWall* ww, REAL time, REAL a) const{
03266     gPlayerWall const * w = dynamic_cast< gPlayerWall const * >( ww );
03267     if ( w )
03268     {
03269         if ( !Vulnerable() )
03270             return false;
03271 
03272         gNetPlayerWall *nw = w->NetWall();
03273 
03274         // check whether the wall is one of the walls no real collision is
03275         // possible with. The lastNetWall is only checked for enemy cycles,
03276         // because it can be an arbitrary wall for your own cycle. But for
03277         // enemy cycles, it may be what lastWall should be, and the test
03278         // prevents enemy cycles from getting stuck right after a turn.
03279         if( nw == currentWall || nw == lastWall || ( nw == lastNetWall && Owner() != sn_myNetID ) )
03280             return false;
03281 
03282         // see if the wall is from another player and from the future.
03283         // if it is, it's not dangerous, this cycle was here first.
03284         // on passing the wall later, the other cycle will be pushed back
03285         // to the collision point or killed if that is no longer possible.
03286         // z-man notes: I've got the vaque feeling something like this was
03287         // here before, but got thrown out again for making problems.
03288         if ( gJustChecking::justChecking && w->CycleMovement() != static_cast< const gCycleMovement * >( this ) && w->Time(a) > time )
03289         {
03290             // One problem with this is that you kill teammates if they can't
03291             // be pushed back, whereas we'd just use our rubber to survive.
03292             // So we should first check whether the other player should be
03293             // protected.
03294             gCycle const * otherPlayer = w->Cycle();
03295             if ( !otherPlayer || // valididy
03296                     otherPlayer->Team() != this->Team() || // team protection
03297                     !otherPlayer->currentWall || w == otherPlayer->currentWall->Wall() // pushback protection, if the other player can be pushed back, it's all right as well
03298                )
03299                 return false;
03300         }
03301     }
03302 
03303     return gCycleMovement::EdgeIsDangerous( ww, time, a );
03304 }
03305 
03306 // turn future walls of a cycle into gaping holes of nothingness if it indeed belongs to the cycle
03307 static void sg_KillFutureWall( gCycle * cycle, gNetPlayerWall * wall )
03308 {
03309     if ( cycle && wall && wall->Cycle() == cycle && wall->Pos(1) > cycle->GetDistance() )
03310     {
03311         wall->BlowHole( cycle->GetDistance(), wall->Pos(1) + 100, 0 );
03312     }
03313 }
03314 
03315 // turn future walls of a cycle into gaping holes of nothingness
03316 static void sg_KillFutureWalls( gCycle * cycle )
03317 {
03318 #ifdef DEBUG_X
03319     con << "Removing future walls of the cylce that just got killed mercilessly.\n";
03320 #endif
03321 
03322     // handle future walls that won't be drawn after all. Just make them a big hole.
03323     if ( sn_GetNetState() != nCLIENT )
03324     {
03325         int i;
03326         for ( i = sg_netPlayerWalls.Len()-1; i >= 0; --i )
03327             sg_KillFutureWall( cycle, sg_netPlayerWalls(i) );
03328 
03329         for ( i = sg_netPlayerWallsGridded.Len()-1; i >= 0; --i )
03330             sg_KillFutureWall( cycle, sg_netPlayerWallsGridded(i) );
03331     }
03332 }
03333 
03334 static void sg_HoleScore( gCycle & cycle )
03335 {
03336     cycle.Player()->AddScore( score_hole, tOutput("$player_win_hole"), tOutput("$player_lose_hole") );
03337 }
03338 
03339 void gCycle::PassEdge(const eWall *ww,REAL time,REAL a,int){
03340     {
03341         // deactivate time check
03342         gJustChecking thisIsSerious;
03343 
03344         if (!EdgeIsDangerous(ww,time,a) || !Alive() )
03345         {
03346             // request a sync for everyone if this is a non-bogus wall passage, maybe not all clients know the wall is passable
03347             if ( ( !currentWall || ww != currentWall->Wall() ) && ( !lastWall || ww != lastWall->Wall() ) )
03348                 RequestSyncAll();
03349             
03350             // check whether we drove through a hole in an enemy wall made by a teammate
03351             gPlayerWall const * w = dynamic_cast< gPlayerWall const * >( ww );
03352             if ( w && score_hole )
03353             {
03354                 gExplosion * explosion = w->Holer( a, time );
03355                 if ( explosion )
03356                 {
03357                     gCycle * holer = explosion->GetOwner();
03358                     if ( holer && holer->Player() &&
03359                          Player() &&
03360                          w->Cycle() && w->Cycle()->Player() &&
03361                          holer->Player()->CurrentTeam() == Player()->CurrentTeam() &&       // holer must have been a teammate 
03362                          w->Cycle()->Player()->CurrentTeam() != Player()->CurrentTeam()  // wall must have been an enemy
03363                         )
03364                     {
03365                         // this test must come last, it resets the flag.
03366                         if ( explosion->AccountForHole() )
03367                         {
03368                             tString ladderLog;
03369                             ladderLog << "SACRIFICE " << Player()->GetUserName() << " " << holer->Player()->GetUserName() << " " << w->Cycle()->Player()->GetUserName() << "\n";
03370                             se_SaveToLadderLog( ladderLog );
03371                             if ( score_hole > 0 )
03372                             {
03373                                 // positive hole score goes to the holer
03374                                 sg_HoleScore( *holer );
03375                             }
03376                             else
03377                             {
03378                                 // negative hole score to the driver
03379                                 sg_HoleScore( *this );
03380 
03381                             }
03382                        }
03383                     }
03384                 }
03385             }
03386 
03387             return;
03388         }
03389 
03390 #ifdef DEBUG
03391         if (!EdgeIsDangerous(ww,time,a) || !Alive() )
03392             return;
03393 #endif
03394     }
03395 
03396     // request a sync to bring everyone up to date about the cycle passing/getting stuck on this wall
03397     RequestSyncOwner();
03398 
03399 #ifdef DEBUG_X
03400     // keep other cycle around
03401     tJUST_CONTROLLED_PTR<gCycleExtrapolator> keepOther( extrapolator_ );
03402 
03403     ResetExtrapolator();
03404 
03405     // extrapolate state from server and copy state when finished
03406     REAL dt = 1;
03407     for ( int i = 9; i>= 0; --i )
03408     {
03409         Extrapolate( dt );
03410     }
03411 
03412     extrapolator_ = keepOther;
03413 #endif
03414 
03415     eCoord collPos = ww->Point( a );
03416 
03417     const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(ww);
03418 
03419     enemyInfluence.AddWall( ww, collPos, 0, this );
03420 
03421     if (w)
03422     {
03423         gCycle *otherPlayer=w->Cycle();
03424 
03425         REAL otherTime = w->Time(a);
03426         if(otherPlayer && time < otherTime*(1-EPS) )
03427         {
03428             // also send updates about the other cylce
03429             otherPlayer->RequestSyncOwner();
03430 
03431             // get the distance of the wall
03432             REAL wallDist = w->Pos(a);
03433             // get the distance the cycle is simulated up to
03434             REAL cycleDist = w->Cycle()->distance;
03435             // comparing these two gives an accurate criterion whether the wall is extrapolated
03436             if ( wallDist > cycleDist * (1 + EPS ) )
03437             {
03438                 static bool fix = false;
03439                 // it's an extrapolation wall, don't simulate further.
03440                 if ( fix && lastTime > se_GameTime() - 2 * Lag() - GetMaxLazyLag() )
03441                     throw gCycleStop();
03442                 else
03443                     return;
03444             }
03445 
03446             // we were first!
03447             if ( otherPlayer->Vulnerable() )
03448             {
03449                 static bool tryToSaveFutureWallOwner = true;
03450                 bool saved = false;
03451 
03452                 if ( tryToSaveFutureWallOwner && otherPlayer->currentWall && w == otherPlayer->currentWall->Wall() )
03453                 {
03454                     // teleport the other cycle back to the point before the collision; its next timestep
03455                     // will simulate the collision again from the right viewpoint
03456                     // determine the distance of the pushback
03457                     REAL d = otherPlayer->GetDistanceSinceLastTurn() * .001;
03458                     if ( d < .01 )
03459                         d = .01;
03460                     REAL maxd = eCoord::F( otherPlayer->dirDrive, collPos - otherPlayer->GetLastTurnPos() ) * .5/otherPlayer->dirDrive.NormSquared();
03461                     if ( d > maxd )
03462                         d = maxd;
03463                     if ( d > 0 )
03464                     {
03465                         saved = true;
03466 
03467                         // do the move
03468                         otherPlayer->MoveSafely( collPos-otherPlayer->dirDrive*d, otherPlayer->LastTime(), otherTime - d/otherPlayer->Speed() );
03469                         otherPlayer->currentWall->Update( otherPlayer->lastTime, otherPlayer->pos );
03470                         otherPlayer->dropWallRequested_ = false;
03471 
03472                         // drop our wall so collisions are more accurate
03473                         dropWallRequested_ = true;
03474                     }
03475                 }
03476 
03477                 // another possibility: if the walls are very short compared to rubber, we could
03478                 // get away with just accounding for some rubber on the cycle that we'd need to kill
03479                 // otherwise.
03480                 if ( !saved && verletSpeed_ >= 0 )
03481                 {
03482                     REAL dt = otherTime - time;
03483 
03484                     // this long would the other cycle have to sit in front of our wall
03485                     // before he's released by the end
03486                     REAL wallTimeLeft = this->ThisWallsLength()/verletSpeed_ - dt;
03487 
03488                     if ( wallTimeLeft < 0 )
03489                     {
03490                         // isn't hit at all
03491                         return;
03492                     }
03493 
03494                     // check how much rubber would be used
03495                     REAL max, effectiveness;
03496                     sg_RubberValues( otherPlayer->Player(), otherPlayer->verletSpeed_, max, effectiveness );
03497                     if ( effectiveness > 0 )
03498                     {
03499                         REAL rubberToEat = wallTimeLeft * otherPlayer->Speed()/effectiveness;
03500 
03501                         otherPlayer->rubber += rubberToEat;
03502                         if ( otherPlayer->rubber > max )
03503                             otherPlayer->rubber = max; // too much rubber used
03504                         else
03505                             saved = true;              // within bounds, he may survive
03506                     }
03507                 }
03508 
03509                 if ( !saved && sn_GetNetState() != nCLIENT )
03510                 {
03511                     // err, trouble. Can't push the other guy back far enough. Better kill him.
03512                     if ( currentWall )
03513                         otherPlayer->enemyInfluence.AddWall( currentWall->Wall(), lastTime, otherPlayer );
03514                     otherPlayer->distance = wallDist;
03515                     otherPlayer->DropWall();
03516                     otherPlayer->KillAt( collPos );
03517 
03518                     // get rid of future walls
03519                     sg_KillFutureWalls( otherPlayer );
03520                 }
03521             }
03522         }
03523         else // sad but true
03524         {
03525             // this cycle has to die here unless it has rubber left or is invulnerable (checked on catching the exception, and besides, this code path isn't called for invulnerable cycles)
03526             throw gCycleDeath( collPos );
03527 
03528             //                  REAL dist = w->Pos( a );
03529             //                  const_cast<gPlayerWall*>(w)->BlowHole( dist - explosionRadius, dist + explosionRadius );
03530         }
03531     }
03532     else
03533     {
03534         if (bool(player) && sn_GetNetState()!=nCLIENT && Alive() )
03535         {
03536             throw gCycleDeath( collPos );
03537         }
03538 
03539     }
03540 }
03541 
03542 REAL gCycle::PathfindingModifier( const eWall *w ) const
03543 {
03544     if (!w)
03545         return 1;
03546     if (dynamic_cast<const gPlayerWall*>(w))
03547         return .9f;
03548     else
03549         return 1;
03550 }
03551 
03552 
03553 bool gCycle::Act(uActionPlayer *Act, REAL x){
03554     // delegate to joystick
03555     if ( joystick_ && joystick_->Act( Act, x ) )
03556     {
03557         return true;
03558     }
03559 
03560     // don't accept premature input
03561     if (se_mainGameTimer && ( se_mainGameTimer->speed <= 0 || se_mainGameTimer->Time() < -1 ) )
03562         return false;
03563 
03564     if (!Alive() && sn_GetNetState()==nSERVER)
03565         RequestSync(false);
03566 
03567     if(se_turnLeft==*Act && x>.5){
03568         //SendControl(lastTime,&se_turnLeft,1);
03569         Turn(-1);
03570         return true;
03571     }
03572     else if(se_turnRight==*Act && x>.5){
03573         //SendControl(lastTime,&se_turnRight,1);
03574         Turn(1);
03575         return true;
03576     }
03577     else if(s_brake==*Act){
03578         //SendControl(lastTime,&brake,x);
03579         unsigned short newBraking=(x>0);
03580         if ( braking != newBraking )
03581         {
03582             AccelerationDiscontinuity();
03583             braking = newBraking;
03584             AddDestination();
03585         }
03586         return true;
03587     }
03588     else if(s_brakeToggle==*Act){
03589         if ( x > 0 )
03590         {
03591             AccelerationDiscontinuity();
03592             braking = !braking;
03593             AddDestination();
03594         }
03595         return true;
03596     }
03597     return false;
03598 }
03599 
03600 // client side bugfix: network sync messages get actually used
03601 static nVersionFeature sg_SyncsAreUsed( 5 );
03602 
03603 // temporarily override driving directions on wall drops
03604 static eCoord const * sg_fakeDirDrive = NULL;
03605 class gFakeDirDriveSetter
03606 {
03607 public:
03608     gFakeDirDriveSetter( eCoord const & dir )
03609             : lastFakeDir_( sg_fakeDirDrive )
03610     {
03611         sg_fakeDirDrive = &dir;
03612     }
03613 
03614     ~gFakeDirDriveSetter()
03615     {
03616         sg_fakeDirDrive = lastFakeDir_;
03617     }
03618 private:
03619     eCoord const * lastFakeDir_;
03620 };
03621 
03622 bool gCycle::DoTurn(int d)
03623 {
03624 #ifdef DELAYEDTURN_DEBUG
03625     REAL delay = tSysTimeFloat() - sg_turnReceivedTime;
03626     if ( delay > EPS && sn_GetNetState() == nSERVER && Owner() != 0 )
03627     {
03628         con << "Delayed turn execution! " << turns << "\n";
03629     }
03630 #endif
03631 
03632 #ifdef GNUPLOT_DEBUG
03633     if ( sg_gnuplotDebug && Player() )
03634     {
03635         std::ofstream f( Player()->GetUserName() + "_turn", std::ios::app );
03636         f << pos.x << " " << pos.y << "\n";
03637     }
03638 #endif
03639 
03640     if (d >  1) d =  1;
03641     if (d < -1) d = -1;
03642 
03643     if (Alive()){
03644         eSoundMixer* mixer = eSoundMixer::GetMixer();
03645         mixer->PushButton(CYCLE_TURN, Position());
03646 
03647         clientside_action();
03648 
03649         if ( gCycleMovement::DoTurn( d ) )
03650         {
03651             sg_ArchiveCoord( pos, 1 );
03652 
03653             skewDot+=4*d;
03654 
03655             if (sn_GetNetState() == nCLIENT && Owner() == ::sn_myNetID)
03656                 AddDestination();
03657 
03658             if (sn_GetNetState()!=nCLIENT)
03659             {
03660                 RequestSync();
03661             }
03662 
03663             // hack: while dropping the wall, turn around dirDrive.
03664             // this makes FindCurrentFace work better.
03665             {
03666                 FindCurrentFace();
03667                 REAL factor = -16;
03668                 eCoord dirDriveFake = dirDrive * factor;
03669                 eCoord lastDirDriveBack = lastDirDrive;
03670                 lastDirDrive = lastDirDrive * factor;
03671                 gFakeDirDriveSetter fakeSetter( dirDriveFake );
03672                 DropWall();
03673                 lastDirDrive = lastDirDriveBack;
03674             }
03675 
03676             return true;
03677         }
03678     }
03679 
03680     return false;
03681 }
03682 
03683 void gCycle::DropWall( bool buildNew )
03684 {
03685     // keep this cycle alive
03686     tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
03687 
03688     // drop last net wall if it is outdated
03689     if ( lastWall && lastNetWall && lastWall->Time(.5) > lastNetWall->Time(0) )
03690         lastNetWall = 0;
03691 
03692     // update and drop current wall
03693     if(currentWall)
03694     {
03695         lastWall=currentWall;
03696         currentWall->Update(lastTime,pos);
03697         currentWall->CopyIntoGrid( grid );
03698         currentWall=NULL;
03699     }
03700 
03701     if ( buildNew && lastTime >= spawnTime_ + sg_cycleWallTime )
03702         currentWall=new gNetPlayerWall(this,pos,dirDrive,lastTime,distance);
03703 
03704     // grid datastructures change on inserting a wall, better recheck
03705     // all game objects. Temporarily override this cycle's driving direction.
03706     eCoord dirDriveBack = dirDrive;
03707     if ( sg_fakeDirDrive )
03708         dirDrive = *sg_fakeDirDrive;
03709 
03710     if ( grid )
03711     {
03712         for(int i=grid->GameObjects().Len()-1;i>=0;i--)
03713         {
03714             eGameObject * c = grid->GameObjects()(i);
03715             if (c->CurrentFace() && !c->CurrentFace()->IsInGrid() )
03716                 c->FindCurrentFace();
03717         }
03718     }
03719     dirDrive = dirDriveBack;
03720 
03721     // reset flag
03722     dropWallRequested_ = false;
03723 }
03724 
03725 void gCycle::Kill(){
03726     // keep this cycle alive
03727     tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
03728 
03729     if (sn_GetNetState()!=nCLIENT){
03730         RequestSync(true);
03731         if (Alive() && grid && GOID() >= 0 ){
03732             Die( lastTime );
03733             tNEW(gExplosion)(grid, pos,lastTime, color_, this );
03734             //   eEdge::SeethroughHasChanged();
03735 
03736             if ( currentWall )
03737             {
03738                 // z-man: updating the wall so it reflects exactly the position of death looks like
03739                 // a good idea, but unfortunately, the collision position reported from above
03740                 // is inaccurate. It's better not to use it at all, or the cycle's wall will stick out
03741                 // a bit on the other side of the wall it crashed into.
03742 
03743                 // but if prediction was active, do it anyway
03744                 if ( currentWall->Pos(1) > distance || currentWall->Time(1) > lastTime )
03745                     currentWall->Update( lastTime, pos );
03746 
03747                 // copy the wall into the grid, but not directly; the grid datastructures are probably currently traversed. Kill() is called from eGameObject::Move().
03748                 currentWall->CopyIntoGrid( 0 );
03749 
03750                 currentWall = NULL;
03751             }
03752         }
03753     }
03754     // z-man: another stupid idea. Why would we need a destination when we're dead?
03755     //    else if (Owner() == ::sn_myNetID)
03756     //        AddDestination();
03757     /*
03758       else if (owner!=::sn_myNetID)
03759       speed=-.01;
03760     */
03761 }
03762 
03763 /*
03764 void gCycle::Turbo(bool turbo){
03765     if (turbo && speed<30){
03766         speed=40;
03767         Turn(0);
03768     }
03769 
03770     if (!turbo && speed>=30){
03771         speed=20;
03772         Turn(0);
03773     }
03774 }
03775 */
03776 
03777 static rFileTexture cycle_shad(rTextureGroups::TEX_FLOOR,"textures/shadow.png",0,0,true);
03778 /*
03779 static tString lala_cycle_shad("Anonymous/original/textures/shadow.png");
03780 static nSettingItem<tString> lalala_cycle_shad("TEXTURE_CYCLE_SHADOW", lala_cycle_shad);
03781 rFileTexture cycle_shad(rTextureGroups::TEX_FLOOR, lala_cycle_shad, 0,0,true);
03782 */
03783 
03784 //#define ENABLE_OLD_LAG_O_METER
03785 
03786 REAL sg_laggometerScale=1;
03787 static tSettingItem< REAL > sg_laggometerScaleConf( "LAG_O_METER_SCALE", sg_laggometerScale );
03788 REAL sg_laggometerThreshold=.5;
03789 static tSettingItem< REAL > sg_laggometerThresholdConf( "LAG_O_METER_THRESHOLD", sg_laggometerThreshold );
03790 REAL sg_laggometerBlend=.5;
03791 static tSettingItem< REAL > sg_laggometerBlendConf( "LAG_O_METER_BLEND", sg_laggometerBlend );
03792 #ifdef ENABLE_OLD_LAG_O_METER
03793 bool sg_laggometerUseOld=false;
03794 static tSettingItem< bool > sg_laggometerUseOldConf( "LAG_O_METER_USE_OLD", sg_laggometerUseOld );
03795 #endif
03796 bool sg_axesIndicator=false;
03797 
03798 int sg_blinkFrequency=10;
03799 static tSettingItem< int > sg_blinkFrequencyConf( "CYCLE_BLINK_FREQUENCY", sg_blinkFrequency );
03800 
03801 #ifndef DEDICATED
03802 // put meriton's classes into a namespace so they can't possibly conflict with other code, especially the Colour class. --wrtl
03803 namespace gLaggometer {
03804 class Colour {
03805 public:
03806     REAL cp[3];
03807     Colour(REAL r, REAL g, REAL b) {
03808         cp[0]=r;
03809         cp[1]=g;
03810         cp[2]=b;
03811     }
03812     Colour(ePlayerNetID* player) {
03813         if ( player )
03814         {
03815             player->Color(cp[0], cp[1], cp[2]);
03816         }
03817         else
03818         {
03819             cp[0]=cp[1]=cp[2]=1;
03820         }
03821     }
03822     void blend(REAL factor, const Colour& target) {
03823         for (int i=0; i<3; i++) {
03824             cp[i] = (1 - factor) * cp[i] + factor * target.cp[i];
03825         }
03826     }
03827     void toGl() const { glColor3f(cp[0], cp[1], cp[2]); }
03828 
03829     static const Colour white;
03830     static const Colour black;
03831 };
03832 
03833 const Colour Colour::white(1,1,1);
03834 const Colour Colour::black(0,0,0);
03835 
03836 class DirectionTransformer {
03837 private:
03838     eGrid* grid;
03839     eCoord factor;
03840 public:
03841     DirectionTransformer(eGrid* theGrid) : grid(theGrid), factor(theGrid->GetDirection(0).Conj()) { }
03842     eCoord get(int i) {
03843         return grid->GetDirection(i).Turn(factor);
03844 
03845     }
03846     int ahead() { return 2 * (grid->WindingNumber()); }
03847 };
03848 
03849 class LagOMeterRenderer {
03850 private:
03851     DirectionTransformer directions;
03852     REAL delay;
03853     Colour color;
03854 protected:
03855     bool drawTriangle(eCoord loc, int winding, REAL lag, int inc);
03856 public:
03857     LagOMeterRenderer(gCycle* cycle) :
03858             directions(cycle->Grid()),
03859             delay(cycle->GetTurnDelay()),
03860             color(cycle->Player())
03861     {
03862         color.blend(sg_laggometerBlend, Colour::white);
03863     }
03864     void render(REAL lag);
03865 };
03866 
03868 bool LagOMeterRenderer::drawTriangle(eCoord loc, int winding, REAL lag, int inc) {
03869     eCoord outer = loc + directions.get(winding) * lag;
03870     if (outer.y * inc > 0.01f) {
03871         eCoord oldOuter = loc + directions.get(winding - inc) * lag;
03872         eCoord d = outer - oldOuter;
03873         outer = oldOuter + d * (-oldOuter.y / d.y);
03874         glVertex2f(outer.x, outer.y);
03875         return true;
03876     } else {
03877         glVertex2f(outer.x, outer.y);
03878         if (lag > delay) {
03879             if (drawTriangle(loc + directions.get(winding + inc) * delay, winding + inc, lag - delay, inc)) return true;
03880         } else {
03881             outer = loc + directions.get(winding + inc) * lag;
03882             glVertex2f(outer.x, outer.y);
03883         }
03884         glVertex2f(loc.x, loc.y);
03885         return false;
03886     }
03887 }
03888 
03889 void LagOMeterRenderer::render(REAL lag) {
03890     color.toGl();
03891     BeginLineStrip();
03892     drawTriangle(eCoord(0,0), directions.ahead(), lag, 1);
03893     RenderEnd();
03894 
03895     BeginLineStrip();
03896     drawTriangle(eCoord(0,0), directions.ahead(), lag, -1);
03897     RenderEnd();
03898 }
03899 
03900 
03901 class AxesIndicator {
03902 private:
03903     DirectionTransformer directions;
03904     Colour color;
03905 public:
03906     AxesIndicator(gCycle* cycle) :
03907             directions(cycle->Grid()),
03908             color(cycle->Player())
03909     {
03910         color.blend(.5f, Colour::white);
03911     }
03912     void line(int i) {
03913         eCoord midle = directions.get(directions.ahead() + i) * .1f;
03914         eCoord inner = midle * .5f;
03915         eCoord outer = midle + directions.get(directions.ahead() + 2 * i) * .05f;
03916 
03917         BeginLineStrip();
03918         //Colour::black.toGl();
03919         color.toGl();
03920         glVertex2f(inner.x, inner.y);
03921 
03922 
03923         glVertex2f(midle.x, midle.y);
03924 
03925         Colour::black.toGl();
03926         glVertex2f(outer.x, outer.y);
03927         RenderEnd();
03928     }
03929     void render() {
03930         //return; // disable, for now
03931         glShadeModel(GL_SMOOTH);
03932         line(-1);
03933         line(0);
03934         line(1);
03935     }
03936 };
03937 
03938 }
03939 
03940 static REAL mp_eWall_stretch=4;
03941 static tSettingItem<REAL> mpws
03942 ("MOVIEPACK_WALL_STRETCH",mp_eWall_stretch);
03943 
03944 static rFileTexture dir_eWall(rTextureGroups::TEX_WALL,"textures/dir_wall.png",1,0,1);
03945 static rFileTexture dir_eWall_moviepack(rTextureGroups::TEX_WALL,"moviepack/dir_wall.png",1,0,1);
03946 
03947 static void dir_eWall_select()
03948 {
03949     if (sg_MoviePack()){
03950         TexMatrix();
03951         IdentityMatrix();
03952         ScaleMatrix(1/mp_eWall_stretch,1,1);
03953         dir_eWall_moviepack.Select();
03954     }
03955     else
03956     {
03957         dir_eWall.Select();
03958     }
03959 }
03960 
03961 gCycleWallsDisplayListManager::gCycleWallsDisplayListManager()
03962     : wallList_(0)
03963     , wallsWithDisplayList_(0)
03964     , wallsWithDisplayListMinDistance_(0)
03965     , wallsInDisplayList_(0)
03966 {
03967 }
03968 
03969 bool gCycleWallsDisplayListManager::CannotHaveList( REAL distance, gCycle const * cycle )
03970 {
03971     return
03972             ( !cycle->Alive() && gCycle::WallsStayUpDelay() >= 0 && se_GameTime()-cycle->DeathTime()-gCycle::WallsStayUpDelay() > 0 ) 
03973 
03974             ||
03975 
03976             ( cycle->ThisWallsLength() > 0 && cycle->GetDistance() - cycle->ThisWallsLength() > distance );
03977 }
03978 
03979 void gCycleWallsDisplayListManager::RenderAll( eCamera const * camera, gCycle * cycle )
03980 {
03981     dir_eWall_select();
03982 
03983     glDisable(GL_CULL_FACE);
03984     
03985     gNetPlayerWall * run = 0;
03986     // transfer walls with display list into their list
03987 
03988     int wallsWithPossibleDisplayList = 0;
03989     run = wallList_;
03990     while( run )
03991     {
03992         gNetPlayerWall * next = run->Next();
03993         if ( run->CanHaveDisplayList() )
03994         {
03995             wallsWithPossibleDisplayList++;
03996         }
03997         else
03998         {
03999             // wall has expired, remove it
04000             if ( cycle->ThisWallsLength() > 0 && cycle->GetDistance() - cycle->MaxWallsLength() > run->EndPos() )
04001                 
04002             {
04003                 run->Remove();
04004             }
04005             else
04006             {
04007                 run->Render( camera );
04008             }
04009         }
04010         run = next;
04011     }
04012 
04013     // clear display list if needed
04014     bool tailExpired=false;
04015     if ( CannotHaveList( wallsWithDisplayListMinDistance_, cycle ) )
04016     {
04017         tailExpired=true;
04018         displayList_.Clear(0);
04019     }
04020     // check if enough new walls are present to warrant altering the display list
04021     else if ( wallsWithPossibleDisplayList >= 3 ||
04022          wallsWithPossibleDisplayList * 5 > wallsInDisplayList_ )
04023     {
04024         // yes? Ok, rebuild the list in this case, too
04025         displayList_.Clear(0);
04026     }
04027     else if ( wallsWithPossibleDisplayList )
04028     {
04029         // oops, at least render the newcomers normally
04030         run = wallList_;
04031         while( run )
04032         {
04033             gNetPlayerWall * next = run->Next();
04034             if ( run->CanHaveDisplayList() )
04035             {
04036                 run->Render( camera );
04037             }
04038 
04039             run = next;
04040         }
04041     }
04042 
04043     // call display list
04044     if ( displayList_.Call() )
04045     {
04046         return;
04047     }
04048 
04049     // remove and render walls without display list
04050     run = wallsWithDisplayList_;
04051     while( run )
04052     {
04053         gNetPlayerWall * next = run->Next();
04054         if ( !run->CanHaveDisplayList() || ( tailExpired && wallsWithDisplayListMinDistance_ >= run->BegPos() ) )
04055         {
04056             run->Render( camera );
04057             run->Insert( wallList_ );
04058         }
04059         run = next;
04060     }
04061 
04062     if ( wallsWithPossibleDisplayList > 0 )
04063     {
04064         run = wallList_;
04065         while( run )
04066         {
04067             gNetPlayerWall * next = run->Next();
04068             if ( run->CanHaveDisplayList() )
04069             {
04070                 run->Insert( wallsWithDisplayList_ );
04071             
04072                 // clear the wall's own display list, it will no longer be needed
04073                 run->ClearDisplayList(0, -1);
04074             }
04075         
04076             run = next;
04077         }
04078     }
04079 
04080     if ( !wallsWithDisplayList_ )
04081     {
04082         return;
04083     }
04084 
04085     // fill display list
04086     rDisplayListFiller filler( displayList_ );
04087 
04088     if ( !rDisplayList::IsRecording() )
04089     {
04090         // display list recording did not start; render traditionally
04091         run = wallsWithDisplayList_;
04092         while( run )
04093         {   
04094             gNetPlayerWall * next = run->Next();
04095             run->Render( camera );
04096             run = next;
04097         }
04098 
04099         return;
04100     }
04101 
04102     wallsWithDisplayListMinDistance_ = 1E+30;
04103     wallsInDisplayList_ = 0;
04104 
04105     // render walls;
04106     // first, render all lines
04107     sr_DepthOffset(true);
04108     if ( rTextureGroups::TextureMode[rTextureGroups::TEX_WALL] != 0 )
04109         glDisable(GL_TEXTURE_2D);
04110     
04111     run = wallsWithDisplayList_;
04112     while( run )
04113     {
04114         gNetPlayerWall * next = run->Next();
04115         if ( run->BegPos() < wallsWithDisplayListMinDistance_ )
04116         {
04117             wallsWithDisplayListMinDistance_ = run->BegPos();
04118         }
04119 
04120         wallsInDisplayList_++;
04121 
04122         run->RenderList( true, gNetPlayerWall::gWallRenderMode_Lines );
04123         run = next;
04124     }
04125 
04126     RenderEnd();
04127     sr_DepthOffset(false);
04128     if ( rTextureGroups::TextureMode[rTextureGroups::TEX_WALL] != 0 )
04129         glEnable(GL_TEXTURE_2D);
04130     
04131     run = wallsWithDisplayList_;
04132     while( run )
04133     {
04134         gNetPlayerWall * next = run->Next();
04135         run->RenderList( true, gNetPlayerWall::gWallRenderMode_Quads );
04136         run = next;
04137     }
04138 
04139     RenderEnd();
04140 }
04141 
04142 void gCycle::Render(const eCamera *cam){
04143     // are we blinking from invulnerability?
04144     bool blinking = false;
04145     if ( lastTime > spawnTime_ && !Vulnerable() )
04146     {
04147         double time = tSysTimeFloat();
04148         double wrap = time - floor(time);
04149         int pulse = int ( 2 * wrap * sg_blinkFrequency );
04150         blinking = pulse & 1;
04151     }
04152 
04153 #ifdef USE_HEADLIGHT
04154 #ifdef LINUX
04155     typedef void (*glProgramStringARB_Func)(GLenum, GLenum, GLsizei, const void*);
04156     glProgramStringARB_Func glProgramStringARB_ptr = 0;
04157 
04158     typedef void (*glProgramLocalParameter4fARB_Func)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
04159     glProgramLocalParameter4fARB_Func glProgramLocalParameter4fARB_ptr = 0;
04160 
04161     glProgramStringARB_ptr = (glProgramStringARB_Func) SDL_GL_GetProcAddress("glProgramStringARB");
04162     glProgramLocalParameter4fARB_ptr = (glProgramLocalParameter4fARB_Func) SDL_GL_GetProcAddress("glProgramLocalParameter4fARB");
04163 #endif
04164 #endif    
04165     if (!finite(z) || !finite(pos.x) ||!finite(pos.y)||!finite(dir.x)||!finite(dir.y)
04166             || !finite(skew))
04167         st_Breakpoint();
04168     if (Alive()){
04169         //con << "Drawing cycle at " << pos << '\n';
04170 
04171 #ifdef DEBUG
04172         /*     {
04173                    gDestination *l = destinationList;
04174                    glDisable(GL_LIGHTING);
04175                    glColor3f(1,1,1);
04176                    while(l){
04177                    if (l == currentDestination)
04178                    glColor3f(0,1,0);
04179 
04180                    glBegin(GL_LINES);
04181                    glVertex3f(l->position.x, l->position.y, 0);
04182                    glVertex3f(l->position.x, l->position.y, 100);
04183                    glEnd();
04184 
04185                    if (l == currentDestination)
04186                    glColor3f(0,1,1);
04187 
04188                    l=l->next;
04189                    }
04190                    } */
04191 #endif
04192 
04193         GLfloat color[4]={1,1,1,1};
04194         static GLfloat lposa[4] = { 320, 240, 200,0};
04195         static GLfloat lposb[4] = { -240, -100, 200,0};
04196         static GLfloat lighta[4] = { 1, .7, .7, 1 };
04197         static GLfloat lightb[4] = { .7, .7, 1, 1 };
04198 
04199         glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,color);
04200         glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color);
04201 
04202         glLightfv(GL_LIGHT0, GL_DIFFUSE, lighta);
04203         glLightfv(GL_LIGHT0, GL_SPECULAR, lighta);
04204         glLightfv(GL_LIGHT0, GL_POSITION, lposa);
04205         glLightfv(GL_LIGHT1, GL_DIFFUSE, lightb);
04206         glLightfv(GL_LIGHT1, GL_SPECULAR, lightb);
04207         glLightfv(GL_LIGHT1, GL_POSITION, lposb);
04208 
04209 
04210         ModelMatrix();
04211         glPushMatrix();
04212         eCoord p = PredictPosition();
04213         glTranslatef(p.x,p.y,0);
04214         glScalef(.5f,.5f,.5f);
04215 
04216 
04217         eCoord ske(1,skew);
04218         ske=ske*(1/sqrt(ske.NormSquared()));
04219 
04220         GLfloat m[4][4]={{dir.x,dir.y,0,0},
04221                          {-dir.y,dir.x,0,0},
04222                          {0,0,1,0},
04223                          {0,0,0,1}};
04224         glMultMatrixf(&m[0][0]);
04225 
04226         glPushMatrix();
04227         //glTranslatef(-1.84,0,0);
04228         if (!mp)
04229             glTranslatef(-1.5,0,0);
04230 
04231         glPushMatrix();
04232 
04233         GLfloat sk[4][4]={{1,0,0,0},
04234                           {0,ske.x,ske.y,0},
04235                           {0,-ske.y,ske.x,0},
04236                           {0,0,0,1}};
04237 
04238         glMultMatrixf(&sk[0][0]);
04239 
04240 
04241         glEnable(GL_LIGHT0);
04242         glEnable(GL_LIGHT1);
04243         glEnable(GL_LIGHTING);
04244 
04245 
04246 
04247         TexMatrix();
04248         IdentityMatrix();
04249 
04250         if (mp){
04251 
04252             ModelMatrix();
04253             if ( !blinking )
04254             {
04255                 glPushMatrix();
04256                 customTexture->Select();
04257                 glColor3f(1,1,1);
04258                 customModel->Render();
04259                 glPopMatrix();
04260             }
04261 
04262             glPopMatrix();
04263             glTranslatef(-1.5,0,0);
04264         }
04265         else{
04266             glEnable(GL_TEXTURE_2D);
04267 
04268             ModelMatrix();
04269 
04270             if ( !blinking )
04271             {
04272                 bodyTex->Select();
04273                 body->Render();
04274 
04275                 wheelTex->Select();
04276                 
04277                 glPushMatrix();
04278                 glTranslatef(0,0,.73);
04279                 
04280                 GLfloat mr[4][4]={{rotationRearWheel.x,0,rotationRearWheel.y,0},
04281                                   {0,1,0,0},
04282                                   {-rotationRearWheel.y,0,rotationRearWheel.x,0},
04283                                   {0,0,0,1}};
04284                 
04285                 
04286                 glMultMatrixf(&mr[0][0]);
04287                 
04288                 rear->Render();
04289                 glPopMatrix();
04290 
04291                 glPushMatrix();
04292                 glTranslatef(1.84,0,.43);
04293 
04294                 GLfloat mf[4][4]={{rotationFrontWheel.x,0,rotationFrontWheel.y,0},
04295                                   {0,1,0,0},
04296                                   {-rotationFrontWheel.y,0,rotationFrontWheel.x,0},
04297                                   {0,0,0,1}};
04298                 
04299                 glMultMatrixf(&mf[0][0]);
04300 
04301                 front->Render();
04302                 glPopMatrix();
04303             }
04304             
04305             glPopMatrix();
04306         }
04307 
04308 
04309         //     TexMatrix();
04310         //     IdentityMatrix();
04311         ModelMatrix();
04312 
04313         /*
04314           glDisable(GL_TEXTURE_GEN_S);
04315           glDisable(GL_TEXTURE_GEN_T);
04316           glDisable(GL_TEXTURE_GEN_Q);
04317           glDisable(GL_TEXTURE_GEN_R);
04318         */
04319 
04320         glDisable(GL_LIGHT0);
04321         glDisable(GL_LIGHT1);
04322         glDisable(GL_LIGHTING);
04323 
04324         //glDisable(GL_TEXTURE);
04325         glDisable(GL_TEXTURE_2D);
04326         glColor3f(1,1,1);
04327 
04328         {
04329             bool renderPyramid = false;
04330             gRealColor colorPyramid;
04331             REAL alpha = 1;
04332             const REAL timeout = .5f;
04333 
04334             if ( bool(player) )
04335             {
04336                 if ( player->IsChatting() )
04337                 {
04338                     renderPyramid = true;
04339                     colorPyramid.b = 0.0f;
04340                 }
04341                 else if ( !player->IsActive() )
04342                 {
04343                     renderPyramid = true;
04344                     colorPyramid.b = 0.0f;
04345                     colorPyramid.g = 0.0f;
04346                 }
04347                 else if ( cam && cam->Center() == this && se_GameTime() < timeout && player->CurrentTeam() && player->CurrentTeam()->NumPlayers() > 1 )
04348                 {
04349                     renderPyramid = true;
04350                     alpha = timeout - se_GameTime();
04351                 }
04352             }
04353 
04354             if ( renderPyramid )
04355             {
04356                 GLfloat s=sin(lastTime);
04357                 GLfloat c=cos(lastTime);
04358 
04359                 GLfloat m[4][4]={{c,s,0,0},
04360                                  {-s,c,0,0},
04361                                  {0,0,1,0},
04362                                  {0,0,1,1}};
04363 
04364                 glPushMatrix();
04365 
04366                 glMultMatrixf(&m[0][0]);
04367                 glScalef(.5,.5,.5);
04368 
04369 
04370                 BeginTriangles();
04371 
04372                 glColor4f( colorPyramid.r,colorPyramid.g,colorPyramid.b, alpha );
04373                 glVertex3f(0,0,3);
04374                 glVertex3f(0,1,4.5);
04375                 glVertex3f(0,-1,4.5);
04376 
04377                 glColor4f( colorPyramid.r * .7f,colorPyramid.g * .7f,colorPyramid.b * .7f, alpha );
04378                 glVertex3f(0,0,3);
04379                 glVertex3f(1,0,4.5);
04380                 glVertex3f(-1,0,4.5);
04381 
04382                 RenderEnd();
04383 
04384                 glPopMatrix();
04385             }
04386         }
04387 
04388 #ifdef USE_HEADLIGHT
04389         // Headlight contributed by Jonathan
04390         if(headlights) {
04391             if(!cycleprograminited) { // set to false on every sr_InitDisplay, without it I lost my program when I switched to windowed
04392                 const char *program =
04393                     "!!ARBfp1.0\
04394                     \
04395                     PARAM normal = program.local[0];\
04396                     ATTRIB texcoord = fragment.texcoord;\
04397                     TEMP final, diffuse, distance;\
04398                     \
04399                     DP3 distance, texcoord, texcoord;\
04400                     RSQ diffuse, distance.w;\
04401                     RCP distance, distance.w;\
04402                     MUL diffuse, texcoord, diffuse;\
04403                     DP3 diffuse, diffuse, normal;\
04404                     MUL final, diffuse, distance;\
04405                     MOV result.color.w, fragment.color;\
04406                     MUL result.color.xyz, fragment.color, final;\
04407                     \
04408                     END";
04409 #ifdef LINUX
04410                 glProgramStringARB_ptr(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(program), program);
04411 #else
04412                 glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(program), program);
04413 #endif
04414                 cycleprograminited = true;
04415             }
04416 #ifdef LINUX
04417             glProgramLocalParameter4fARB_ptr(GL_FRAGMENT_PROGRAM_ARB, 0, 0, 0, verletSpeed_ * verletSpeed_, 0);
04418 #else                   
04419             glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, 0, verletSpeed_ * verletSpeed_, 0);
04420 #endif
04421             glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // blend func and depth mask. Efficient or not, glPushAttrib/glPopAttrib is a quick way to manage state.
04422             glEnable(GL_FRAGMENT_PROGRAM_ARB); // doesn't check if it exists...
04423 
04424             const unsigned sensors = 32; // actually one more
04425             const double mul = 0.25 * M_PI / sensors;
04426             const double add = -0.125 * M_PI;
04427 
04428             double size = gArena::SizeMultiplier() * 500 * M_SQRT2; // is M_SQRT2 in your math.h?
04429             GLfloat array[sensors+2][5];
04430 
04431             array[0][0] = 0;
04432             array[0][1] = 0;
04433             array[0][2] = p.x;
04434             array[0][3] = p.y;
04435             array[0][4] = 0.125;
04436 
04437             for(unsigned i=0; i<=sensors; i++) {
04438                 gSensor sensor(this, p, dir.Turn(cos(i * mul + add), sin(i * mul + add)));
04439                 sensor.detect(size);
04440                 array[i][5] = sensor.before_hit.x - p.x;
04441                 array[i][6] = sensor.before_hit.y - p.y;
04442                 array[i][7] = sensor.before_hit.x;
04443                 array[i][8] = sensor.before_hit.y;
04444                 array[i][9] = 0.125;
04445             }
04446 
04447             glPushMatrix();
04448             glLoadIdentity();
04449 
04450             glMatrixMode(GL_TEXTURE);
04451             glPushMatrix();
04452             glTranslatef(0, 0, 1);
04453 
04454             glBlendFunc(GL_ONE, GL_ONE);
04455             glDepthMask(GL_FALSE);
04456 
04457             glColor3fv(reinterpret_cast<GLfloat *>(&color_)); // 8-)
04458             glEnableClientState(GL_VERTEX_ARRAY);
04459             glEnableClientState(GL_TEXTURE_COORD_ARRAY);
04460 
04461             glInterleavedArrays(GL_T2F_V3F, 0, array);
04462             glDrawArrays(GL_TRIANGLE_FAN, 0, sensors+2);
04463 
04464             glDisableClientState(GL_VERTEX_ARRAY);
04465             glDisableClientState(GL_TEXTURE_COORD_ARRAY);
04466 
04467             glDisable(GL_FRAGMENT_PROGRAM_ARB);
04468 
04469             glPopMatrix();
04470             glMatrixMode(GL_MODELVIEW);
04471 
04472             glPopMatrix();
04473             glPopAttrib();
04474         }
04475 #endif // USE_HEADLIGHT
04476         // Name
04477         RenderName( cam );
04478 
04479 
04480         // shadow:
04481 
04482         sr_DepthOffset(true);
04483 
04484 
04485         REAL h=0;//se_cameraZ*.005+.03;
04486 
04487         glEnable(GL_CULL_FACE);
04488 
04489         if(!blinking && sr_floorDetail>rFLOOR_GRID && rTextureGroups::TextureMode[rTextureGroups::TEX_FLOOR]>0 && sr_alphaBlend){
04490             glColor3f(0,0,0);
04491             cycle_shad.Select();
04492             BeginQuads();
04493             glTexCoord2f(0,1);
04494             glVertex3f(-.6,.4,h);
04495 
04496             glTexCoord2f(1,1);
04497             glVertex3f(-.6,-.4,h);
04498 
04499             glTexCoord2f(1,0);
04500             glVertex3f(2.1,-.4,h);
04501 
04502             glTexCoord2f(0,0);
04503             glVertex3f(2.1,.4,h);
04504 
04505             RenderEnd();
04506         }
04507 
04508         glDisable(GL_CULL_FACE);
04509 
04510         // sr_laggometer;
04511 
04512 
04513         REAL f=verletSpeed_;
04514 
04515         REAL l=Lag();
04516 
04517         glPopMatrix();
04518 
04519         h=cam->CameraZ()*.005+.03;
04520 
04521 #ifdef ENABLE_OLD_LAG_O_METER
04522         if(sg_laggometerUseOld) {
04523             if (sn_GetNetState() != nSTANDALONE && sr_laggometer && f*l>.5) {
04524                 //&& owner!=::sn_myNetID){
04525                 glPushMatrix();
04526 
04527                 glColor3f(1,1,1);
04528                 //glDisable(GL_TEXTURE);
04529                 glDisable(GL_TEXTURE_2D);
04530 
04531                 glTranslatef(0,0,h);
04532                 //glScalef(.5*f,.5*f,.5*f);
04533 
04534                 // compensate for the .5 scaling further up
04535                 f *= 2 * sg_laggometerScale;
04536 
04537                 glScalef(f,f,f);
04538 
04539                 // move the sr_laggometer ahead a bit
04540                 if (!sr_predictObjects || sn_GetNetState()==nSERVER)
04541                     glTranslatef(l,0,0);
04542 
04543 
04544                 BeginLineLoop();
04545 
04546 
04547                 glVertex2f(-l,-l);
04548                 glVertex2f(0,0);
04549                 glVertex2f(-l,l);
04550                 REAL delay = GetTurnDelay();
04551                 if(l> 2*delay){
04552                     glVertex2f(-2*l+delay,delay);
04553                     glVertex2f(-2*l+2*delay,0);
04554                     glVertex2f(-2*l+delay,-delay);
04555                 }
04556                 else if (l>delay){
04557                     glVertex2f(-2*l+delay,delay);
04558                     glVertex2f(-l,2*delay-l);
04559                     glVertex2f(-l,-(2*delay-l));
04560                     glVertex2f(-2*l+delay,-delay);
04561                 }
04562 
04563                 RenderEnd();
04564                 glPopMatrix();
04565             }
04566         } else
04567 #endif
04568         {
04569             glPushMatrix();
04570 
04571             //glDisable(GL_TEXTURE);
04572             glDisable(GL_TEXTURE_2D);
04573 
04574             glTranslatef(0,0,h);
04575             //glScalef(.5*f,.5*f,.5*f);
04576 
04577             // compensate for the .5 scaling further up
04578             f *= 2 * sg_laggometerScale;
04579 
04580             glScalef(f,f,f);
04581 
04582             // move the sr_laggometer back a bit
04583             if (sr_predictObjects || sn_GetNetState()==nSERVER) {
04584                 glTranslatef(-l,0,0);
04585             }
04586 
04587             if (f*l>sg_laggometerThreshold) {
04588                 if (sr_laggometer) {
04589                     gLaggometer::LagOMeterRenderer(this).render(l);
04590                 }
04591             } else if(sg_axesIndicator) {
04592                 gLaggometer::AxesIndicator(this).render();
04593             }
04594 
04595             glPopMatrix();
04596         }
04597         sr_DepthOffset(false);
04598 
04599         glPopMatrix();
04600 
04601     }
04602 }
04603 
04604 void gCycle::Render2D(tCoord scale) const {
04605     double alpha = 1;
04606     if(!Alive()) {
04607         alpha -= 2 * (se_GameTime() - DeathTime());
04608         if(alpha <= 0) return;
04609     }
04610     glColor4f(color_.r, color_.g, color_.b, alpha);
04611     eCoord pos = PredictPosition(), dir = Direction();
04612     tCoord p = pos;
04613     glPushMatrix();
04614     GLfloat m[16] = {
04615                         scale.x * dir.x, scale.y * dir.y, 0, 0,
04616                         -scale.x * dir.y, scale.y * dir.x, 0, 0,
04617                         0, 0, 1, 0,
04618                         pos.x, pos.y, 0, 1
04619                     };
04620     glMultMatrixf(m);
04621     glBegin(GL_TRIANGLES);
04622     glVertex2f(.5, 0);
04623     glVertex2f(-.5, .5);
04624     glVertex2f(-.5, -.5);
04625     glEnd();
04626     glPopMatrix();
04627 }
04628 
04629 static REAL fadeOutNameAfter = 5.0f;    /* 0: never show, < 0 always show */
04630 //static int fadeOutNameMode = 1;                       // 0: never show, 1: show for fadeOutNameAfter, 2: always show
04631 static bool showOwnName = 0;                    // show name on own cycle?
04632 
04633 static tSettingItem< bool > sg_showOwnName( "SHOW_OWN_NAME", showOwnName );
04634 //static tSettingItem< int > sg_fadeOutNameMode( "FADEOUT_NAME_MODE", showOwnName )
04635 static tSettingItem< REAL > sg_fadeOutNameAfter( "FADEOUT_NAME_DELAY", fadeOutNameAfter );
04636 
04637 static bool sg_displayColoredNameOverCycles = true;                     // show colored names on cycles?
04638 
04639 static tSettingItem< bool > sg_displayColoredNameOverCyclesCfg( "DISPLAY_COLORED_NAMES_OVER_CYCLES", sg_displayColoredNameOverCycles );
04640 
04641 
04642 void gCycle::RenderName( const eCamera* cam ) {
04643     if ( !this->Player() )
04644         return;
04645 
04646     float modelviewMatrix[16], projectionMatrix[16];
04647     float x, y, z, w;
04648     float xp, yp, wp;
04649     float alpha = 0.75;
04650 
04651     if (fadeOutNameAfter == 0) return; /* XXX put that in ::Render() */
04652     if ( !cam->RenderingMain() ) return; // no name in mirrored image
04653     if ( !showOwnName && cam->Player() == this->player ) return; // don't show own name
04654 
04655     glPushMatrix();
04656     /* position sign above center of cycle */
04657     glTranslatef(0.8, 0, 2.0);
04658     glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
04659     glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);
04660     glPopMatrix();
04661 
04662     /* get coordinates of sign */
04663     x = modelviewMatrix[12];
04664     y = modelviewMatrix[13];
04665     z = modelviewMatrix[14];
04666     w = modelviewMatrix[15];
04667 
04668     /* multiply by projection matrix */
04669     xp = projectionMatrix[0] * x + projectionMatrix[4] * y +
04670          projectionMatrix[8] * z + projectionMatrix[12] * w;
04671     yp = projectionMatrix[1] * x + projectionMatrix[5] * y +
04672          projectionMatrix[9] * z + projectionMatrix[13] * w;
04673     wp = projectionMatrix[3] * x + projectionMatrix[7] * y +
04674          projectionMatrix[11] * z + projectionMatrix[15] * w;
04675 
04676     if (wp <= 0) {
04677         /* behind camera */
04678         timeCameIntoView = 0;
04679         return;
04680     }
04681 
04682     xp /= wp;
04683     yp /= wp;
04684     yp += rCHEIGHT_NORMAL;// * 2.0;
04685 
04686     if (xp <= -1 || xp >= 1 || yp <= -1 || yp >= 1) {
04687         /* out of screen */
04688         timeCameIntoView = 0;
04689         return;
04690     }
04691 
04692     /* it is visible */
04693 
04694     bool doname = true;
04695     if (fadeOutNameAfter > 0) {
04696         REAL now = tSysTimeFloat();
04697         if (timeCameIntoView == 0)
04698             timeCameIntoView = now;
04699 
04700         if (now - timeCameIntoView > fadeOutNameAfter) {
04701             doname = false;
04702         } else if (now - timeCameIntoView > fadeOutNameAfter - 1) {
04703             /* start to fade out */
04704             alpha = 0.75 - (now - timeCameIntoView -
04705                             (fadeOutNameAfter - 1)) * 0.75;
04706         }
04707     }
04708 
04709     ModelMatrix();
04710     glPushMatrix();
04711     glLoadIdentity();
04712 
04713     ProjMatrix();
04714     glPushMatrix();
04715     glLoadIdentity();
04716 
04717     glTranslatef(xp, yp, 0.);
04718     if(doname) {
04719         glColor4f(1, 1, 1, alpha);
04720         tColoredString name;
04721         if(sg_displayColoredNameOverCycles)
04722             name << *this->player;
04723         else
04724             name << this->player->GetName();
04725         DisplayText(0, 0, rCHEIGHT_NORMAL, name, sr_fontCycleLabel, 0, 0);
04726     }
04727     static cCockpit cycleCockpit(cCockpit::VIEWPORT_CYCLE);
04728     cycleCockpit.SetCycle(*this);
04729     cycleCockpit.Render();
04730 
04731     ProjMatrix();
04732     glPopMatrix();
04733 
04734     ModelMatrix();
04735     glPopMatrix();
04736 }
04737 
04738 
04739 bool gCycle::RenderCockpitFixedBefore(bool){
04740     /*
04741       if (alive)
04742       return true;
04743       else{
04744       REAL rd=se_GameTime()-deathTime;
04745       if (rd<1)
04746          return true;
04747       else{
04748          REAL d=1.25-rd;
04749          d*=8;
04750          if (d<0) d=0;
04751          glColor3f(d,d/2,d/4);
04752          glDisable(GL_TEXTURE_2D);
04753          glDisable(GL_TEXTURE);
04754          glDisable(GL_DEPTH_TEST);
04755          glRectf(-1,-1,1,1);
04756          glColor4f(1,1,1,rd*(2-rd/2));
04757          DisplayText(0,0,.05,.15,"You have been deleted.");
04758          return false;
04759       }
04760       }
04761     */
04762     return true;
04763 }
04764 #endif
04765 
04766 #if 0
04767 void gCycle::SoundMix(Uint8 *dest,unsigned int len,
04768                       int viewer,REAL rvol,REAL lvol){
04769 #ifndef HAdVE_LIBSDL_MIXER
04770     if (Alive()){
04771         /*
04772           if (!cycle_run.alt){
04773           rvol*=4;
04774           lvol*=4;
04775           }
04776         */
04777 
04778         if (engine)
04779             engine->Mix(dest,len,viewer,rvol,lvol,verletSpeed_/(sg_speedCycleSound * SpeedMultiplier()));
04780 
04781         if (turning)
04782             if (turn_wav.alt)
04783                 turning->Mix(dest,len,viewer,rvol,lvol,5);
04784             else
04785                 turning->Mix(dest,len,viewer,rvol,lvol,1);
04786 
04787         if (spark)
04788             spark->Mix(dest,len,viewer,rvol*.5,lvol*.5,4);
04789     }
04790 #endif
04791 }
04792 #endif
04793 
04794 eCoord gCycle::PredictPosition() const {
04795     return predictPosition_;
04796 
04797     //    eCoord p = pos + dir * (speed * se_PredictTime());
04798     //    return p + correctPosSmooth;
04799 }
04800 
04801 eCoord gCycle::CamPos() const
04802 {
04803     return PredictPosition() + dir.Turn(0 ,-skew*z);
04804 
04805     //    gSensor s(this,pos, PredictPosition() - pos );
04806     //    s.detect(1);
04807 
04808     //    return s.before_hit + dir.Turn(0 ,-skew*z);
04809 
04810     // return pos + dir.Turn(0 ,-skew*z);
04811 }
04812 
04813 eCoord  gCycle::CamTop() const
04814 {
04815     return dir.Turn(0,-skew);
04816 }
04817 
04818 eCoord gCycle::CamDir() const
04819 {
04820     if ( joystick_ && joystick_->cameraDirection_.NormSquared() > .25 )
04821     {
04822         return joystick_->cameraDirection_;
04823     }
04824     else
04825     {
04826         return dir;
04827     }
04828 }
04829 
04830 eCoord gCycle::Direction() const
04831 {
04832     return gCycleMovement::Direction();
04833 
04834     if ( joystick_ && joystick_->cameraDirection_.NormSquared() > .25 )
04835     {
04836         return joystick_->cameraDirection_;
04837     }
04838     else
04839     {
04840         return dirDrive;
04841     }
04842 }
04843 
04844 #ifdef POWERPAK_DEB
04845 void gCycle::PPDisplay(){
04846     int R=int(r*255);
04847     int G=int(g*255);
04848     int B=int(b*255);
04849     PD_PutPixel(DoubleBuffer,
04850                 se_X_ToScreen(pos.x),
04851                 se_Y_ToScreen(pos.y),
04852                 PD_CreateColor(DoubleBuffer,R,G,B));
04853     /*
04854       PD_PutPixel(DoubleBuffer,
04855       se_X_ToScreen(pos.x+1),
04856       se_Y_ToScreen(pos.y),
04857       PD_CreateColor(DoubleBuffer,R,G,B));
04858       PD_PutPixel(DoubleBuffer,
04859       se_X_ToScreen(pos.x-1),
04860       se_Y_ToScreen(pos.y),
04861       PD_CreateColor(DoubleBuffer,R,G,B));
04862       PD_PutPixel(DoubleBuffer,
04863       se_X_ToScreen(pos.x),
04864       se_Y_ToScreen(pos.y+1),
04865       PD_CreateColor(DoubleBuffer,R,G,B));
04866       PD_PutPixel(DoubleBuffer,
04867       se_X_ToScreen(pos.x),
04868       se_Y_ToScreen(pos.y-1),
04869       PD_CreateColor(DoubleBuffer,R,G,B));
04870     */
04871 }
04872 #endif
04873 
04874 
04875 
04876 
04877 
04878 // cycle network routines:
04879 gCycle::gCycle(nMessage &m)
04880         :gCycleMovement(m),
04881         skew(0),skewDot(0),
04882         rotationFrontWheel(1,0),rotationRearWheel(1,0),heightFrontWheel(0),heightRearWheel(0),
04883         currentWall(NULL),
04884         lastWall(NULL)
04885 {
04886     deathTime=0;
04887     lastNetWall=lastWall=currentWall=NULL;
04888     windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dirDrive);
04889     dirDrive = Grid()->GetDirection(windingNumberWrapped_);
04890     dir = dirDrive;
04891 
04892     rubber=0;
04893     //correctTimeSmooth =0;
04894     correctDistanceSmooth =0;
04895     //correctSpeedSmooth =0;
04896 
04897     deathTime = 0;
04898     spawnTime_ = se_GameTime() + 100;
04899     dirSpawn = dirDrive;
04900 
04901 
04902     m >> color_.r;
04903     m >> color_.g;
04904     m >> color_.b;
04905 
04906     trailColor_ = color_;
04907 
04908     se_MakeColorValid( color_.r, color_.g, color_.b, 1.0f );
04909     se_MakeColorValid( trailColor_.r, trailColor_.g, trailColor_.b, .5f );
04910 
04911     // set last time so that the first read_sync will not think this is old
04912     lastTimeAnim = lastTime = -EPS;
04913 
04914     nextSync = nextSyncOwner = -1;
04915 }
04916 
04917 
04918 void gCycle::WriteCreate(nMessage &m){
04919     eNetGameObject::WriteCreate(m);
04920     m << color_.r;
04921     m << color_.g;
04922     m << color_.b;
04923 }
04924 
04925 static nVersionFeature sg_verletIntegration( 7 );
04926 
04927 void gCycle::WriteSync(nMessage &m){
04928     //  eNetGameObject::WriteSync(m);
04929 
04930     if ( Alive() )
04931     {
04932         m << lastTime;
04933     }
04934     else
04935     {
04936         m << deathTime;
04937     }
04938     m << Direction();
04939     m << Position();
04940 
04941     REAL speed = verletSpeed_;
04942     // if the clients understand it, send them the real current speed
04943     if ( sg_verletIntegration.Supported() )
04944         speed = Speed();
04945 
04946     if ( speed > 15 )
04947     {
04948         int x;
04949         x = 0;
04950     }
04951 
04952     m << speed;
04953     m << short( Alive() ? 1 : 0 );
04954     m << distance;
04955     if (!currentWall || currentWall->preliminary)
04956         m.Write(0);
04957     else
04958         m.Write(currentWall->ID());
04959 
04960     m.Write(turns);
04961     m.Write(braking);
04962 
04963     // write last turn position
04964     m << GetLastTurnPos();
04965 
04966     // write rubber
04967     compressZeroOne.Write( m, rubber/( sg_rubberCycle + .1 ) );
04968     compressZeroOne.Write( m, 1/( 1 + rubberMalus ) );
04969 
04970     // write last clientside sync message ID
04971     unsigned short lastMessageID = 0;
04972     if ( lastDestination )
04973         lastMessageID = lastDestination->messageID;
04974     m.Write(lastMessageID);
04975 
04976     // write brake
04977     compressZeroOne.Write( m, brakingReservoir );
04978 
04979     // set new sync times
04980     // nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
04981     // nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
04982 }
04983 
04984 bool gCycle::SyncIsNew(nMessage &m){
04985     bool ret=eNetGameObject::SyncIsNew(m);
04986 
04987 
04988     REAL dummy2;
04989     short al;
04990     unsigned short Turns;
04991 
04992     m >> dummy2;
04993     m >> al;
04994     m >> dummy2;
04995     m.Read(Turns);
04996 
04997 #ifdef DEBUG_X
04998     con << "received sync for player " << player->GetName() << "  ";
04999     if (ret || al!=1){
05000         con << "accepting..\n";
05001         return true;
05002     }
05003     else
05004     {
05005         con << "rejecting..\n";
05006         return false;
05007     }
05008 #endif
05009 
05010     return ret || al!=1;
05011 }
05012 
05013 void gCycle::RequestSyncOwner()
05014 {
05015     // no more syncs when you're dead
05016     if ( !Alive() )
05017     {
05018         return;
05019     }
05020 
05021     // nothing to do on the client or if the cycle belongs to an AI
05022     if ( sn_GetNetState() != nSERVER || Owner() == 0 )
05023         return;
05024 
05025     REAL syncInterval = sg_GetSyncIntervalSelf( this );
05026     if ( nextSyncOwner < tSysTimeFloat() + syncInterval * 2.0 )
05027     {
05028         // postpone next sync so the notal number of syncs stays the same
05029         RequestSync( Owner(), false );
05030         nextSyncOwner += syncInterval;
05031     }
05032 }
05033 
05034 void gCycle::RequestSyncAll()
05035 {
05036     // no more syncs when you're dead
05037     if ( !Alive() )
05038     {
05039         return;
05040     }
05041 
05042     // nothing to do on the client or if the cycle belongs to an AI
05043     if ( sn_GetNetState() != nSERVER || Owner() == 0 )
05044         return;
05045 
05046     REAL syncInterval = sg_syncIntervalEnemy;
05047     if ( nextSync < tSysTimeFloat() + syncInterval * 2.0 )
05048     {
05049         // postpone next sync so the notal number of syncs stays the same
05050         RequestSync( false );
05051         nextSync += syncInterval;
05052     }
05053 }
05054 
05055 // resets the extrapolator to the last known state
05056 void gCycle::ResetExtrapolator()
05057 {
05058     resimulate_ = false;
05059     if (!extrapolator_)
05060     {
05061         extrapolator_ = tNEW( gCycleExtrapolator )(grid, pos, dir );
05062     }
05063 
05064     extrapolator_->CopyFrom( lastSyncMessage_, *this );
05065 
05066     // simulate a bit, only to get current rubberSpeedFactor and acceleration
05067     extrapolator_->TimestepCore( extrapolator_->LastTime(), true );
05068 }
05069 
05070 // simulate the extrapolator at higher speed
05071 bool gCycle::Extrapolate( REAL dt )
05072 {
05073     tASSERT( extrapolator_ );
05074 
05075     eCoord posBefore = extrapolator_->Position();
05076 
05077     // calculate target time
05078     REAL newTime = extrapolator_->LastTime() + dt;
05079 
05080     bool ret = false;
05081 
05082     // clamp: don't simulate further than our current time
05083     if ( newTime >= lastTime )
05084     {
05085         // simulate extrapolator until now
05086         eGameObject::TimestepThis( lastTime, extrapolator_ );
05087 
05088         // test if there are real (the check for list does that) destinations left; we cannot call it finished if there are.
05089         gDestination* unhandledDestination = extrapolator_->GetCurrentDestination();
05090         ret = !unhandledDestination || !unhandledDestination->list;
05091 
05092         newTime = lastTime;
05093     }
05094     else
05095     {
05096         // simulate extrapolator as requested
05097         eGameObject::TimestepThis( newTime, extrapolator_ );
05098     }
05099 
05100     //eCoord posAfter = extrapolator_->Position();
05101     //eDebugLine::SetTimeout( 1.0 );
05102     //eDebugLine::SetColor( 1,0,0 );
05103     //eDebugLine::Draw( posBefore, 8, posAfter, 4 );
05104     //eDebugLine::Draw( posBefore, 4, posAfter, 4 );
05105 
05106     return ret;
05107 }
05108 
05109 // makes sure the given displacement does not cross walls
05110 void se_SanifyDisplacement( eGameObject* base, eCoord& displacement )
05111 {
05112     eCoord base_pos = base->Position();
05113     eCoord reachable_pos = base->Position() + displacement;
05114 
05115     int timeout = 5;
05116     while( timeout > 0 )
05117     {
05118         --timeout;
05119 
05120         // cast ray fron sync_pos to reachable_pos
05121         gSensor test( base, base_pos, displacement );
05122         test.detect(1);
05123 
05124         if ( timeout == 0 )
05125         {
05126             // emergency exit; take something closely before the wall
05127             displacement = ( displacement ) * ( test.hit * .99 );
05128             break;
05129         }
05130 
05131         if ( !test.ehit )
05132         {
05133             // path is clear
05134             break;
05135         }
05136         else
05137         {
05138 #ifdef DEBUG
05139             // see if the wall that was hit is nearly parallel to the desired move. maybe we should add
05140             // special code for that. However, the projection idea seems to be universally robust.
05141             float p = test.ehit->Vec() * displacement;
05142             float m = se_EstimatedRangeOfMult( test.ehit->Vec(), displacement );
05143             if ( fabs(p) < m * .001 )
05144             {
05145                 con << "Almost missed wall during gCycle::ReadSync positon update";
05146             }
05147 #endif
05148 
05149             // project reachable_pos to the line that was hit
05150             REAL alpha = test.ehit->Ratio( base_pos + displacement );
05151             displacement = *test.ehit->Point() + test.ehit->Vec() * alpha - base_pos;
05152 
05153             // move it a bit closer to the known good position
05154             displacement = displacement * .99;
05155         }
05156     }
05157 }
05158 
05159 void gCycle::TransferPositionCorrectionToDistanceCorrection()
05160 {
05161     REAL newCorrectDist = eCoord::F( correctPosSmooth, dirDrive );
05162     distance += newCorrectDist - correctDistanceSmooth;
05163     correctDistanceSmooth = newCorrectDist;
05164 }
05165 
05166 // take over the extrapolator's data
05167 void gCycle::SyncFromExtrapolator()
05168 {
05169     // store old position
05170     eCoord oldPos = pos;
05171 
05172     // con << "Copy: " << LastTime() << ", " << extrapolator_->LastTime() << ", " << extrapolator_->Position() << ", " << pos << "\n";
05173 
05174     tASSERT( extrapolator_ );
05175 
05176     // delegate
05177     CopyFrom( *extrapolator_ );
05178 
05179     // adjust current wall (not essential, don't do it for the first wall)
05180     if ( currentWall && currentWall->tBeg > spawnTime_ + sg_cycleWallTime + .01f )
05181     {
05182         // update start position
05183         currentWall->beg = extrapolator_->GetLastTurnPos();
05184 
05185         // set begin distance as well
05186         REAL dBeg = extrapolator_->GetDistance() - eCoord::F( extrapolator_->Direction(), extrapolator_->Position() - extrapolator_->GetLastTurnPos() );
05187 
05188         currentWall->dbegin = dBeg;
05189         currentWall->coords_[0].Pos = dBeg;
05190 
05191         // and care for consistency
05192         int i;
05193         for ( i = currentWall->coords_.Len() -1 ; i>=0; --i )
05194         {
05195             gPlayerWallCoord & coord = currentWall->coords_( i );
05196             if ( coord.Pos <= dBeg )
05197                 coord.Pos = dBeg;
05198         }
05199     }
05200 
05201     // transfer last turn position
05202     lastTurnPos_ = extrapolator_->GetLastTurnPos();
05203 
05204     // smooth position correction
05205     correctPosSmooth = correctPosSmooth + oldPos - pos;
05206 
05207 #ifdef DEBUG
05208     if ( correctPosSmooth.NormSquared() > .1f )
05209     {
05210         std::cout << "Lag slide! " << correctPosSmooth << "\n";
05211         resimulate_ = true;
05212     }
05213 #endif
05214 
05215     // calculate time difference between this cycle and extrapolator
05216     REAL dt = this->LastTime() - extrapolator_->LastTime();
05217 
05218     // extrapolate true distance ( the best estimate we have for the distance on the server )
05219     REAL trueDistance = extrapolator_->trueDistance_ + extrapolator_->Speed() * dt;
05220 
05221     // update distance correction
05222     // con <<   correctDistanceSmooth << "," << trueDistance << "," << distance << "\n";
05223     // correctDistanceSmooth = trueDistance - distance;
05224     distance = trueDistance;
05225     correctDistanceSmooth=0;
05226 
05227     // split away part in driving direction
05228     TransferPositionCorrectionToDistanceCorrection();
05229 
05230     // make sure correction does not bring us to the wrong side of a wall
05231     // se_SanifyDisplacement( this, correctPosSmooth );
05232 
05233     //eDebugLine::SetTimeout( 1.0 );
05234     //eDebugLine::SetColor( 0,1,0 );
05235     //eDebugLine::Draw( pos, 4, pos + dirDrive * 10, 14 );
05236 
05237     // delete extrapolator
05238     extrapolator_ = 0;
05239 }
05240 
05241 static int sg_useExtrapolatorSync=1;
05242 static tSettingItem<int> sg_useExtrapolatorSyncConf("EXTRAPOLATOR_SYNC",sg_useExtrapolatorSync);
05243 
05244 // make sure no correction moves the cycle backwards beyond the beginning of the last wall
05245 void ClampForward( eCoord& newPos, const eCoord& startPos, const eCoord& dir )
05246 {
05247     REAL forward = eCoord::F( newPos - startPos, dir )/dir.NormSquared();
05248     if ( forward < 0 )
05249         newPos = newPos - dir * forward;
05250 }
05251 
05252 extern REAL sg_cycleBrakeRefill;
05253 extern REAL sg_cycleBrakeDeplete;
05254 
05255 void gCycle::ReadSync( nMessage &m )
05256 {
05257     // data from sync message
05258     SyncData sync;
05259 
05260     short sync_alive;               // is this cycle alive?
05261     unsigned short sync_wall=0;     // ID of wall
05262 
05263     eCoord new_pos = pos;       // the extrapolated position
05264 
05265     // warning: depends on the implementation of eNetGameObject::WriteSync
05266     // since we don't call eNetGameObject::ReadSync.
05267     m >> sync.time;
05268 
05269     // reset values not sent with old protocol messages
05270     sync.rubber = rubber;
05271     sync.turns = turns;
05272     sync.braking = braking;
05273     sync.messageID = 1;
05274 
05275     m >> sync.dir;
05276     m >> sync.pos;
05277 
05278     //eDebugLine::SetTimeout( 1.0 );
05279     //eDebugLine::SetColor( 1,1,1 );
05280     //eDebugLine::Draw( lastSyncMessage_.pos, 0, lastSyncMessage_.pos, 20 );
05281 
05282     m >> sync.speed;
05283     m >> sync_alive;
05284     m >> sync.distance;
05285     m.Read(sync_wall);
05286     if (!m.End())
05287         m.Read(sync.turns);
05288     if (!m.End())
05289         m.Read(sync.braking);
05290 
05291     if ( !m.End() )
05292     {
05293         m >> sync.lastTurn;
05294     }
05295     else if ( currentWall )
05296     {
05297         sync.lastTurn = currentWall->beg;
05298     }
05299 
05300     bool canUseExtrapolatorMethod = false;
05301 
05302     bool rubberSent = false;
05303     if ( !m.End() )
05304     {
05305         rubberSent = true;
05306 
05307         // read rubber
05308         REAL preRubber, preRubberMalus;
05309         preRubber = compressZeroOne.Read( m );
05310         preRubberMalus = compressZeroOne.Read( m );
05311 
05312         // read last message ID
05313         m.Read(sync.messageID);
05314 
05315         // read braking reservoir
05316         sync.brakingReservoir = compressZeroOne.Read( m );
05317         // std::cout << "sync: " << sync.brakingReservoir << ":" << sync.braking << "\n";
05318 
05319         // undo skewing
05320         sync.rubber = preRubber * ( sg_rubberCycle + .1 );
05321         sync.rubberMalus = 1/preRubberMalus - 1;
05322 
05323         // extrapolation is probably safe
05324         canUseExtrapolatorMethod = sg_useExtrapolatorSync && lastTime > 0;
05325     }
05326     else
05327     {
05328         // try to extrapolate brake status backwards in time
05329         sync.brakingReservoir = brakingReservoir;
05330         if ( brakingReservoir > 0 && sync.braking )
05331             sync.brakingReservoir += ( lastTime - sync.time ) * sg_cycleBrakeDeplete;
05332         else if ( brakingReservoir < 1 && !sync.braking )
05333             sync.brakingReservoir -= ( lastTime - sync.time ) * sg_cycleBrakeRefill;
05334 
05335         if ( sync.brakingReservoir < 0 )
05336             sync.brakingReservoir = 0;
05337         else if ( sync.brakingReservoir > 1 )
05338             sync.brakingReservoir = 1;
05339     }
05340 
05341     // abort if sync is not new
05342     if ( lastSyncMessage_.time >= sync.time && lastSyncMessage_.turns >= sync.turns && sync_alive > 0 )
05343     {
05344         //eDebugLine::SetTimeout( 5 );
05345         //eDebugLine::SetColor( 1,1,0);
05346         //eDebugLine::Draw( lastSyncMessage_.pos, 1.5, lastSyncMessage_.pos, 5.0 );
05347         return;
05348     }
05349     lastSyncMessage_ = sync;
05350 
05351     // store last known good position: a bit before the last position confirmed by the server
05352     lastGoodPosition_ = sync.pos + ( sync.lastTurn - sync.pos ) *.01;
05353     // offset it a tiny bit by our last driving direction
05354     if ( eCoord::F( dirDrive, sync.dir ) > .99f*dirDrive.NormSquared() )
05355         lastGoodPosition_ = lastGoodPosition_ - this->lastDirDrive * .0001;
05356 
05357     //eDebugLine::SetTimeout( 2 );
05358     //eDebugLine::SetColor( 0,1,0);
05359     //eDebugLine::Draw( lastSyncMessage_.pos, 1.5, lastSyncMessage_.pos, 5.0 );
05360 
05361     // con << "Sync: " << lastTime << ", " << lastSyncMessage_.time << ", " << lastSyncMessage_.pos << ", " << pos << "\n";
05362 
05363     if ( canUseExtrapolatorMethod )
05364     {
05365         // reset extrapolator if information from server is more up to date
05366         if ( extrapolator_ && extrapolator_->LastTime() < lastSyncMessage_.time )
05367         {
05368             extrapolator_ = 0;
05369         }
05370     }
05371 
05372     // killed?
05373     if (Alive() && sync_alive!=1 && GOID() >= 0 && grid )
05374     {
05375         Die( lastSyncMessage_.time );
05376         MoveSafely( lastSyncMessage_.pos, lastTime, deathTime );
05377         distance=lastSyncMessage_.distance;
05378         correctDistanceSmooth=0;
05379         DropWall( false );
05380 
05381         tNEW(gExplosion)( grid, lastSyncMessage_.pos, lastSyncMessage_.time ,color_, this );
05382 
05383         return;
05384     }
05385 
05386     // no point going on if you're not alive
05387     if (!Alive())
05388     {
05389 #ifdef DEBUG
05390         con << "Received duplicate death sync message; those things confuse old clients!\n";
05391 #endif
05392         return;
05393     }
05394 
05395     gDestination emergency_aft(*this);
05396 
05397     // all the data was read. check where it fits in our destination list:
05398     gDestination *bef= GetDestinationBefore( lastSyncMessage_, destinationList );
05399     gDestination *aft=NULL;
05400     if (bef){
05401         aft=bef->next;
05402         if (!aft)
05403             aft=&emergency_aft;
05404 
05405         //eDebugLine::SetTimeout(1);
05406         //eDebugLine::SetColor( 1,0,0 );
05407         //eDebugLine::Draw( bef->position, 1.5, lastSyncMessage_.pos, 3.0 );
05408         //eDebugLine::SetColor( 0,1,0 );
05409         //eDebugLine::Draw( lastSyncMessage_.pos, 3.0, aft->position, 5.0 );
05410 
05411     }
05412 
05413 
05414     // detect local tunneling by casting a ray from the server certified position
05415     // to the next known client position
05416     if ( lastTime > 0 )
05417     {
05418         eCoord position = pos;
05419         if ( bef )
05420             position = bef->position;
05421         eSensor tunnel( this, lastSyncMessage_.pos, position - lastSyncMessage_.pos );
05422         tunnel.detect( 1 );
05423 
05424         // use extrapolation to undo local tunneling
05425         if ( tunnel.ehit )
05426         {
05427             canUseExtrapolatorMethod = true;
05428             if ( 0 )
05429             {
05430                 con << "possible local tunneling detected\n";
05431                 eSensor tunnel( this, lastSyncMessage_.pos, position - lastSyncMessage_.pos );
05432                 tunnel.detect( 1 );
05433             }
05434         }
05435     }
05436     else
05437     {
05438         // first sync. Accept the position without questioning it.
05439         pos = sync.pos;
05440         FindCurrentFace();
05441     }
05442 
05443     // determine whether we can use the distance based interpolating sync method here
05444     bool distanceBased = aft && aft != &emergency_aft && Owner() == sn_myNetID;
05445 
05446     if ( canUseExtrapolatorMethod && Owner()==sn_myNetID )
05447     {
05448         // exactlu resimulate from sync position for cycles controlled by this client
05449         resimulate_ = true;
05450 
05451         return;
05452     }
05453 
05454     rubber = lastSyncMessage_.rubber;
05455 
05456     // bool fullSync = false;
05457 
05458     // try to get a distance value closer to the client's data by calculating the distance from the sync position to the next destination
05459     REAL interpolatedDistance = lastSyncMessage_.distance;
05460     if ( aft )
05461     {
05462         interpolatedDistance = aft->distance - sqrt((lastSyncMessage_.pos-aft->position).NormSquared());
05463     }
05464 
05465     // determine true lag
05466     // REAL lag = 1;
05467     //if ( player )
05468     //    lag = player->ping;
05469 
05470     if ( distanceBased )
05471     {
05472         // new way: correct our time and speed
05473 
05474 #ifdef DEBUG
05475         // destination *save = bef;
05476 #endif
05477 
05478         REAL ratio = (interpolatedDistance - bef->distance)/
05479                      (aft->distance - bef->distance);
05480 
05481         if (!finite(ratio))
05482             ratio = 0;
05483 
05484         // interpolate when the cycle was at the position the sync message was sent
05485         REAL interpolatedTime = bef->gameTime * (1-ratio) + aft->gameTime * ratio;
05486         // REAL interpolatedSpeed = bef->speed   * (1-ratio) + aft->speed    * ratio;
05487 
05488         // calculate deltas
05489         REAL correctTime  = ( lastSyncMessage_.time     - interpolatedTime     );
05490         // REAL correctSpeed = ( lastSyncMessage_.speed    - interpolatedSpeed    );
05491         REAL correctDist  = ( lastSyncMessage_.distance - interpolatedDistance );
05492 
05493         // don't trust high ratios too much; they may be skewed by rubber application
05494         {
05495             REAL factor = (1 - ratio) * 5;
05496             if ( factor < 1 )
05497             {
05498                 if ( factor < 0 )
05499                     factor = 0;
05500                 correctTime *= factor;
05501                 correctDist *= factor;
05502             }
05503         }
05504 
05505         //if (correctTime > 0)
05506         //{
05507         //    correctTime*=.5;
05508         //    correctSpeed*=.5;
05509         //    correctDist*=.5;
05510         //}
05511 
05512         //correctTimeSmooth       += correctTime;
05513         //correctSpeedSmooth      += correctSpeed;
05514         // correctDistanceSmooth   += correctDist;
05515 
05516         // correctDistanceSmooth   += correctDist;
05517 
05518         // con << ratio << ", " << correctDist << ", " << correctTime << "\n";
05519 
05520         // correct distances according to sync
05521         {
05522             distance += correctDist;
05523             gDestination * run = bef;
05524             while ( run )
05525             {
05526                 run->distance += correctDist;
05527                 run = run->next;
05528             }
05529         }
05530 
05531         // correct time by adapting position and distance
05532         eCoord newPos = pos - Direction() * Speed() * correctTime;
05533         if ( currentWall )
05534         {
05535             ClampForward( newPos, currentWall->beg, Direction() );
05536         }
05537 
05538         // don't tunnel through walls
05539         {
05540             const eCoord & safePos = pos; // aft->position
05541             gSensor test( this, safePos , newPos - safePos );
05542             test.detect(1);
05543             if ( test.ehit )
05544             {
05545                 newPos = test.before_hit;
05546 
05547                 // something bad must be going on, better recheck with accurate extrapolation
05548                 resimulate_ = true;
05549             }
05550         }
05551 
05552         correctPosSmooth = correctPosSmooth + pos - newPos;
05553         distance += eCoord::F( newPos - pos, Direction() )/Direction().NormSquared();
05554 
05555         MoveSafely( newPos, lastTime, lastTime );
05556 
05557         /*
05558         REAL ts = lastSyncMessage_.time - lastTime;
05559 
05560         //        eCoord intPos = pos + dirDrive * (ts * speed + .5 * ts*ts*acceleration);
05561         eCoord intPos = pos + dirDrive * ( ts * ( speed + lastSyncMessage_.speed ) * .5 );
05562         REAL  int_speed = speed + acceleration*ts;
05563 
05564         dirDrive = lastSyncMessage_.dir;
05565 
05566         correctPosSmooth = lastSyncMessage_.pos - intPos;
05567 
05568         distance = lastSyncMessage_.distance - speed * ts - acceleration * ts*ts*.5;
05569 
05570         //correctTimeSmooth = 0;
05571         */
05572     }
05573     else
05574     {
05575         // direct sync
05576         if ( Owner() != sn_myNetID )
05577         {
05578             // direct extrapolation for cycles of other clients or if no turn is newer than the sync
05579             SyncEnemy( lastSyncMessage_.lastTurn );
05580 
05581             // update beginning of current wall
05582             if ( currentWall )
05583             {
05584                 currentWall->beg = lastSyncMessage_.lastTurn;
05585             }
05586 
05587             // update brake status
05588             AccelerationDiscontinuity();
05589             braking = lastSyncMessage_.braking;
05590 
05591             // store last turn
05592             lastTurnPos_ = lastSyncMessage_.lastTurn;
05593         }
05594         else
05595         {
05596             // same algorithm, but update smooth position correction so that there is no immediate visual change
05597             eCoord oldPos = pos + correctPosSmooth;
05598             SyncEnemy( lastSyncMessage_.lastTurn );
05599             correctPosSmooth = oldPos - pos;
05600         }
05601 
05602         // restore rubber meter
05603         if ( !rubberSent )
05604         {
05605             rubber = lastSyncMessage_.rubber;
05606         }
05607     }
05608 
05609     // if this happens during creation, ignore position correction
05610     if ( this->ID() == 0 )
05611     {
05612         correctPosSmooth = eCoord();
05613 
05614         // some other stuff that should happen on the first sync
05615 
05616         // estimate time of spawning (HACK)
05617         spawnTime_=lastTime;
05618         if ( verletSpeed_ > 0 )
05619             spawnTime_ -= distance/verletSpeed_;
05620 
05621         // set spawn time to infinite past if this is the first spawn
05622         if ( !sg_cycleFirstSpawnProtection && spawnTime_ <= 1.0 )
05623         {
05624             spawnTime_ = -1E+20;
05625         }
05626         dirSpawn = dirDrive;
05627 
05628         // reset position and direction
05629         predictPosition_ = pos;
05630         dir = dirDrive;
05631         skew = skewDot = 0;
05632         lastDirDrive=dirDrive;
05633         lastTurnPos_=pos;
05634     }
05635 #ifdef DEBUG
05636     else
05637         if ( correctPosSmooth.NormSquared() > .1f && lastTime > 0.0 )
05638         {
05639             std::cout << "Lag slide! " << correctPosSmooth << "\n";
05640             int x;
05641             x = 0;
05642         }
05643 #endif
05644 
05645     sn_Update(turns,lastSyncMessage_.turns);
05646 
05647     //if (fabs(correctTimeSmooth > 5))
05648     //    st_Breakpoint();
05649 
05650     // calculate new winding number. Try to change it as little as possible.
05651     this->SetWindingNumberWrapped( Grid()->DirectionWinding(dirDrive) );
05652 
05653     // Resnap to the axis
05654     dirDrive = Grid()->GetDirection(windingNumberWrapped_);
05655 }
05656 
05657 void gCycle::SyncEnemy ( const eCoord& begWall)
05658 {
05659     // keep this cycle alive
05660     tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
05661 
05662     resimulate_ = false;
05663 
05664     // calculate turning
05665     bool turned = false;
05666     REAL turnDirection=( dirDrive*lastSyncMessage_.dir );
05667     REAL notTurned=eCoord::F( dirDrive, lastSyncMessage_.dir )/dirDrive.NormSquared();
05668 
05669     // the last known time
05670     REAL lastKnownTime = lastTime;
05671 
05672     // calculate the position of the last turn from the sync data
05673     if ( distance > 0 && ( notTurned < .99 || this->turns < lastSyncMessage_.turns ) )
05674     {
05675         // update old wall as good as we can
05676         eCoord crossPos = lastSyncMessage_.pos;
05677         REAL crossDist = lastSyncMessage_.distance;
05678         REAL crossTime = lastSyncMessage_.time;
05679 
05680         // calculate intersection of old and new trajectory (if only one turn was made)
05681         // the second condition is for old servers that don't send the turn count; they
05682         // don't support multiple axes, so we can possibly detect missed turns if
05683         // the dot product of the current and last direction is negative.
05684         if (this->turns+1 >= lastSyncMessage_.turns && ( lastSyncMessage_.turns > 0 || notTurned > -.5 ) )
05685         {
05686             if ( fabs( turnDirection ) > .01 )
05687             {
05688                 REAL b = ( crossPos - pos ) * dirDrive;
05689                 REAL distplace = b/turnDirection;
05690                 crossPos = crossPos + lastSyncMessage_.dir * distplace;
05691                 crossDist += distplace;
05692                 if ( lastSyncMessage_.speed > 0 )
05693                     crossTime += distplace / lastSyncMessage_.speed;
05694 
05695                 tASSERT( fabs ( ( crossPos - pos ) * dirDrive ) < 1 );
05696                 tASSERT( fabs ( ( crossPos - lastSyncMessage_.pos ) * lastSyncMessage_.dir ) < 1 );
05697 
05698                 // update the old wall
05699                 if (currentWall) {
05700                     currentWall->Update(crossTime,crossPos);
05701 
05702                     eSoundMixer* mixer = eSoundMixer::GetMixer();
05703                     mixer->PushButton(CYCLE_TURN, crossPos);
05704                 }
05705             }
05706         }
05707         else
05708         {
05709             // a turn sync was dropped for whatever reason. The last wall is not reliable.
05710             // make it disappear immediately.
05711             if (currentWall)
05712             {
05713                 currentWall->real_CopyIntoGrid(grid);
05714             }
05715         }
05716 
05717         eDebugLine::SetTimeout(5);
05718         eDebugLine::SetColor( 1,0,0 );
05719         eDebugLine::Draw( crossPos, 0, crossPos, 8 );
05720 
05721         // drop old wall
05722         if(currentWall){
05723             lastWall=currentWall;
05724             currentWall->CopyIntoGrid( grid );
05725             tControlledPTR< gNetPlayerWall > bounce( currentWall );
05726             currentWall=NULL;
05727         }
05728 
05729         // create new wall at sync location
05730         distance = lastSyncMessage_.distance;
05731         correctDistanceSmooth=0;
05732 
05733         REAL startBuildWallAt = spawnTime_ + sg_cycleWallTime;
05734         if ( crossTime > startBuildWallAt )
05735             currentWall=new gNetPlayerWall
05736                         (this,crossPos,lastSyncMessage_.dir,crossTime,crossDist);
05737 
05738         turned = true;
05739 
05740         // save last driving direction
05741         lastDirDrive = dirDrive;
05742 
05743         // move to cross position
05744         MoveSafely( crossPos, lastTime, crossTime );
05745         lastKnownTime = crossTime;
05746     }
05747 
05748     // side bending
05749     skewDot+=4*turnDirection;
05750 
05751     // calculate timestep
05752     // REAL ts = lastSyncMessage_.time - lastTime;
05753 
05754     // backup current time
05755     REAL oldTime = lastTime;
05756 
05757     // update position, speed, distance and direction
05758     MoveSafely( lastSyncMessage_.pos, lastKnownTime, lastSyncMessage_.time );
05759     verletSpeed_  = lastSyncMessage_.speed;
05760     lastTimestep_ = 0;
05761     distance = lastSyncMessage_.distance;
05762     correctDistanceSmooth=0;
05763     dirDrive = lastSyncMessage_.dir;
05764     rubber = lastSyncMessage_.rubber;
05765     brakingReservoir = lastSyncMessage_.brakingReservoir;
05766 
05767     // update time to values from sync
05768     lastTime = lastSyncMessage_.time;
05769     if ( oldTime < 0 )
05770         oldTime = lastTime;
05771     clientside_action();
05772 
05773     // bend last driving direction if it is antiparallel to the current one
05774     if ( lastDirDrive * dirDrive < EPS && eCoord::F( lastDirDrive, dirDrive ) < 0 )
05775     {
05776         lastDirDrive = dirDrive.Turn(0,1);
05777     }
05778 
05779     // correct laggometer to actual facts
05780     if (Owner() != sn_myNetID )
05781     {
05782         // calculate lag from sync delay
05783         REAL lag = se_GameTime() - lastSyncMessage_.time;
05784         if ( lag < 0 )
05785             lag = 0;
05786 
05787         // try not to let the lag jump
05788         REAL maxLag = laggometer * 1.2;
05789         REAL minLag = laggometer * .8;
05790 
05791         // store it
05792         laggometer = lag;
05793 
05794         // clamp smooth laggometer to it (or set it if a turn was made)
05795         //if ( laggometerSmooth > lag )
05796         //    laggometerSmooth = lag;
05797 
05798         // For the client and without prediction:
05799         // do not simulate if a turn was made, just accept sync as it is
05800         // (so turns don't look awkward)
05801         if (
05802             sn_GetNetState()==nCLIENT && turned )
05803         {
05804             laggometerSmooth = lag;
05805 
05806             // but at least update the current wall
05807             if ( currentWall )
05808                 currentWall->Update(lastTime,pos);
05809 
05810             // and reset the animation time
05811             lastTimeAnim=lastTime;
05812 
05813             return;
05814         }
05815         else
05816         {
05817             // sanity check: the lag should not jump up suddenly
05818             // (and if it does, we'll catch up at the next turn)
05819             // REAL maxLag = 1.2 * laggometerSmooth;
05820             if ( laggometer > maxLag )
05821                 laggometer = maxLag;
05822             if ( laggometer < minLag )
05823                 laggometer = minLag;
05824         }
05825     }
05826 
05827     // simulate to extrapolate, but don't touch the smooth laggometer
05828     {
05829         REAL laggometerSmoothBackup = this->laggometerSmooth;
05830         TimestepThis(oldTime, this);
05831         this->laggometerSmooth = laggometerSmoothBackup;
05832     }
05833 }
05834 
05835 /*
05836 void gCycle::old_ReadSync(nMessage &m){
05837   REAL oldTime=lastTime;
05838   eCoord oldpos=pos;
05839   //+correctPosSmooth;
05840   //correctPosSmooth=0;
05841   eCoord olddir=dir;
05842   eNetGameObject::ReadSync(m);
05843 
05844   REAL t=(dirDrive*dir);
05845   if (fabs(t)>.2){
05846 #ifdef DEBUG
05847     if (owner==sn_myNetID)
05848       con << "Warning! Turned cycle!\n";
05849 #endif
05850     turning.Reset();
05851   }
05852 
05853   // side bending
05854   skewDot+=4*t;
05855 
05856 
05857   dirDrive=dir;
05858   dir=olddir;
05859 
05860   m >> speed;
05861   short new_alive;
05862   m >> new_alive;
05863   if (alive && new_alive!=1){
05864     new gExplosion(pos,oldTime,r,g,b);
05865     deathTime=oldTime;
05866     eEdge::SeethroughHasChanged();
05867   }
05868   alive=new_alive;
05869 
05870   m >> distance;
05871 
05872   // go to old time frame
05873 
05874   eCoord realpos=pos;
05875   REAL realtime=lastTime;
05876   REAL realdist=distance;
05877 
05878   if (currentWall)
05879     lastWall=currentWall;
05880 
05881   m.Read(currentWallID);
05882 
05883   unsigned short Turns;
05884   if (!m.End())
05885     m.Read(Turns);
05886   else
05887     Turns=turns;
05888 
05889   if (!m.End())
05890     m.Read(braking);
05891   else
05892     braking=false;
05893 
05894   TimestepThis(oldTime,this);
05895 
05896   if (!currentWall || fabs(t)>.3 || currentWall->inGrid){
05897     //REAL d=eCoord::F(pos-realpos,olddir);
05898     //eCoord crosspos=realpos+olddir*d;
05899     //d=eCoord::F(oldpos-crosspos,dir);
05900     //crosspos=crosspos+dir*d;
05901 
05902     eCoord crosspos=realpos;
05903 
05904     if (currentWall){
05905       currentWall->Update(realtime,crosspos);
05906       //currentWall->Update(realtime,realpos);
05907       currentWall->CopyIntoGrid();
05908     }
05909     //con << "NEW\n";
05910     currentWall=new gNetPlayerWall
05911       (this,crosspos,dirDrive,realtime,realdist);
05912   }
05913 
05914 
05915   // smooth correction
05916   if ((oldpos-pos).NormSquared()<4 && fabs(t)<.5){
05917     correctPosSmooth=pos-oldpos;
05918     pos=oldpos;
05919   }
05920   
05921 
05922 
05923 #ifdef DEBUG
05924   //int old_t=turns;
05925   //if(sn_Update(turns,Turns))
05926   //con << "Updated turns form " << old_t << " to " << turns << "\n";
05927 #endif
05928   sn_Update(turns,Turns);
05929 }
05930 */
05931 
05932 void gCycle::ReceiveControl(REAL time,uActionPlayer *act,REAL x){
05933     //forceTime=true;
05934     TimestepThis(time,this);
05935     Act(act,x);
05936     //forceTime=false;
05937 }
05938 
05939 void gCycle::PrintName(tString &s) const
05940 {
05941     s << "gCycle nr. " << ID();
05942     if ( this->player )
05943     {
05944         s << " owned by ";
05945         this->player->PrintName( s );
05946     }
05947 }
05948 
05949 bool gCycle::ActionOnQuit()
05950 {
05951     //  currentWall = NULL;
05952     //  lastWall = NULL;
05953     if ( sn_GetNetState() == nSERVER )
05954     {
05955         TakeOwnership();
05956         Kill();
05957         return false;
05958     }
05959     else
05960     {
05961         return true;
05962     }
05963 }
05964 
05965 
05966 nDescriptor &gCycle::CreatorDescriptor() const{
05967     return cycle_init;
05968 }
05969 
05970 /*
05971 void gCycle::AddDestination(gDestination *dest){
05972     //  con << "got new dest " << dest->position << "," << dest->direction
05973     // << "," << dest->distance << "\n";
05974 
05975     dest->InsertIntoList(&destinationList);
05976 
05977     // if the new destination was inserted at the end of the list
05978     //if (!currentDestination && !dest->next &&
05979     //if ((dest->distance >= distance || (!currentDestination && !dest->next)) &&
05980 
05981     if (dest->next && dest->next->hasBeenUsed){
05982         delete dest;
05983         return;
05984     }
05985 
05986     // go back one destination if the new destination appears to be older than the current one
05987     if ((!currentDestination || currentDestination == dest->next ) &&
05988             sn_GetNetState()!=nSTANDALONE && Owner()!=::sn_myNetID){
05989         currentDestination=dest;
05990         // con << "setting new cd\n";
05991         }
05992 }
05993 */
05994 
05995 void gCycle::RightBeforeDeath( int numTries )
05996 {
05997     if ( player )
05998     {
05999         player->RightBeforeDeath( numTries );
06000     }
06001 
06002     // delay syncs for old clients; they would tunnel locally
06003     if ( sg_avoidBadOldClientSync && !sg_NoLocalTunnelOnSync.Supported( Owner() ) )
06004     {
06005         nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
06006         nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
06007     }
06008 
06009     correctPosSmooth = correctPosSmooth * .5;
06010 }
06011 
06012 // *******************************************************************************************
06013 // *
06014 // *    DoIsDestinationUsed
06015 // *
06016 // *******************************************************************************************
06021 // *******************************************************************************************
06022 
06023 bool gCycle::DoIsDestinationUsed( const gDestination * dest ) const
06024 {
06025     return ( extrapolator_ && extrapolator_->IsDestinationUsed( dest ) ) || gCycleMovement::DoIsDestinationUsed( dest );
06026 }
06027 
06028 // *******************************************************************************************
06029 // *
06030 // *   DoGetDistanceSinceLastTurn
06031 // *
06032 // *******************************************************************************************
06036 // *******************************************************************************************
06037 
06038 /*
06039 REAL gCycle::DoGetDistanceSinceLastTurn( void ) const
06040 {
06041     if ( currentWall )
06042     {
06043         return ( currentWall->Pos(1) - currentWall->Pos(0) );
06044     }
06045 
06046     return 0;
06047 }
06048 */
06049 
06050 // *******************************************************************************************
06051 // *
06052 // *   DoGetDistanceSinceLastTurn
06053 // *
06054 // *******************************************************************************************
06058 // *******************************************************************************************
06059 
06060 /*
06061 REAL gCycleExtrapolator::DoGetDistanceSinceLastTurn( void ) const
06062 {
06063     return eCoord::F( pos - lastTurn_, dirDrive );
06064 }
06065 */
06066 
06067 // *******************************************************************************************
06068 // *
06069 // *    Vulnerable
06070 // *
06071 // *******************************************************************************************
06075 // *******************************************************************************************
06076 
06077 bool gCycle::Vulnerable() const
06078 {
06079     return Alive() && lastTime > spawnTime_ + sg_cycleInvulnerableTime;
06080 }

Generated on Sat Mar 15 22:56:06 2008 for Armagetron Advanced by  doxygen 1.5.4