00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "eEventNotification.h"
00029 #include "gCycle.h"
00030 #include "nConfig.h"
00031 #include "rModel.h"
00032
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
00071 #include "tDirectories.h"
00072
00073
00074 bool sg_gnuplotDebug = false;
00075
00076 #define GNUPLOT_DEBUG
00077
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
00096
00097
00098
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;
00116
00117 REAL gCycle::wallsLength=-1.0f;
00118 REAL gCycle::explosionRadius=4.0f;
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
00134 static REAL sg_speedCycleSound=15;
00135 static nSettingItem<REAL> c_ss("CYCLE_SOUND_SPEED",
00136 sg_speedCycleSound);
00137
00138
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
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
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
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
00172 static bool sg_avoidBadOldClientSync=true;
00173 static tSettingItem<bool> c_sbs( "CYCLE_AVOID_OLDCLIENT_BAD_SYNC",
00174 sg_avoidBadOldClientSync );
00175
00176
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
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
00197
00198
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
00216
00217 uActionPlayer gCycle::s_brake("CYCLE_BRAKE", -5);
00218 static uActionPlayer s_brakeToggle("CYCLE_BRAKE_TOGGLE", -5);
00219
00220
00221
00222 class gTextureCycle: public rSurfaceTexture
00223 {
00224 gRealColor color_;
00225 bool wheel;
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
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
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
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
00346
00347
00348
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
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
00377
00378 case gSENSOR_SELF:
00379 {
00380
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
00394
00395 REAL wallAlpha = playerWall->Edge()->Ratio( before_hit );
00396
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
00406 if ( hitTime_ > hitOwner_->LastTime() - sg_chatBotNewWallBlindness && hitOwner_ != owned )
00407 {
00408 ehit = false;
00409 hit = 1E+40;
00410 return false;
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425 }
00426 }
00427
00428 return true;
00429 }
00430
00431
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_;
00452 REAL hitTime_;
00453 REAL hitDistance_;
00454 short lrSuggestion_;
00455 int windingNumber_;
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
00469 class WallHug
00470 {
00471 public:
00472 gCycle const * owner_;
00473 REAL lastTimeSeen_;
00474
00475 WallHug()
00476 : owner_ ( NULL )
00477 , lastTimeSeen_ ( 0 )
00478 {
00479 }
00480 };
00481
00482
00483 void FindHugReplacement( Sensor const & sensor )
00484 {
00485 gCycle const * owner = sensor.hitOwner_;
00486 if (!owner)
00487 return;
00488
00489
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
00499 if ( owner == hugLeft_.owner_ )
00500 hugLeft_.lastTimeSeen_ = nextChatAI_;
00501 if ( owner == hugRight_.owner_ )
00502 hugRight_.lastTimeSeen_ = nextChatAI_;
00503 }
00504
00505
00506
00507 REAL Distance( Sensor const & a, Sensor const & b )
00508 {
00509
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
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
00540 REAL bigDistance = owner_->MaxWallsLength();
00541 if ( bigDistance <= 0 )
00542 bigDistance = owner_->GetDistance();
00543
00544 if ( a.hitOwner_ != b.hitOwner_ )
00545 {
00546
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
00555 if ( !self )
00556 ret = bigDistance * 2;
00557 }
00558
00559
00560
00561 ret *= 16;
00562
00563
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
00572 return ( a.before_hit - b.before_hit).Norm() * selfHatred;
00573 }
00574 else if ( a.type == gSENSOR_NONE && b.type == gSENSOR_NONE )
00575 {
00576
00577 return owner_->GetDistance() * 256;
00578 }
00579 else if ( a.lr != b.lr )
00580 {
00581
00582 return ( fabsf( a.hitDistance_ - b.hitDistance_ ) + .25 * bigDistance ) * selfHatred;
00583 }
00584
00585
00586
00587
00588
00589
00590
00591 else
00592 {
00593
00594 return fabsf( a.hitDistance_ - b.hitDistance_ ) * selfHatred;
00595 }
00596
00597
00598 return ( a.before_hit - b.before_hit).Norm() * selfHatred;
00599 }
00600
00601 static gCycleChatBot & Get( gCycle * cycle )
00602 {
00603 tASSERT( cycle );
00604
00605
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
00618 void Activate( REAL currentTime )
00619 {
00620
00621 if ( currentTime < nextChatAI_ )
00622 return;
00623
00624 REAL lookahead = sg_chatBotRange;
00625 REAL minstep = sg_chatBotMinTimestep;
00626 REAL maxstep = sg_chatBotMinTimestep * 4 * ( 1 + .1 * tReproducibleRandomizer::GetInstance().Get() );
00627
00628
00629 if ( nextChatAI_ <= EPS )
00630 {
00631 nextChatAI_ = sg_chatBotDelay + currentTime;
00632 return;
00633 }
00634
00635 timeOnChatAI_ += currentTime - nextChatAI_;
00636
00637
00638 REAL speed = owner_->Speed();
00639 eCoord dir = owner_->Direction();
00640 eCoord pos = owner_->Position();
00641
00642
00643 if ( sn_GetNetState() != nSTANDALONE )
00644 {
00645 REAL qualityFactor = ( timeOnChatAI_ * sg_chatBotDecay );
00646 if ( qualityFactor > 1 )
00647 {
00648 minstep *= qualityFactor;
00649
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
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
00675 tJUST_CONTROLLED_PTR< gNetPlayerWall > lastWall = owner_->lastWall;
00676 owner_->lastWall = NULL;
00677
00678 REAL narrowFront = 1;
00679
00680
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
00691 if ( hugReplacement_.owner_ && !hugLeft_.owner_ && !hugRight_.owner_ )
00692 {
00693
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
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
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
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788 if ( forwardRight.type == gSENSOR_SELF &&
00789 forwardLeft.type == gSENSOR_RIM &&
00790 backwardRight.type == gSENSOR_SELF &&
00791 backwardLeft.type == gSENSOR_RIM &&
00792
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
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
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
00841 owner_->lastWall = lastWall;
00842
00843
00844
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
00852 minMoveOn = bestBackward.HitWallExtends( dir, pos );
00853
00854
00855 REAL minMoveOnOther = direct.HitWallExtends( dir, pos );
00856
00857
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
00870 moveOn = .5 * ( minMoveOn * ( 1 + rubberRatio ) + maxMoveOn * ( 1 - rubberRatio ) );
00871
00872
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
00882
00883
00884
00885
00886
00887 turnedRecently_ = true;
00888
00889 minMoveOn = maxMoveOn = moveOn = 0;
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
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
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
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
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
00955 if ( bestHit * lookahead < owner_->GetTurnDelay() + rubberTime )
00956 {
00957 if ( otherHit < bestForward.hit * 2 && front.hit * lookahead > owner_->GetTurnDelay() * 2 )
00958 {
00959
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
00973 if ( maxMoveOn < speed * owner_->GetTurnDelay() )
00974 {
00975
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
00993 owner_->Act( &gCycle::s_brake, brake ? 1 : -1 );
00994
00995
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
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
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
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
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
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
01159 distance += other.correctDistanceSmooth;
01160 }
01161
01162
01163
01164
01165
01166
01171
01172
01173 int gDestination::CompareWith( const gDestination & other ) const
01174 {
01175
01176
01177
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
01188 if ( distance < other.distance )
01189 return -1;
01190 else if ( distance > other.distance )
01191 return 1;
01192
01193
01194 return 0;
01195 }
01196
01197 class gFloatCompressor
01198 {
01199 public:
01200 gFloatCompressor( REAL min, REAL max )
01201 : min_( min ), max_( max ){}
01202
01203
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_;
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
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
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
01274 void gDestination::InsertIntoList(gDestination **l){
01275 if (!l)
01276 return;
01277
01278 RemoveFromList();
01279
01280
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
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
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
01326
01327
01331
01332
01333 REAL gDestination::GetGameTime( void ) const
01334 {
01335 return this->gameTime;
01336 }
01337
01338
01339
01340
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
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
01377 unsigned short cycle_id;
01378 gDestination *dest=new gDestination(m, cycle_id );
01379
01380
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
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
01407
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
01445 if (sn_GetNetState()==nCLIENT && ::sn_myNetID == Owner())
01446 BroadCastNewDestination(this,dest);
01447
01448 if ( extrapolator_ )
01449 {
01450
01451 extrapolator_->NotifyNewDestination( dest );
01452 }
01453
01454
01455
01456
01457
01458 if( sn_GetNetState() == nSERVER )
01459 {
01460
01461 REAL simTime=se_GameTime() - Lag();
01462
01463 REAL lag = simTime - dest->gameTime;
01464 REAL lagOffset = simTime - lastTime;
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;
01471
01472
01473 if ( lag < 0 )
01474 return;
01475
01476
01477 REAL minDist = currentWall->Wall()->Pos(0);
01478 REAL maxGoBack = ( distance - minDist ) * .8;
01479
01480
01481 REAL stepBack = distance - dest->distance;
01482
01483 if ( rubberSpeedFactor < 1-EPS )
01484 {
01485
01486 maxGoBack = stepBack;
01487 }
01488
01489
01490 if ( stepBack > maxGoBack )
01491 {
01492 stepBack = maxGoBack;
01493 lag = rubberSpeedFactor*stepBack/verletSpeed_;
01494 }
01495
01496
01497
01498 lag = eLag::TakeCredit( Owner(), lag + lagOffset ) - lagOffset;
01499
01500
01501 if ( lag < 0 )
01502 return;
01503
01504
01505 if ( rubberSpeedFactor >= 1-EPS )
01506 {
01507
01508 TimestepCore( lastTime - lag );
01509 }
01510 else if ( 0 )
01511 {
01512
01513
01514
01515 REAL step = lag * verletSpeed_;
01516 lastTime -= lag;
01517
01518
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
01528 step *= rubberSpeedFactor;
01529 distance -= step;
01530 pos = pos - dirDrive * step;
01531
01532
01533 verletSpeed_ -= acceleration * lag;
01534 }
01535
01536
01537 if ( distance < minDist )
01538 {
01539 st_Breakpoint();
01540 TimestepCore( lastTime + ( minDist - distance )/verletSpeed_ );
01541 }
01542 }
01543 }
01544 }
01545 }
01546
01547
01548
01549
01550
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
01568 bool wallRight = ( currentWall && ( wall->NetWall() == currentWall || wall->NetWall() == currentWall ) );
01569
01570
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
01577
01578 if ( wallRight )
01579 {
01580
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
01589 dropWallRequested_ = true;
01590
01591
01592
01593 lastDirDrive = -dir;
01594 }
01595 }
01596 }
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607 void gCycle::SetWallsStayUpDelay ( REAL delay )
01608 {
01609 c_pwsud->Set( delay );
01610 }
01611
01612
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
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
01636 static REAL sg_CycleWallLengthFromDist( REAL distance )
01637 {
01638 REAL len = gCycle::WallsLength();
01639
01640
01641 REAL d = sg_cycleDistWallShrinkOffset - distance;
01642 if ( d > 0 )
01643 len -= sg_cycleDistWallShrink * d;
01644
01645 return len;
01646 }
01647
01648
01649 REAL gCycle::ThisWallsLength() const
01650 {
01651
01652 REAL len = sg_CycleWallLengthFromDist( distance );
01653
01654
01655 return len - GetRubber() * sg_cycleRubberWallShrink;
01656 }
01657
01658
01659
01660 REAL gCycle::WallEndSpeed() const
01661 {
01662 REAL rubberMax, rubberEffectiveness;
01663 sg_RubberValues( player, Speed(), rubberMax, rubberEffectiveness );
01664
01665
01666 REAL speed = rubberSpeedFactor * Speed();
01667
01668
01669 REAL d = sg_cycleDistWallShrinkOffset - distance;
01670 if ( d > 0 )
01671 speed *= ( 1 - sg_cycleDistWallShrink );
01672
01673
01674 if ( rubberEffectiveness > 0 )
01675 speed += Speed() * ( 1 - rubberSpeedFactor ) * sg_cycleRubberWallShrink / rubberEffectiveness;
01676
01677 return speed;
01678 }
01679
01680
01681 REAL gCycle::MaxWallsLength() const
01682 {
01683 REAL len = sg_CycleWallLengthFromDist( distance );
01684
01685
01686
01687 if ( sg_cycleDistWallShrink > 1 )
01688 {
01689 len = wallsLength;
01690 }
01691
01692
01693 if ( sg_cycleRubberWallShrink >= 0 || sg_rubberCycle < 0 )
01694 return len;
01695 else
01696 return len - sg_cycleRubberWallShrink * sg_rubberCycle;
01697 }
01698
01699
01700 void gCycle::SetWallsLength ( REAL length)
01701 {
01702 c_pwl->Set( length );
01703 }
01704
01705
01706 void gCycle::SetExplosionRadius ( REAL radius)
01707 {
01708 c_per->Set( radius );
01709 }
01710
01711
01712
01713
01714 void gCycleExtrapolator::CopyFrom( const gCycleMovement& other )
01715 {
01716
01717 gCycleMovement::CopyFrom( other );
01718
01719
01720 parent_ = &other;
01721
01722
01723 trueDistance_ = GetDistance();
01724 }
01725
01726
01727 void gCycleExtrapolator::CopyFrom( const SyncData& sync, const gCycle& other )
01728 {
01729
01730 gCycleMovement::CopyFrom( sync, other );
01731
01732
01733 MoveSafely( other.lastGoodPosition_, sync.time, sync.time );
01734
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
01747 parent_ = &other;
01748
01749
01750 lastTurnPos_ = sync.lastTurn;
01751
01752
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
01762 eFace * currentFaceBack = currentFace;
01763 RemoveFromList();
01764 currentFace = currentFaceBack;
01765 if ( !currentFace )
01766 currentFace = grid->FindSurroundingFace( pos, currentFace );
01767 }
01768
01769
01770 gCycleExtrapolator::~gCycleExtrapolator()
01771 {
01772 }
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
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
01806 REAL builtTime = w->Time(alpha);
01807
01808
01809 if ( builtTime > time )
01810 return false;
01811
01812
01813 if ( nw && nw->Preliminary() && w->Cycle() == parent_ && fabs( dirDrive * w->Vec() ) < EPS )
01814 return false;
01815
01816
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
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
01845 gDestination destDefault( *parent_ ), *dest=&destDefault;
01846 if ( GetCurrentDestination() )
01847 {
01848 dest = GetCurrentDestination();
01849 }
01850
01851
01852
01853
01854 tASSERT(finite(distance));
01855
01856
01857 bool ret = false;
01858 try{
01859 ret = gCycleMovement::TimestepCore( currentTime, calculateAcceleration );
01860 }
01861 catch( gCycleDeath & )
01862 {
01863 return false;
01864 }
01865
01866
01867
01868 trueDistance_ = distance;
01869
01870 return ret;
01871 }
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881 nDescriptor &gCycleExtrapolator::CreatorDescriptor() const{
01882
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
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925 #ifndef DEDICATED
01926 struct gCycleVisuals
01927 {
01928 rModel *customModel, *bodyModel, *frontModel, *rearModel;
01929 gTextureCycle *customTexture, *bodyTexture, *wheelTexture;
01930 gRealColor color;
01931 bool mpType;
01932 int mpPreference;
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
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
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
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
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
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
02053 bool LoadModel2( bool mp )
02054 {
02055
02056 return LoadModel( mp, mp ) || LoadModel( !mp, mp );
02057 }
02058
02059
02060 bool LoadModel( bool mp )
02061 {
02062 mpPreference = mp ? 1 : 0;
02063
02064
02065 return LoadModel2( mp ) || LoadModel2( !mp );
02066 }
02067 };
02068 #endif
02069
02070 #ifndef DEDICATED
02071
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
02122 #ifndef DEDICATED
02123 new gCycleWallRenderer( this );
02124 #endif
02125
02126 dropWallRequested_ = false;
02127 lastGoodPosition_ = pos;
02128
02129 #ifdef DEBUG
02130
02131 #endif
02132 eSoundMixer* mixer = eSoundMixer::GetMixer();
02133 mixer->PlayContinuous(CYCLE_MOTOR, this);
02134
02135
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 {
02171
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
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
02203 if ( mp )
02204 {
02205
02206 customModel = visuals.customModel;
02207 visuals.customModel = 0;
02208 customTexture = visuals.customTexture;
02209 visuals.customTexture = 0;
02210 }
02211 else
02212 {
02213
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
02233
02234
02235
02236
02237
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251
02252
02253
02254
02255
02256
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
02272 if ( !sg_cycleFirstSpawnProtection && spawnTime_ <= 1.0 )
02273 {
02274 spawnTime_ = -1E+20;
02275 }
02276 dirSpawn = dirDrive;
02277
02278
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
02335 #endif
02336
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
02356 #endif
02357
02358
02359
02360
02361 }
02362
02363 void gCycle::OnRemoveFromGame()
02364 {
02365
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
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
02396 void gCycle::OnRoundEnd()
02397 {
02398
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
02415 bool crash_sparks=false;
02416 #else
02417 bool crash_sparks=true;
02418 #endif
02419
02420
02421
02422
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
02437
02438
02442
02443 void gCycle::PreparePredictPosition( gPredictPositionData & data )
02444 {
02445
02446 data.maxSpaceReport = 0;
02447 #ifdef DEDICATED
02448 if ( sg_predictWalls )
02449 {
02450
02451 REAL maxTime = lastTime + MaxSimulateAhead() + GetMaxLazyLag();
02452 REAL predictDT = maxTime - lastTime;
02453 REAL lookAhead = predictDT * verletSpeed_;
02454
02455
02456 data.rubberStart = verletSpeed_ / RubberSpeed();
02457
02458
02459 data.maxSpaceReport = lookAhead + data.rubberStart;
02460 }
02461 #else
02462 data.maxSpaceReport = verletSpeed_ * se_PredictTime() * rubberSpeedFactor;
02463 #endif
02464
02465
02466 maxSpaceMaxCast_ = data.maxSpaceReport;
02467 }
02468
02469
02470
02471
02472
02473
02478
02479
02480 REAL gCycle::CalculatePredictPosition( gPredictPositionData & data )
02481 {
02482
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
02496 predictPosition_ = pos + dirDrive * spaceAhead;
02497 predictTime = lastTime + spaceAhead/verletSpeed_;
02498 }
02499 }
02500 #else
02501
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
02512 gMaxSpaceAheadHitInfoClearer hitInfoClearer( maxSpaceHit_ );
02513
02514
02515
02516
02517
02518 gPredictPositionData predictPositionData;
02519 PreparePredictPosition( predictPositionData );
02520
02521
02522 if ( dropWallRequested_ )
02523 {
02524
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
02550
02551
02552
02553
02554 if ( !Alive() )
02555 {
02556
02557 Die( lastTime );
02558
02559
02560
02561 return true;
02562 }
02563
02564
02565 sg_ArchiveCoord( pos, 7 );
02566 sg_ArchiveReal( verletSpeed_, 7 );
02567
02568 if ( !destinationList && sn_GetNetState() == nCLIENT )
02569 {
02570
02571 gDestination* dest = tNEW(gDestination)(*this);
02572 dest->messageID = 0;
02573 dest->distance = -.001;
02574 dest->InsertIntoList(&destinationList);
02575 }
02576
02577
02578 if ( !extrapolator_ && resimulate_ )
02579 ResetExtrapolator();
02580
02581
02582 if ( extrapolator_ )
02583 {
02584 REAL dt = ( currentTime - lastTime ) * sg_syncFF / sg_syncFFSteps;
02585 #ifdef DEBUG
02586
02587
02588
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
02603 if (currentTime < lastTime)
02604 {
02605 ret = gCycleMovement::Timestep(currentTime);
02606 }
02607
02608 else if ( !currentDestination && pendingTurns.empty() )
02609 {
02610
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
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
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
02675
02676
02677
02678
02679 currentWall->Update(predictTime, PredictPosition() );
02680 }
02681
02682 return ret;
02683 }
02684
02685
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
02695 if ( clamp > 0 )
02696 relSpeed *= ( 1 + smooth * smooth / ( clamp * clamp ) );
02697
02698
02699 if ( smooth == 0 )
02700 {
02701 return;
02702 }
02703
02704
02705 REAL speed = smooth * relSpeed;
02706
02707
02708 if ( fabs( speed ) < minSpeed )
02709 speed = copysign ( minSpeed , smooth );
02710
02711
02712 if ( fabs( speed ) > fabs( smooth ) )
02713 smooth = 0;
02714 else
02715 smooth -= speed;
02716 }
02717
02718
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
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
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
02748 if ( joystick_ )
02749 {
02750 joystick_->Turn();
02751 }
02752
02753
02754 REAL rubberSpeedFactorBack = rubberSpeedFactor;
02755
02756 REAL ts=(currentTime-lastTime);
02757
02758 clamp(ts, -10, 10);
02759
02760 clamp(correctDistanceSmooth, -100, 100);
02761
02762
02763
02764
02765
02766
02767 REAL smooth = 0;
02768
02769 if ( ts > 0 )
02770 {
02771
02772 smooth = 1 - 1/( 1 + ts / sg_cycleSyncSmoothTime );
02773 }
02774
02775
02776
02777
02778
02779
02780
02781
02782
02783 TransferPositionCorrectionToDistanceCorrection();
02784
02785
02786
02787
02788
02789
02790
02791
02792
02793 REAL animts=currentTime-lastTimeAnim;
02794 if (animts<0 || !finite(animts))
02795 animts=0;
02796 else
02797 lastTimeAnim=currentTime;
02798
02799
02800 {
02801
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
02809 if ( correctPosSmooth.NormSquared() > EPS )
02810 {
02811
02812 ClampDisplacement( this, correctPosSmooth, correctPosSmooth * 2, pos );
02813
02814
02815 ClampDisplacement( this, correctPosSmooth, -correctPosSmooth, pos );
02816
02817
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
02835
02836
02837 {
02838
02839 dir=dir+dirDrive*animts*verletSpeed_*3;
02840
02841
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
02849 dir=dir*(1/sqrt(dir.NormSquared()));
02850 }
02851
02852 {
02853 eCoord oldPos = pos;
02854
02855 if ( Alive() ){
02856
02857 try
02858 {
02859
02860 REAL startBuildWallAt = spawnTime_ + sg_cycleWallTime;
02861 if ( !currentWall && currentTime > startBuildWallAt )
02862 {
02863
02864 if ( currentTime < startBuildWallAt )
02865 if ( gCycleMovement::TimestepCore( startBuildWallAt, calculateAcceleration ) )
02866 return true;
02867 calculateAcceleration = true;
02868
02869
02870 REAL lastSpawn = spawnTime_;
02871 spawnTime_ += -1E+20;
02872 DropWall();
02873 spawnTime_ = lastSpawn;
02874 dirSpawn = dirDrive;
02875 lastTurnPos_ = pos;
02876 }
02877
02878
02879 if ( gCycleMovement::TimestepCore( currentTime, calculateAcceleration ) )
02880 return true;
02881 }
02882 catch ( gCycleDeath const & death )
02883 {
02884 KillAt( death.pos_ );
02885
02886
02887 oldPos = death.pos_;
02888 }
02889 }
02890
02891
02892 if ( !Alive() )
02893 {
02894 MoveSafely(oldPos,currentTime,currentTime);
02895 Die( currentTime );
02896 }
02897 }
02898
02899
02900 sg_ArchiveCoord( pos, 7 );
02901 sg_ArchiveReal( verletSpeed_, 7 );
02902
02903 if (Alive()){
02904 #ifndef DEDICATED
02905
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
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
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
02991
02992
02993 }
02994
02995
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
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
03034 if ( rubberSpeedFactor < .99 && rubberSpeedFactorBack >= .99 )
03035 {
03036 RequestSyncOwner();
03037 }
03038
03039 if (nextSync < tSysTimeFloat() )
03040 {
03041
03042 REAL lookahead = Speed() * sg_syncIntervalEnemy*.5;
03043 if ( !sg_avoidBadOldClientSync || sg_NoLocalTunnelOnSync.Supported( Owner() ) || GetMaxSpaceAhead( lookahead ) >= lookahead )
03044 {
03045 RequestSync(false);
03046
03047
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
03060 RequestSyncOwner();
03061 }
03062 }
03063
03064 return (!Alive());
03065 }
03066
03067
03068 void gCycle::InteractWith(eGameObject *target,REAL,int){
03069
03070
03071
03072
03073
03074
03075
03076
03077
03078
03079
03080
03081
03082
03083
03084
03085
03086 }
03087
03088
03089
03090
03091
03092
03096
03097
03098 void gCycle::Die( REAL time )
03099 {
03100 if ( sn_GetNetState() == nSERVER )
03101 {
03102
03103 RequestSync( true );
03104 }
03105
03106 gCycleMovement::Die( time );
03107
03108
03109 correctPosSmooth = eCoord();
03110 TransferPositionCorrectionToDistanceCorrection();
03111 predictPosition_ = pos;
03112
03113
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
03126 if ( !Vulnerable() )
03127 {
03128 MoveSafely( deathPos, lastTime, lastTime );
03129 return;
03130 }
03131
03132
03133 ePlayerNetID const * constHunter = enemyInfluence.GetEnemy();
03134 ePlayerNetID * hunter = Player();
03135
03136
03137 if ( constHunter && constHunter->Object() )
03138 hunter = constHunter->Object()->Player();
03139
03140
03141 if ( LastTime() - enemyInfluence.GetTime() > sg_suicideTimeout )
03142 hunter = NULL;
03143
03144
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
03230 if (Player())
03231 Player()->AddScore(score_die,tOutput(),"$player_lose_frag");
03232 }
03233
03234
03235 if (Player() && hunter && gStats)
03236 {
03237
03238 if (Player()->IsHuman())
03239 {
03240 gStats->deaths->add(Player()->GetName(), 1);
03241 }
03242 if (hunter != Player() && hunter->IsHuman())
03243 {
03244 gStats->kills->add(hunter->GetName(), 1);
03245 }
03246 }
03247
03248
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
03275
03276
03277
03278
03279 if( nw == currentWall || nw == lastWall || ( nw == lastNetWall && Owner() != sn_myNetID ) )
03280 return false;
03281
03282
03283
03284
03285
03286
03287
03288 if ( gJustChecking::justChecking && w->CycleMovement() != static_cast< const gCycleMovement * >( this ) && w->Time(a) > time )
03289 {
03290
03291
03292
03293
03294 gCycle const * otherPlayer = w->Cycle();
03295 if ( !otherPlayer ||
03296 otherPlayer->Team() != this->Team() ||
03297 !otherPlayer->currentWall || w == otherPlayer->currentWall->Wall()
03298 )
03299 return false;
03300 }
03301 }
03302
03303 return gCycleMovement::EdgeIsDangerous( ww, time, a );
03304 }
03305
03306
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
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
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
03342 gJustChecking thisIsSerious;
03343
03344 if (!EdgeIsDangerous(ww,time,a) || !Alive() )
03345 {
03346
03347 if ( ( !currentWall || ww != currentWall->Wall() ) && ( !lastWall || ww != lastWall->Wall() ) )
03348 RequestSyncAll();
03349
03350
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() &&
03362 w->Cycle()->Player()->CurrentTeam() != Player()->CurrentTeam()
03363 )
03364 {
03365
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
03374 sg_HoleScore( *holer );
03375 }
03376 else
03377 {
03378
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
03397 RequestSyncOwner();
03398
03399 #ifdef DEBUG_X
03400
03401 tJUST_CONTROLLED_PTR<gCycleExtrapolator> keepOther( extrapolator_ );
03402
03403 ResetExtrapolator();
03404
03405
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
03429 otherPlayer->RequestSyncOwner();
03430
03431
03432 REAL wallDist = w->Pos(a);
03433
03434 REAL cycleDist = w->Cycle()->distance;
03435
03436 if ( wallDist > cycleDist * (1 + EPS ) )
03437 {
03438 static bool fix = false;
03439
03440 if ( fix && lastTime > se_GameTime() - 2 * Lag() - GetMaxLazyLag() )
03441 throw gCycleStop();
03442 else
03443 return;
03444 }
03445
03446
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
03455
03456
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
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
03473 dropWallRequested_ = true;
03474 }
03475 }
03476
03477
03478
03479
03480 if ( !saved && verletSpeed_ >= 0 )
03481 {
03482 REAL dt = otherTime - time;
03483
03484
03485
03486 REAL wallTimeLeft = this->ThisWallsLength()/verletSpeed_ - dt;
03487
03488 if ( wallTimeLeft < 0 )
03489 {
03490
03491 return;
03492 }
03493
03494
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;
03504 else
03505 saved = true;
03506 }
03507 }
03508
03509 if ( !saved && sn_GetNetState() != nCLIENT )
03510 {
03511
03512 if ( currentWall )
03513 otherPlayer->enemyInfluence.AddWall( currentWall->Wall(), lastTime, otherPlayer );
03514 otherPlayer->distance = wallDist;
03515 otherPlayer->DropWall();
03516 otherPlayer->KillAt( collPos );
03517
03518
03519 sg_KillFutureWalls( otherPlayer );
03520 }
03521 }
03522 }
03523 else
03524 {
03525
03526 throw gCycleDeath( collPos );
03527
03528
03529
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
03555 if ( joystick_ && joystick_->Act( Act, x ) )
03556 {
03557 return true;
03558 }
03559
03560
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
03569 Turn(-1);
03570 return true;
03571 }
03572 else if(se_turnRight==*Act && x>.5){
03573
03574 Turn(1);
03575 return true;
03576 }
03577 else if(s_brake==*Act){
03578
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
03601 static nVersionFeature sg_SyncsAreUsed( 5 );
03602
03603
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
03664
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
03686 tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
03687
03688
03689 if ( lastWall && lastNetWall && lastWall->Time(.5) > lastNetWall->Time(0) )
03690 lastNetWall = 0;
03691
03692
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
03705
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
03722 dropWallRequested_ = false;
03723 }
03724
03725 void gCycle::Kill(){
03726
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
03735
03736 if ( currentWall )
03737 {
03738
03739
03740
03741
03742
03743
03744 if ( currentWall->Pos(1) > distance || currentWall->Time(1) > lastTime )
03745 currentWall->Update( lastTime, pos );
03746
03747
03748 currentWall->CopyIntoGrid( 0 );
03749
03750 currentWall = NULL;
03751 }
03752 }
03753 }
03754
03755
03756
03757
03758
03759
03760
03761 }
03762
03763
03764
03765
03766
03767
03768
03769
03770
03771
03772
03773
03774
03775
03776
03777 static rFileTexture cycle_shad(rTextureGroups::TEX_FLOOR,"textures/shadow.png",0,0,true);
03778
03779
03780
03781
03782
03783
03784
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
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
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
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
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
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
04014 bool tailExpired=false;
04015 if ( CannotHaveList( wallsWithDisplayListMinDistance_, cycle ) )
04016 {
04017 tailExpired=true;
04018 displayList_.Clear(0);
04019 }
04020
04021 else if ( wallsWithPossibleDisplayList >= 3 ||
04022 wallsWithPossibleDisplayList * 5 > wallsInDisplayList_ )
04023 {
04024
04025 displayList_.Clear(0);
04026 }
04027 else if ( wallsWithPossibleDisplayList )
04028 {
04029
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
04044 if ( displayList_.Call() )
04045 {
04046 return;
04047 }
04048
04049
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
04073 run->ClearDisplayList(0, -1);
04074 }
04075
04076 run = next;
04077 }
04078 }
04079
04080 if ( !wallsWithDisplayList_ )
04081 {
04082 return;
04083 }
04084
04085
04086 rDisplayListFiller filler( displayList_ );
04087
04088 if ( !rDisplayList::IsRecording() )
04089 {
04090
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
04106
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
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
04170
04171 #ifdef DEBUG
04172
04173
04174
04175
04176
04177
04178
04179
04180
04181
04182
04183
04184
04185
04186
04187
04188
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
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
04310
04311 ModelMatrix();
04312
04313
04314
04315
04316
04317
04318
04319
04320 glDisable(GL_LIGHT0);
04321 glDisable(GL_LIGHT1);
04322 glDisable(GL_LIGHTING);
04323
04324
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
04390 if(headlights) {
04391 if(!cycleprograminited) {
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);
04422 glEnable(GL_FRAGMENT_PROGRAM_ARB);
04423
04424 const unsigned sensors = 32;
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;
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_));
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
04477 RenderName( cam );
04478
04479
04480
04481
04482 sr_DepthOffset(true);
04483
04484
04485 REAL h=0;
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
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
04525 glPushMatrix();
04526
04527 glColor3f(1,1,1);
04528
04529 glDisable(GL_TEXTURE_2D);
04530
04531 glTranslatef(0,0,h);
04532
04533
04534
04535 f *= 2 * sg_laggometerScale;
04536
04537 glScalef(f,f,f);
04538
04539
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
04572 glDisable(GL_TEXTURE_2D);
04573
04574 glTranslatef(0,0,h);
04575
04576
04577
04578 f *= 2 * sg_laggometerScale;
04579
04580 glScalef(f,f,f);
04581
04582
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;
04630
04631 static bool showOwnName = 0;
04632
04633 static tSettingItem< bool > sg_showOwnName( "SHOW_OWN_NAME", showOwnName );
04634
04635 static tSettingItem< REAL > sg_fadeOutNameAfter( "FADEOUT_NAME_DELAY", fadeOutNameAfter );
04636
04637 static bool sg_displayColoredNameOverCycles = true;
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;
04652 if ( !cam->RenderingMain() ) return;
04653 if ( !showOwnName && cam->Player() == this->player ) return;
04654
04655 glPushMatrix();
04656
04657 glTranslatef(0.8, 0, 2.0);
04658 glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
04659 glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);
04660 glPopMatrix();
04661
04662
04663 x = modelviewMatrix[12];
04664 y = modelviewMatrix[13];
04665 z = modelviewMatrix[14];
04666 w = modelviewMatrix[15];
04667
04668
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
04678 timeCameIntoView = 0;
04679 return;
04680 }
04681
04682 xp /= wp;
04683 yp /= wp;
04684 yp += rCHEIGHT_NORMAL;
04685
04686 if (xp <= -1 || xp >= 1 || yp <= -1 || yp >= 1) {
04687
04688 timeCameIntoView = 0;
04689 return;
04690 }
04691
04692
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
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
04742
04743
04744
04745
04746
04747
04748
04749
04750
04751
04752
04753
04754
04755
04756
04757
04758
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
04773
04774
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
04798
04799 }
04800
04801 eCoord gCycle::CamPos() const
04802 {
04803 return PredictPosition() + dir.Turn(0 ,-skew*z);
04804
04805
04806
04807
04808
04809
04810
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
04855
04856
04857
04858
04859
04860
04861
04862
04863
04864
04865
04866
04867
04868
04869
04870
04871 }
04872 #endif
04873
04874
04875
04876
04877
04878
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
04894 correctDistanceSmooth =0;
04895
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
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
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
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
04964 m << GetLastTurnPos();
04965
04966
04967 compressZeroOne.Write( m, rubber/( sg_rubberCycle + .1 ) );
04968 compressZeroOne.Write( m, 1/( 1 + rubberMalus ) );
04969
04970
04971 unsigned short lastMessageID = 0;
04972 if ( lastDestination )
04973 lastMessageID = lastDestination->messageID;
04974 m.Write(lastMessageID);
04975
04976
04977 compressZeroOne.Write( m, brakingReservoir );
04978
04979
04980
04981
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
05016 if ( !Alive() )
05017 {
05018 return;
05019 }
05020
05021
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
05029 RequestSync( Owner(), false );
05030 nextSyncOwner += syncInterval;
05031 }
05032 }
05033
05034 void gCycle::RequestSyncAll()
05035 {
05036
05037 if ( !Alive() )
05038 {
05039 return;
05040 }
05041
05042
05043 if ( sn_GetNetState() != nSERVER || Owner() == 0 )
05044 return;
05045
05046 REAL syncInterval = sg_syncIntervalEnemy;
05047 if ( nextSync < tSysTimeFloat() + syncInterval * 2.0 )
05048 {
05049
05050 RequestSync( false );
05051 nextSync += syncInterval;
05052 }
05053 }
05054
05055
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
05067 extrapolator_->TimestepCore( extrapolator_->LastTime(), true );
05068 }
05069
05070
05071 bool gCycle::Extrapolate( REAL dt )
05072 {
05073 tASSERT( extrapolator_ );
05074
05075 eCoord posBefore = extrapolator_->Position();
05076
05077
05078 REAL newTime = extrapolator_->LastTime() + dt;
05079
05080 bool ret = false;
05081
05082
05083 if ( newTime >= lastTime )
05084 {
05085
05086 eGameObject::TimestepThis( lastTime, extrapolator_ );
05087
05088
05089 gDestination* unhandledDestination = extrapolator_->GetCurrentDestination();
05090 ret = !unhandledDestination || !unhandledDestination->list;
05091
05092 newTime = lastTime;
05093 }
05094 else
05095 {
05096
05097 eGameObject::TimestepThis( newTime, extrapolator_ );
05098 }
05099
05100
05101
05102
05103
05104
05105
05106 return ret;
05107 }
05108
05109
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
05121 gSensor test( base, base_pos, displacement );
05122 test.detect(1);
05123
05124 if ( timeout == 0 )
05125 {
05126
05127 displacement = ( displacement ) * ( test.hit * .99 );
05128 break;
05129 }
05130
05131 if ( !test.ehit )
05132 {
05133
05134 break;
05135 }
05136 else
05137 {
05138 #ifdef DEBUG
05139
05140
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
05150 REAL alpha = test.ehit->Ratio( base_pos + displacement );
05151 displacement = *test.ehit->Point() + test.ehit->Vec() * alpha - base_pos;
05152
05153
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
05167 void gCycle::SyncFromExtrapolator()
05168 {
05169
05170 eCoord oldPos = pos;
05171
05172
05173
05174 tASSERT( extrapolator_ );
05175
05176
05177 CopyFrom( *extrapolator_ );
05178
05179
05180 if ( currentWall && currentWall->tBeg > spawnTime_ + sg_cycleWallTime + .01f )
05181 {
05182
05183 currentWall->beg = extrapolator_->GetLastTurnPos();
05184
05185
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
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
05202 lastTurnPos_ = extrapolator_->GetLastTurnPos();
05203
05204
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
05216 REAL dt = this->LastTime() - extrapolator_->LastTime();
05217
05218
05219 REAL trueDistance = extrapolator_->trueDistance_ + extrapolator_->Speed() * dt;
05220
05221
05222
05223
05224 distance = trueDistance;
05225 correctDistanceSmooth=0;
05226
05227
05228 TransferPositionCorrectionToDistanceCorrection();
05229
05230
05231
05232
05233
05234
05235
05236
05237
05238 extrapolator_ = 0;
05239 }
05240
05241 static int sg_useExtrapolatorSync=1;
05242 static tSettingItem<int> sg_useExtrapolatorSyncConf("EXTRAPOLATOR_SYNC",sg_useExtrapolatorSync);
05243
05244
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
05258 SyncData sync;
05259
05260 short sync_alive;
05261 unsigned short sync_wall=0;
05262
05263 eCoord new_pos = pos;
05264
05265
05266
05267 m >> sync.time;
05268
05269
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
05279
05280
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
05308 REAL preRubber, preRubberMalus;
05309 preRubber = compressZeroOne.Read( m );
05310 preRubberMalus = compressZeroOne.Read( m );
05311
05312
05313 m.Read(sync.messageID);
05314
05315
05316 sync.brakingReservoir = compressZeroOne.Read( m );
05317
05318
05319
05320 sync.rubber = preRubber * ( sg_rubberCycle + .1 );
05321 sync.rubberMalus = 1/preRubberMalus - 1;
05322
05323
05324 canUseExtrapolatorMethod = sg_useExtrapolatorSync && lastTime > 0;
05325 }
05326 else
05327 {
05328
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
05342 if ( lastSyncMessage_.time >= sync.time && lastSyncMessage_.turns >= sync.turns && sync_alive > 0 )
05343 {
05344
05345
05346
05347 return;
05348 }
05349 lastSyncMessage_ = sync;
05350
05351
05352 lastGoodPosition_ = sync.pos + ( sync.lastTurn - sync.pos ) *.01;
05353
05354 if ( eCoord::F( dirDrive, sync.dir ) > .99f*dirDrive.NormSquared() )
05355 lastGoodPosition_ = lastGoodPosition_ - this->lastDirDrive * .0001;
05356
05357
05358
05359
05360
05361
05362
05363 if ( canUseExtrapolatorMethod )
05364 {
05365
05366 if ( extrapolator_ && extrapolator_->LastTime() < lastSyncMessage_.time )
05367 {
05368 extrapolator_ = 0;
05369 }
05370 }
05371
05372
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
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
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
05406
05407
05408
05409
05410
05411 }
05412
05413
05414
05415
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
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
05439 pos = sync.pos;
05440 FindCurrentFace();
05441 }
05442
05443
05444 bool distanceBased = aft && aft != &emergency_aft && Owner() == sn_myNetID;
05445
05446 if ( canUseExtrapolatorMethod && Owner()==sn_myNetID )
05447 {
05448
05449 resimulate_ = true;
05450
05451 return;
05452 }
05453
05454 rubber = lastSyncMessage_.rubber;
05455
05456
05457
05458
05459 REAL interpolatedDistance = lastSyncMessage_.distance;
05460 if ( aft )
05461 {
05462 interpolatedDistance = aft->distance - sqrt((lastSyncMessage_.pos-aft->position).NormSquared());
05463 }
05464
05465
05466
05467
05468
05469
05470 if ( distanceBased )
05471 {
05472
05473
05474 #ifdef DEBUG
05475
05476 #endif
05477
05478 REAL ratio = (interpolatedDistance - bef->distance)/
05479 (aft->distance - bef->distance);
05480
05481 if (!finite(ratio))
05482 ratio = 0;
05483
05484
05485 REAL interpolatedTime = bef->gameTime * (1-ratio) + aft->gameTime * ratio;
05486
05487
05488
05489 REAL correctTime = ( lastSyncMessage_.time - interpolatedTime );
05490
05491 REAL correctDist = ( lastSyncMessage_.distance - interpolatedDistance );
05492
05493
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
05506
05507
05508
05509
05510
05511
05512
05513
05514
05515
05516
05517
05518
05519
05520
05521 {
05522 distance += correctDist;
05523 gDestination * run = bef;
05524 while ( run )
05525 {
05526 run->distance += correctDist;
05527 run = run->next;
05528 }
05529 }
05530
05531
05532 eCoord newPos = pos - Direction() * Speed() * correctTime;
05533 if ( currentWall )
05534 {
05535 ClampForward( newPos, currentWall->beg, Direction() );
05536 }
05537
05538
05539 {
05540 const eCoord & safePos = pos;
05541 gSensor test( this, safePos , newPos - safePos );
05542 test.detect(1);
05543 if ( test.ehit )
05544 {
05545 newPos = test.before_hit;
05546
05547
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
05559
05560
05561
05562
05563
05564
05565
05566
05567
05568
05569
05570
05571
05572 }
05573 else
05574 {
05575
05576 if ( Owner() != sn_myNetID )
05577 {
05578
05579 SyncEnemy( lastSyncMessage_.lastTurn );
05580
05581
05582 if ( currentWall )
05583 {
05584 currentWall->beg = lastSyncMessage_.lastTurn;
05585 }
05586
05587
05588 AccelerationDiscontinuity();
05589 braking = lastSyncMessage_.braking;
05590
05591
05592 lastTurnPos_ = lastSyncMessage_.lastTurn;
05593 }
05594 else
05595 {
05596
05597 eCoord oldPos = pos + correctPosSmooth;
05598 SyncEnemy( lastSyncMessage_.lastTurn );
05599 correctPosSmooth = oldPos - pos;
05600 }
05601
05602
05603 if ( !rubberSent )
05604 {
05605 rubber = lastSyncMessage_.rubber;
05606 }
05607 }
05608
05609
05610 if ( this->ID() == 0 )
05611 {
05612 correctPosSmooth = eCoord();
05613
05614
05615
05616
05617 spawnTime_=lastTime;
05618 if ( verletSpeed_ > 0 )
05619 spawnTime_ -= distance/verletSpeed_;
05620
05621
05622 if ( !sg_cycleFirstSpawnProtection && spawnTime_ <= 1.0 )
05623 {
05624 spawnTime_ = -1E+20;
05625 }
05626 dirSpawn = dirDrive;
05627
05628
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
05648
05649
05650
05651 this->SetWindingNumberWrapped( Grid()->DirectionWinding(dirDrive) );
05652
05653
05654 dirDrive = Grid()->GetDirection(windingNumberWrapped_);
05655 }
05656
05657 void gCycle::SyncEnemy ( const eCoord& begWall)
05658 {
05659
05660 tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
05661
05662 resimulate_ = false;
05663
05664
05665 bool turned = false;
05666 REAL turnDirection=( dirDrive*lastSyncMessage_.dir );
05667 REAL notTurned=eCoord::F( dirDrive, lastSyncMessage_.dir )/dirDrive.NormSquared();
05668
05669
05670 REAL lastKnownTime = lastTime;
05671
05672
05673 if ( distance > 0 && ( notTurned < .99 || this->turns < lastSyncMessage_.turns ) )
05674 {
05675
05676 eCoord crossPos = lastSyncMessage_.pos;
05677 REAL crossDist = lastSyncMessage_.distance;
05678 REAL crossTime = lastSyncMessage_.time;
05679
05680
05681
05682
05683
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
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
05710
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
05722 if(currentWall){
05723 lastWall=currentWall;
05724 currentWall->CopyIntoGrid( grid );
05725 tControlledPTR< gNetPlayerWall > bounce( currentWall );
05726 currentWall=NULL;
05727 }
05728
05729
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
05741 lastDirDrive = dirDrive;
05742
05743
05744 MoveSafely( crossPos, lastTime, crossTime );
05745 lastKnownTime = crossTime;
05746 }
05747
05748
05749 skewDot+=4*turnDirection;
05750
05751
05752
05753
05754
05755 REAL oldTime = lastTime;
05756
05757
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
05768 lastTime = lastSyncMessage_.time;
05769 if ( oldTime < 0 )
05770 oldTime = lastTime;
05771 clientside_action();
05772
05773
05774 if ( lastDirDrive * dirDrive < EPS && eCoord::F( lastDirDrive, dirDrive ) < 0 )
05775 {
05776 lastDirDrive = dirDrive.Turn(0,1);
05777 }
05778
05779
05780 if (Owner() != sn_myNetID )
05781 {
05782
05783 REAL lag = se_GameTime() - lastSyncMessage_.time;
05784 if ( lag < 0 )
05785 lag = 0;
05786
05787
05788 REAL maxLag = laggometer * 1.2;
05789 REAL minLag = laggometer * .8;
05790
05791
05792 laggometer = lag;
05793
05794
05795
05796
05797
05798
05799
05800
05801 if (
05802 sn_GetNetState()==nCLIENT && turned )
05803 {
05804 laggometerSmooth = lag;
05805
05806
05807 if ( currentWall )
05808 currentWall->Update(lastTime,pos);
05809
05810
05811 lastTimeAnim=lastTime;
05812
05813 return;
05814 }
05815 else
05816 {
05817
05818
05819
05820 if ( laggometer > maxLag )
05821 laggometer = maxLag;
05822 if ( laggometer < minLag )
05823 laggometer = minLag;
05824 }
05825 }
05826
05827
05828 {
05829 REAL laggometerSmoothBackup = this->laggometerSmooth;
05830 TimestepThis(oldTime, this);
05831 this->laggometerSmooth = laggometerSmoothBackup;
05832 }
05833 }
05834
05835
05836
05837
05838
05839
05840
05841
05842
05843
05844
05845
05846
05847
05848
05849
05850
05851
05852
05853
05854
05855
05856
05857
05858
05859
05860
05861
05862
05863
05864
05865
05866
05867
05868
05869
05870
05871
05872
05873
05874
05875
05876
05877
05878
05879
05880
05881
05882
05883
05884
05885
05886
05887
05888
05889
05890
05891
05892
05893
05894
05895
05896
05897
05898
05899
05900
05901
05902
05903
05904
05905
05906
05907
05908
05909
05910
05911
05912
05913
05914
05915
05916
05917
05918
05919
05920
05921
05922
05923
05924
05925
05926
05927
05928
05929
05930
05931
05932 void gCycle::ReceiveControl(REAL time,uActionPlayer *act,REAL x){
05933
05934 TimestepThis(time,this);
05935 Act(act,x);
05936
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
05952
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
05972
05973
05974
05975
05976
05977
05978
05979
05980
05981
05982
05983
05984
05985
05986
05987
05988
05989
05990
05991
05992
05993
05994
05995 void gCycle::RightBeforeDeath( int numTries )
05996 {
05997 if ( player )
05998 {
05999 player->RightBeforeDeath( numTries );
06000 }
06001
06002
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
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
06031
06032
06036
06037
06038
06039
06040
06041
06042
06043
06044
06045
06046
06047
06048
06049
06050
06051
06052
06053
06054
06058
06059
06060
06061
06062
06063
06064
06065
06066
06067
06068
06069
06070
06071
06075
06076
06077 bool gCycle::Vulnerable() const
06078 {
06079 return Alive() && lastTime > spawnTime_ + sg_cycleInvulnerableTime;
06080 }