src/tron/gCycleMovement.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2004  Armagetron Advanced Team (http://sourceforge.net/projects/armagetronad/) 
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023   
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "rSDL.h"
00029 
00030 // declaration
00031 #ifndef         ARMAGETRONAD_SRC_TRON_GCYCLEMOVEMENT_H_INCLUDED
00032 #include        "gCycleMovement.h"
00033 #endif
00034 
00035 #include "tMath.h"
00036 
00037 #include "nConfig.h"
00038 
00039 #include "ePlayer.h"
00040 #include "eDebugLine.h"
00041 #include "eGrid.h"
00042 #include "eLagCompensation.h"
00043 #include "eTeam.h"
00044 
00045 #include "eTimer.h"
00046 
00047 #include "gWall.h"
00048 #include "gSensor.h"
00049 #include "gAIBase.h"
00050 
00051 #include "tRecorder.h"
00052 
00053 // #define DEBUG_RUBBER
00054 
00055 #ifdef DEBUG_RUBBER
00056 #include <fstream>
00057 #endif
00058 
00059 #undef  INLINE_DEF
00060 #define INLINE_DEF
00061 
00062 #ifndef DEDICATED
00063 #define MAXRUBBER 1
00064 #else
00065 #define MAXRUBBER 3
00066 #endif
00067 
00068 #ifdef DEBUG
00069 #define DEBUGOUTPUT
00070 #endif
00071 
00072 #ifdef DEBUGOUTPUT
00073 #include "tSysTime.h"
00074 static int sg_cycleDebugPrintLevel = 0;
00075 #endif
00076 
00077 // get rubber values in effect
00078 void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness );
00079 
00080 //  *****************************************************************
00081 
00082 static void sg_ArchiveCoord( eCoord & coord, int level )
00083 {
00084     static char const * section = "_COORD";
00085     tRecorderSync< eCoord >::Archive( section, level, coord );
00086 }
00087 
00088 static void sg_ArchiveReal( REAL & real, int level )
00089 {
00090     static char const * section = "_REAL";
00091     tRecorderSync< REAL >::Archive( section, level, real );
00092 }
00093 
00094 //  *****************************************************************
00095 // version feature indicating that proper verlet integration should be used
00096 static nVersionFeature sg_verletIntegration( 7 );
00097 
00098 // strength of brake
00099 REAL sg_brakeCycle=30;
00100 static nSettingItem<REAL> c_ab("CYCLE_BRAKE",
00101                                sg_brakeCycle);
00102 
00103 REAL sg_cycleBrakeRefill  = 0.0;
00104 REAL sg_cycleBrakeDeplete = 0.0;
00105 
00106 // it should look this way, but a VersionFeature is used to control the application of these settings,
00107 // so the nonwatched itmes suffice.
00108 static nSettingItemWatched<REAL> sg_cycleBrakeRefillConf("CYCLE_BRAKE_REFILL",sg_cycleBrakeRefill, nConfItemVersionWatcher::Group_Annoying, 2 );
00109 static nSettingItemWatched<REAL> sg_cycleBrakeDepleteConf("CYCLE_BRAKE_DEPLETE",sg_cycleBrakeDeplete, nConfItemVersionWatcher::Group_Annoying, 2 );
00110 
00111 // static nSettingItem<REAL> sg_cycleBrakeRefillConf("CYCLE_BRAKE_REFILL",sg_cycleBrakeRefill );
00112 // static nSettingItem<REAL> sg_cycleBrakeDepleteConf("CYCLE_BRAKE_DEPLETE",sg_cycleBrakeDeplete );
00113 
00114 // cycle width: it won't fit into tunnels that are smaller than this
00115 REAL sg_cycleWidth = 0;
00116 static nSettingItemWatched<REAL> c_cw("CYCLE_WIDTH",
00117                                       sg_cycleWidth, nConfItemVersionWatcher::Group_Bumpy, 14 );
00118 
00119 REAL sg_cycleWidthSide = 0;
00120 static nSettingItemWatched<REAL> c_cws("CYCLE_WIDTH_SIDE",
00121                                        sg_cycleWidthSide, nConfItemVersionWatcher::Group_Bumpy, 14 );
00122 // calculate the gridning distance sparks should start flying at
00123 REAL sg_GetSparksDistance()
00124 {
00125     if ( sg_cycleWidth < 2 * sg_cycleWidthSide )
00126         return sg_cycleWidth;
00127     else if ( sg_cycleWidthSide > 0 )
00128         return sg_cycleWidthSide * 2;
00129     else
00130         return .25; // return 0.2.8.2 default
00131 }
00132 
00133 // amout of rubber you use per meter when you squeeze inside a too tight tunnel
00134 // when just barely squeezed
00135 REAL sg_cycleWidthRubberMin = 1;
00136 static nSettingItemWatched<REAL> c_cwrmax("CYCLE_WIDTH_RUBBER_MIN",
00137         sg_cycleWidthRubberMin, nConfItemVersionWatcher::Group_Bumpy, 14 );
00138 // when squeezed to a point
00139 REAL sg_cycleWidthRubberMax = 1;
00140 static nSettingItemWatched<REAL> c_cwrmin("CYCLE_WIDTH_RUBBER_MAX",
00141         sg_cycleWidthRubberMax, nConfItemVersionWatcher::Group_Bumpy, 14 );
00142 
00143 // base speed of cycle im m/s
00144 static REAL sg_speedCycle=10;
00145 static nSettingItem<REAL> c_s("CYCLE_SPEED",sg_speedCycle);
00146 
00147 // minimal speed
00148 static REAL sg_speedCycleMin=.25;
00149 static nSettingItemWatched<REAL> c_smin("CYCLE_SPEED_MIN",
00150                                         sg_speedCycleMin,
00151                                         nConfItemVersionWatcher::Group_Bumpy,
00152                                         9);
00153 
00154 // minimal speed
00155 static REAL sg_speedCycleMax=0;
00156 static nSettingItemWatched<REAL> c_smax("CYCLE_SPEED_MAX",
00157                                         sg_speedCycleMax,
00158                                         nConfItemVersionWatcher::Group_Bumpy,
00159                                         14);
00160 
00161 REAL sg_speedCycleDecayBelow = 5;
00162 static nSettingItemWatched<REAL> c_sdb("CYCLE_SPEED_DECAY_BELOW",
00163                                        sg_speedCycleDecayBelow,
00164                                        nConfItemVersionWatcher::Group_Bumpy,
00165                                        8);
00166 
00167 REAL sg_speedCycleDecayAbove = .1;
00168 static nSettingItemWatched<REAL> c_sda("CYCLE_SPEED_DECAY_ABOVE",
00169                                        sg_speedCycleDecayAbove,
00170                                        nConfItemVersionWatcher::Group_Bumpy,
00171                                        8);
00172 
00173 // start speed of cycle im m/s
00174 static REAL sg_speedCycleStart=20;
00175 static tSettingItem<REAL> c_st("CYCLE_START_SPEED",
00176                                sg_speedCycleStart);
00177 
00178 // min time between turns
00179 REAL sg_delayCycle = .1;
00180 static nSettingItem<REAL> c_d("CYCLE_DELAY",
00181                               sg_delayCycle);
00182 //bonus for turns in the same direcion
00183 REAL sg_delayCycleDoublebindBonus = 1.;
00184 static nSettingItemWatched<REAL> c_d_d_b("CYCLE_DELAY_DOUBLEBIND_BONUS",
00185         sg_delayCycleDoublebindBonus, nConfItemVersionWatcher::Group_Bumpy, 14 );
00186 
00187 // number of turns buffered exactly
00188 int sg_cycleTurnMemory = 3;
00189 static tSettingItem<int> c_tm("CYCLE_TURN_MEMORY",
00190                               sg_cycleTurnMemory);
00191 
00192 REAL sg_delayCycleTimeBased = 1;
00193 static nSettingItemWatched<REAL> c_dt("CYCLE_DELAY_TIMEBASED",
00194                                       sg_delayCycleTimeBased,
00195                                       nConfItemVersionWatcher::Group_Bumpy,
00196                                       7);
00197 
00198 // extra factor to sg_delayCycle applied locally
00199 #ifdef DEDICATED
00200 REAL sg_delayCycleBonus=.95;
00201 static tSettingItem<REAL> c_db("CYCLE_DELAY_BONUS",
00202                                sg_delayCycleBonus);
00203 #else
00204 REAL sg_delayCycleBonus=1;
00205 #endif
00206 
00207 REAL sg_cycleTurnSpeedFactor=.95;
00208 static nSettingItemWatched<REAL> c_ctf("CYCLE_TURN_SPEED_FACTOR",
00209                                        sg_cycleTurnSpeedFactor,
00210                                        nConfItemVersionWatcher::Group_Bumpy,
00211                                        7);
00212 
00213 // wall acceleration
00214 static REAL sg_accelerationCycle=10;
00215 static nSettingItem<REAL> c_a("CYCLE_ACCEL",
00216                               sg_accelerationCycle);
00217 
00218 // acceleration multiplicators
00219 REAL sg_accelerationCycleSelf = 1;
00220 static nSettingItemWatched<REAL> c_aco("CYCLE_ACCEL_SELF",
00221                                        sg_accelerationCycleSelf,
00222                                        nConfItemVersionWatcher::Group_Bumpy,
00223                                        8);
00224 
00225 REAL sg_accelerationCycleTeam = 1;
00226 static nSettingItemWatched<REAL> c_act("CYCLE_ACCEL_TEAM",
00227                                        sg_accelerationCycleTeam,
00228                                        nConfItemVersionWatcher::Group_Bumpy,
00229                                        14);
00230 
00231 REAL sg_accelerationCycleEnemy = 1;
00232 static nSettingItemWatched<REAL> c_ace("CYCLE_ACCEL_ENEMY",
00233                                        sg_accelerationCycleEnemy,
00234                                        nConfItemVersionWatcher::Group_Bumpy,
00235                                        14);
00236 
00237 REAL sg_accelerationCycleRim = 0;
00238 static nSettingItemWatched<REAL> c_acr("CYCLE_ACCEL_RIM",
00239                                        sg_accelerationCycleRim,
00240                                        nConfItemVersionWatcher::Group_Bumpy,
00241                                        8);
00242 
00243 REAL sg_accelerationCycleSlingshot = 1;
00244 static nSettingItemWatched<REAL> c_acs("CYCLE_ACCEL_SLINGSHOT",
00245                                        sg_accelerationCycleSlingshot,
00246                                        nConfItemVersionWatcher::Group_Bumpy,
00247                                        8);
00248 
00249 REAL sg_accelerationCycleTunnel = 1;
00250 static nSettingItemWatched<REAL> c_acu("CYCLE_ACCEL_TUNNEL",
00251                                        sg_accelerationCycleTunnel,
00252                                        nConfItemVersionWatcher::Group_Bumpy,
00253                                        14);
00254 
00255 // acceleration offset
00256 static REAL sg_accelerationCycleOffs=2;
00257 static nSettingItem<REAL> c_ao("CYCLE_ACCEL_OFFSET",
00258                                sg_accelerationCycleOffs);
00259 
00260 
00261 // when is a eWall near?
00262 static REAL sg_nearCycle=6;
00263 static nSettingItem<REAL> c_n("CYCLE_WALL_NEAR",
00264                               sg_nearCycle);
00265 
00266 // boost settings, absolute speed increase applied when you break from a wall
00267 REAL sg_boostCycleSelf = 0;
00268 static nSettingItemWatched<REAL> c_bco("CYCLE_BOOST_SELF",
00269                                        sg_boostCycleSelf,
00270                                        nConfItemVersionWatcher::Group_Bumpy,
00271                                        14);
00272 
00273 REAL sg_boostCycleTeam = 0;
00274 static nSettingItemWatched<REAL> c_bct("CYCLE_BOOST_TEAM",
00275                                        sg_boostCycleTeam,
00276                                        nConfItemVersionWatcher::Group_Bumpy,
00277                                        14);
00278 
00279 REAL sg_boostCycleEnemy = 0;
00280 static nSettingItemWatched<REAL> c_bce("CYCLE_BOOST_ENEMY",
00281                                        sg_boostCycleEnemy,
00282                                        nConfItemVersionWatcher::Group_Bumpy,
00283                                        14);
00284 
00285 REAL sg_boostCycleRim = 0;
00286 static nSettingItemWatched<REAL> c_bcr("CYCLE_BOOST_RIM",
00287                                        sg_boostCycleRim,
00288                                        nConfItemVersionWatcher::Group_Bumpy,
00289                                        14);
00290 
00291 // boostFactor settings, speed factor when you break from a wall
00292 REAL sg_boostFactorCycleSelf = 1;
00293 static nSettingItemWatched<REAL> c_bfco("CYCLE_BOOSTFACTOR_SELF",
00294                                         sg_boostFactorCycleSelf,
00295                                         nConfItemVersionWatcher::Group_Bumpy,
00296                                         14);
00297 
00298 REAL sg_boostFactorCycleTeam = 1;
00299 static nSettingItemWatched<REAL> c_bfct("CYCLE_BOOSTFACTOR_TEAM",
00300                                         sg_boostFactorCycleTeam,
00301                                         nConfItemVersionWatcher::Group_Bumpy,
00302                                         14);
00303 
00304 REAL sg_boostFactorCycleEnemy = 1;
00305 static nSettingItemWatched<REAL> c_bfce("CYCLE_BOOSTFACTOR_ENEMY",
00306                                         sg_boostFactorCycleEnemy,
00307                                         nConfItemVersionWatcher::Group_Bumpy,
00308                                         14);
00309 
00310 REAL sg_boostFactorCycleRim = 1;
00311 static nSettingItemWatched<REAL> c_bfcr("CYCLE_BOOSTFACTOR_RIM",
00312                                         sg_boostFactorCycleRim,
00313                                         nConfItemVersionWatcher::Group_Bumpy,
00314                                         14);
00315 
00316 // tolerance for packet loss
00317 static REAL sg_packetLossTolerance = 0;
00318 static tSettingItem<REAL> conf_packetLossTolerance ("CYCLE_PACKETLOSS_TOLERANCE", sg_packetLossTolerance);
00319 
00320 // tolerance for packet misses: if an intermediate packet is missing
00321 static REAL sg_packetMissTolerance = 3;
00322 static tSettingItem<REAL> conf_packetMissTolerance ("CYCLE_PACKETMISS_TOLERANCE", sg_packetMissTolerance);
00323 
00324 // niceness when crashing a eWall
00325 REAL sg_rubberCycle=MAXRUBBER;
00326 static nSettingItem<REAL> c_r("CYCLE_RUBBER",
00327                               sg_rubberCycle);
00328 
00329 REAL sg_rubberCycleTimeBased = 0;
00330 static nSettingItemWatched<REAL> c_rtb("CYCLE_RUBBER_TIMEBASED",
00331                                        sg_rubberCycleTimeBased,
00332                                        nConfItemVersionWatcher::Group_Visual,
00333                                        7);
00334 
00335 // allow legacy rubber code
00336 bool sg_rubberCycleLegacy=true;
00337 static nSettingItem<bool> c_rl("CYCLE_RUBBER_LEGACY",
00338                                sg_rubberCycleLegacy);
00339 
00340 // niceness when crashing a eWall, influence of your ping
00341 static REAL sg_rubberCyclePing=3;
00342 static nSettingItem<REAL> c_rp("CYCLE_PING_RUBBER",
00343                                sg_rubberCyclePing);
00344 
00345 // timescale rubber is restored on
00346 REAL sg_rubberCycleTime=10;
00347 static nSettingItemWatched<REAL> c_rt("CYCLE_RUBBER_TIME",
00348                                       sg_rubberCycleTime,
00349                                       nConfItemVersionWatcher::Group_Visual,
00350                                       7);
00351 
00352 // max logarithmic approximation speed when rubber is in effect
00353 static REAL sg_rubberCycleSpeed=40;
00354 static nSettingItemWatched<REAL> c_rs("CYCLE_RUBBER_SPEED",
00355                                       sg_rubberCycleSpeed,
00356                                       nConfItemVersionWatcher::Group_Cheating,
00357                                       4);
00358 
00359 #ifdef DEDICATED
00360 #define MINDISTANCE_FACTOR 1
00361 #else
00362 #define MINDISTANCE_FACTOR 0
00363 #endif
00364 
00365 // minimal distance to a wall rubber will allow
00366 static REAL sg_rubberCycleMinDistance=.001 * MINDISTANCE_FACTOR;
00367 static nSettingItemWatched<REAL> c_rmd("CYCLE_RUBBER_MINDISTANCE",
00368                                        sg_rubberCycleMinDistance,
00369                                        nConfItemVersionWatcher::Group_Annoying,
00370                                        4);
00371 
00372 // the length of the wall times this value is added to the previous value
00373 static REAL sg_rubberCycleMinDistanceRatio=.0001 * MINDISTANCE_FACTOR;
00374 static nSettingItemWatched<REAL> c_rmdr("CYCLE_RUBBER_MINDISTANCE_RATIO",
00375                                         sg_rubberCycleMinDistanceRatio,
00376                                         nConfItemVersionWatcher::Group_Annoying,
00377                                         4);
00378 
00379 // on an empty rubber reservoir, this value is added as well
00380 static REAL sg_rubberCycleMinDistanceReservoir=0.005 * MINDISTANCE_FACTOR;
00381 static nSettingItemWatched<REAL> c_rmdres("CYCLE_RUBBER_MINDISTANCE_RESERVOIR",
00382         sg_rubberCycleMinDistanceReservoir,
00383         nConfItemVersionWatcher::Group_Annoying,
00384         7);
00385 
00386 // and on a not so well prepared grind (if your last turn was not too long ago), this value.
00387 static REAL sg_rubberCycleMinDistanceUnprepared=0.005 * MINDISTANCE_FACTOR;
00388 static nSettingItemWatched<REAL> c_rmdup("CYCLE_RUBBER_MINDISTANCE_UNPREPARED",
00389         sg_rubberCycleMinDistanceUnprepared ,
00390         nConfItemVersionWatcher::Group_Annoying,
00391         7);
00392 
00393 // this is the distance the preparation lengt is compared with
00394 static REAL sg_rubberCycleMinDistancePreparation=0.2;
00395 static nSettingItemWatched<REAL> c_rmdp("CYCLE_RUBBER_MINDISTANCE_PREPARATION",
00396                                         sg_rubberCycleMinDistancePreparation,
00397                                         nConfItemVersionWatcher::Group_Annoying,
00398                                         7);
00399 
00400 // when connecting to older peers that may be rippable, multiply the minimum distance by this number if the
00401 // wall in front of you is the rim
00402 static REAL sg_rubberCycleMinDistanceLegacy=1;
00403 static nSettingItem<REAL> c_rmdl("CYCLE_RUBBER_MINDISTANCE_LEGACY",
00404                                  sg_rubberCycleMinDistanceLegacy);
00405 
00406 // this feature tracks whether the rip bug was fixed
00407 static nVersionFeature sg_nonRippable(4);
00408 
00409 // when adjusting to a wall, allow to get closer at least by this amount
00410 static REAL sg_rubberCycleMinAdjust=.05;
00411 static nSettingItemWatched<REAL> c_rma("CYCLE_RUBBER_MINADJUST",
00412                                        sg_rubberCycleMinAdjust,
00413                                        nConfItemVersionWatcher::Group_Annoying,
00414                                        4);
00415 
00416 // during this fraction of the cycle delay time, rubber effectiveness will be reduced...
00417 static REAL sg_rubberCycleDelay=0;
00418 static nSettingItemWatched<REAL> c_rcd("CYCLE_RUBBER_DELAY",
00419                                        sg_rubberCycleDelay,
00420                                        nConfItemVersionWatcher::Group_Visual,
00421                                        6);
00422 
00423 // by this factor ( meaning that rubber usage goes up by the inverse )
00424 static REAL sg_rubberCycleDelayBonus=.5;
00425 static nSettingItemWatched<REAL> c_rcdb("CYCLE_RUBBER_DELAY_BONUS",
00426                                         sg_rubberCycleDelayBonus,
00427                                         nConfItemVersionWatcher::Group_Visual,
00428                                         6);
00429 
00430 // rubber usage gets increased by this amout after each turn...
00431 static REAL sg_rubberCycleMalusTurn=0;
00432 static nSettingItemWatched<REAL> c_rctm("CYCLE_RUBBER_MALUS_TURN",
00433                                         sg_rubberCycleMalusTurn,
00434                                         nConfItemVersionWatcher::Group_Visual,
00435                                         6);
00436 
00437 // but the effect wears off after about this many seconds
00438 static REAL sg_rubberCycleMalusTime=5;
00439 static nSettingItem<REAL> c_rcmd("CYCLE_RUBBER_MALUS_TIME",
00440                                  sg_rubberCycleMalusTime);
00441 
00442 // time tolerance when interpreting client commands
00443 static REAL sg_timeTolerance=.1;
00444 static nSettingItemWatched<REAL> c_tt( "CYCLE_TIME_TOLERANCE",
00445                                        sg_timeTolerance,
00446                                        nConfItemVersionWatcher::Group_Visual,
00447                                        6 );
00448 
00449 static int sg_cycleMaxRefCount = 30000;
00450 static tSettingItem<int> conf_sgCycleMaxRefCount ("CYCLE_MAX_REFCOUNT", sg_cycleMaxRefCount );
00451 
00452 static void blocks(const gSensor &s, const gCycleMovement *c, int lr)
00453 {
00454     if ( nCLIENT == sn_GetNetState() )
00455         return;
00456 
00457     if (s.type == gSENSOR_RIM)
00458         gAIPlayer::CycleBlocksRim(c, lr);
00459     else if (s.type == gSENSOR_TEAMMATE || s.type == gSENSOR_ENEMY && s.ehit)
00460     {
00461         gPlayerWall *w = dynamic_cast<gPlayerWall*>(s.ehit->GetWall());
00462         if (w)
00463         {
00464             // int turn     = c->Grid()->WindingNumber();
00465             //    int halfTurn = turn >> 1;
00466 
00467             // calculate the winding number.
00468             int windingBefore = c->WindingNumber();  // we start driving in c's direction
00469             // we need to make a sharp turn in the lr-direction
00470             //    windingBefore   += lr * halfTurn;
00471 
00472             // after the transfer, we need to drive in the direction of the other
00473             // wall:
00474             int windingAfter = w->WindingNumber();
00475 
00476             // if the other wall drives in the opposite direction, we
00477             // need to turn around again:
00478             //    if (s.lr == lr)
00479             // windingAfter -= lr * halfTurn;
00480 
00481             // make the winding difference a multiple of the winding number
00482             /*
00483               int compensation = ((windingAfter - windingBefore - halfTurn) % turn)
00484               + halfTurn;
00485               while (compensation < -halfTurn)
00486               compensation += turn;
00487             */
00488 
00489             // only if the two walls are parallel/antiparallel, there is true blocking.
00490             if (((windingBefore - windingAfter) & 1) == 0)
00491                 gAIPlayer::CycleBlocksWay(c, w->Cycle(),
00492                                           lr, s.lr,
00493                                           w->Pos(s.ehit->Ratio(s.before_hit)),
00494                                           - windingAfter + windingBefore);
00495         }
00496     }
00497 }
00498 
00499 // enemy influence settings
00500 static REAL sg_enemyFriendTimePenalty = 2500.0f;  
00501 // REAL sg_enemySelfTimePenalty = 5000.0f;    //!< penalty for self influence
00502 static REAL sg_enemyDeadTimePenalty = 0.0f;       
00503 REAL sg_suicideTimeout = 10000.0f;         
00504 static REAL sg_enemyCurrentTimeInfluence = 0.0f;  
00505 
00506 static tSettingItem<REAL> sg_enemyFriendTimePenaltyConf( "ENEMY_TEAMMATE_PENALTY", sg_enemyFriendTimePenalty );
00507 static tSettingItem<REAL> sg_enemyDeadTimePenaltyConf( "ENEMY_DEAD_PENALTY", sg_enemyDeadTimePenalty );
00508 static tSettingItem<REAL> sg_suicideTimeoutConf( "ENEMY_SUICIDE_TIMEOUT", sg_suicideTimeout );
00509 static tSettingItem<REAL> sg_enemyCurrentTimeInfluenceConf( "ENEMY_CURRENTTIME_INFLUENCE", sg_enemyCurrentTimeInfluence );
00510 
00511 // the last enemy possibly responsible for our death
00512 const ePlayerNetID* gEnemyInfluence::GetEnemy() const
00513 {
00514     return lastEnemyInfluence.GetPointer();
00515 }
00516 
00517 REAL gEnemyInfluence::GetTime() const
00518 {
00519     return lastTime;
00520 }
00521 
00522 gEnemyInfluence::gEnemyInfluence()
00523 {
00524     lastTime = -sg_suicideTimeout;
00525 }
00526 
00527 // add the result of the sensor scan to our data
00528 void gEnemyInfluence::AddSensor( const gSensor& sensor, REAL timePenalty, gCycleMovement * thisCycle )
00529 {
00530     // the client has no need for this, it does not execute AI code
00531     if ( sn_GetNetState() == nCLIENT )
00532         return;
00533 
00534     // check if the sensor hit an enemy wall
00535     // if ( sensor.type != gSENSOR_ENEMY )
00536     //    return;
00537 
00538     // get the wall
00539     if ( !sensor.ehit )
00540         return;
00541 
00542     eWall* wall = sensor.ehit->GetWall();
00543     if ( !wall )
00544         return;
00545 
00546     AddWall( wall, sensor.before_hit, timePenalty, thisCycle );
00547 }
00548 
00549 // add the interaction with a wall to our data
00550 void gEnemyInfluence::AddWall( const eWall * wall, eCoord const & pos, REAL timePenalty, gCycleMovement * thisCycle )
00551 {
00552     // the client has no need for this, it does not execute AI code
00553     if ( sn_GetNetState() == nCLIENT )
00554         return;
00555 
00556     // see if it is a player wall
00557     gPlayerWall const * playerWall = dynamic_cast<gPlayerWall const *>( wall );
00558     if ( !playerWall )
00559         return;
00560 
00561     // get the approximate time the wall was drawn
00562     REAL alpha = .5f;
00563     // try to get a more accurate value
00564     if ( playerWall->Edge() )
00565     {
00566         // get the position of the collision point
00567         alpha = playerWall->Edge()->Ratio( pos );
00568     }
00569     REAL timeBuilt = playerWall->Time( 0.5f );
00570 
00571     AddWall( playerWall, timeBuilt - timePenalty, thisCycle );
00572 }
00573 
00574 // add the interaction with a wall to our data
00575 void gEnemyInfluence::AddWall( const gPlayerWall * wall, REAL timeBuilt, gCycleMovement * thisCycle )
00576 {
00577     // the client has no need for this, it does not execute AI code
00578     if ( sn_GetNetState() == nCLIENT )
00579         return;
00580 
00581     if ( !wall )
00582         return;
00583 
00584     // get the cycle
00585     gCycle *cycle = wall->Cycle();
00586     if ( !cycle )
00587         return;
00588 
00589     // don't count self influence
00590     if ( thisCycle == cycle )
00591         return;
00592 
00593     REAL time = timeBuilt;
00594     if ( thisCycle )
00595     {
00596         REAL currentTime = thisCycle->LastTime();
00597         time += ( currentTime - time ) * sg_enemyCurrentTimeInfluence;
00598     }
00599 
00600     // get the player
00601     ePlayerNetID* player = cycle->Player();
00602     if ( !player )
00603         return;
00604 
00605     // don't accept milkers.
00606     if ( thisCycle && !ePlayerNetID::Enemies( thisCycle->Player(), player ) )
00607     {
00608         return;
00609     }
00610 
00611     // if the player is not our enemy, add extra time penalty
00612     if ( thisCycle->Player() && player->CurrentTeam() == thisCycle->Player()->CurrentTeam() )
00613     {
00614         // the time shall be at most the time of the last turn, it should count as suicide if
00615         // I drive into your three mile long wall
00616         if ( time > cycle->GetLastTurnTime() )
00617             time = cycle->GetLastTurnTime();
00618         time -= sg_enemyFriendTimePenalty;
00619     }
00620     const ePlayerNetID* pInfluence = this->lastEnemyInfluence.GetPointer();
00621 
00622     // calculate effective last time. Add malus if the player is dead.
00623     REAL lastEffectiveTime = lastTime;
00624     if ( !pInfluence  || !pInfluence->Object() ||  !pInfluence->Object()->Alive() )
00625     {
00626         lastEffectiveTime -= sg_enemyDeadTimePenalty;
00627     }
00628 
00629     // same for the current influence
00630     REAL effectiveTime = time;
00631     if ( !cycle->Alive() )
00632     {
00633         effectiveTime -= sg_enemyDeadTimePenalty;
00634     }
00635 
00636     // if the new influence is newer, take it.
00637     if ( effectiveTime > lastEffectiveTime || !bool(lastEnemyInfluence) )
00638     {
00639         lastEnemyInfluence = player;
00640         lastTime                   = time;
00641     }
00642 }
00643 
00644 // *******************************************************************************************
00645 // *
00646 // *    gCycleMovement
00647 // *
00648 // *******************************************************************************************
00651 // *******************************************************************************************
00652 
00653 // gCycleMovement::gCycleMovement()
00654 // {
00655 //      this->Init_gCycleCore();
00656 // }
00657 
00658 // *******************************************************************************************
00659 // *
00660 // *    gCycleMovement
00661 // *
00662 // *******************************************************************************************
00666 // *******************************************************************************************
00667 
00668 // gCycleMovement::gCycleMovement( gCycleMovement const & other )
00669 // {
00670 //      this->Init_gCycleCore();
00671 //      this->CopyFrom( other );
00672 //}
00673 
00674 // *******************************************************************************************
00675 // *
00676 // *    operator =
00677 // *
00678 // *******************************************************************************************
00683 // *******************************************************************************************
00684 
00685 gCycleMovement & gCycleMovement::operator= ( gCycleMovement const & other )
00686 {
00687     this->CopyFrom( other );
00688     return *this;
00689 }
00690 
00691 // *******************************************************************************************
00692 // *
00693 // *    init_gCycleCore
00694 // *
00695 // *******************************************************************************************
00698 // *******************************************************************************************
00699 
00700 //void gCycleMovement::Init_gCycleCore()
00701 //{
00702 //    assert(0); // TODO: implement me
00703 //}
00704 
00705 // *******************************************************************************************
00706 // *
00707 // *    finit_gCycleCore
00708 // *
00709 // *******************************************************************************************
00712 // *******************************************************************************************
00713 
00714 //void gCycleMovement::Finit_gCycleCore()
00715 //{
00716 //    assert(0); // TODO: implement me
00717 //}
00718 
00719 // ----------------------------------------------------------------------------------------------------------
00720 
00721 static float sg_speedMultiplier = 1.0f;
00722 static nSettingItem<float> conf_mult ("REAL_CYCLE_SPEED_FACTOR", sg_speedMultiplier);
00723 
00724 // *******************************************************************************************
00725 // *
00726 // *    RubberSpeed
00727 // *
00728 // *******************************************************************************************
00732 // *******************************************************************************************
00733 
00734 float gCycleMovement::RubberSpeed()
00735 {
00736     return sg_rubberCycleSpeed;
00737 }
00738 
00739 // *******************************************************************************************
00740 // *
00741 // *    SpeedMultiplier
00742 // *
00743 // *******************************************************************************************
00747 // *******************************************************************************************
00748 
00749 float gCycleMovement::SpeedMultiplier( void )
00750 {
00751     return sg_speedMultiplier;
00752 }
00753 
00754 // *******************************************************************************************
00755 // *
00756 // *    SetSpeedMultiplier
00757 // *
00758 // *******************************************************************************************
00762 // *******************************************************************************************
00763 
00764 void gCycleMovement::SetSpeedMultiplier( REAL mul )
00765 {
00766     conf_mult.Set( mul );
00767 }
00768 
00769 // for the given maximal acceleration, calculate the top speed
00770 static REAL sg_MaxSpeed( REAL maxAcceleration )
00771 {
00772     if ( sg_speedCycleDecayAbove > 0 )
00773         return sg_speedCycle + maxAcceleration / sg_speedCycleDecayAbove;
00774     else
00775         return sg_speedCycle * 100;
00776 }
00777 
00778 // *******************************************************************************************
00779 // *
00780 // *    MaximalSpeed
00781 // *
00782 // *******************************************************************************************
00786 // *******************************************************************************************
00787 
00788 float gCycleMovement::MaximalSpeed( void )
00789 {
00790     // determine the maximal acceleration by walls
00791     REAL maxWallAcceleration = 0;
00792     REAL wallAcceleration = sg_accelerationCycleTeam * sg_accelerationCycle;
00793     if ( wallAcceleration > maxWallAcceleration )
00794         maxWallAcceleration = wallAcceleration;
00795     wallAcceleration = sg_accelerationCycleEnemy * sg_accelerationCycle;
00796     if ( wallAcceleration > maxWallAcceleration )
00797         maxWallAcceleration = wallAcceleration;
00798     wallAcceleration = sg_accelerationCycleRim * sg_accelerationCycle;
00799     if ( wallAcceleration > maxWallAcceleration )
00800         maxWallAcceleration = wallAcceleration;
00801 
00802     // self acceleration is tricky: slingshot countermeasures have to be taken into account
00803     REAL wallAccelerationSelf = sg_accelerationCycleSelf * sg_accelerationCycle;
00804 
00805     {
00806         // different combinations are now possible to get a maximum. It could be a single wall:
00807         REAL wallAccelerationSingle = maxWallAcceleration;
00808         if ( wallAccelerationSingle < wallAccelerationSelf )
00809             wallAccelerationSingle = wallAccelerationSelf;
00810 
00811         // it could be a slingshot, one arbitrary wall and one own wall:
00812         REAL wallAccelerationSlingshot = ( wallAccelerationSingle + wallAccelerationSelf ) * sg_accelerationCycleSlingshot;
00813 
00814         // or a tunnel, two foreign walls:
00815         REAL wallAccelerationTunnel = ( maxWallAcceleration ) * sg_accelerationCycleTunnel;
00816 
00817 
00818         // take the maximum
00819         if ( maxWallAcceleration < wallAccelerationSlingshot )
00820             maxWallAcceleration = wallAccelerationSlingshot;
00821         if ( maxWallAcceleration < wallAccelerationTunnel )
00822             maxWallAcceleration = wallAccelerationTunnel;
00823     }
00824 
00825     // use wall accel formula to take wall distance into account
00826     maxWallAcceleration *= ( 1/sg_accelerationCycleOffs - 1/(sg_accelerationCycleOffs+sg_nearCycle ) );
00827 
00828     // maximal sustainable speed from that
00829     REAL maxSpeed = sg_MaxSpeed( maxWallAcceleration );
00830 
00831     // what if the brake is a booster?
00832     if ( sg_brakeCycle < 0 )
00833     {
00834         // what if it can be applied infinitely?
00835         REAL maxSpeedBoost = sg_MaxSpeed( maxWallAcceleration - sg_brakeCycle );
00836 
00837         // but the boost can permanently only be applied at this efficiency
00838         REAL efficiency = 1;
00839         if ( sg_cycleBrakeRefill + sg_cycleBrakeDeplete > 0 )
00840             efficiency = sg_cycleBrakeRefill/(sg_cycleBrakeRefill + sg_cycleBrakeDeplete);
00841 
00842         // maximal permanent speed
00843         REAL maxSpeedPermanent = sg_MaxSpeed( maxWallAcceleration - sg_brakeCycle*efficiency );
00844 
00845         // if the boost is limited, don't let the result be larger than what you can achieve by
00846         // accelerating from the max speed attainable from walls
00847         if ( sg_cycleBrakeDeplete > 0 )
00848         {
00849             REAL boostTime = 1/sg_cycleBrakeDeplete;
00850             REAL maxSpeedBoost2 = maxSpeedPermanent - boostTime * sg_brakeCycle;
00851             if ( maxSpeedBoost2 < maxSpeedBoost )
00852                 maxSpeedBoost = maxSpeedBoost2;
00853         }
00854 
00855         // take over the result
00856         maxSpeed = maxSpeedBoost;
00857     }
00858 
00859     // start speed
00860     if ( sg_speedCycleStart > maxSpeed )
00861         maxSpeed = sg_speedCycleStart;
00862 
00863     // apply multiplier
00864     return sg_speedMultiplier * maxSpeed;
00865 }
00866 
00867 // ----------------------------------------------------------------------------------------------------------
00868 
00869 // *******************************************************************************************
00870 // *
00871 // *    WindingNumber
00872 // *
00873 // *******************************************************************************************
00877 // *******************************************************************************************
00878 
00879 int gCycleMovement::WindingNumber( void ) const
00880 {
00881     return windingNumber_;
00882 }
00883 
00884 // *******************************************************************************************
00885 // *
00886 // *    SetWindingNumberWrapped
00887 // *
00888 // *******************************************************************************************
00892 // *******************************************************************************************
00893 
00894 void gCycleMovement::SetWindingNumberWrapped ( int newWindingNumberWrapped )
00895 {
00896     // calculate the difference in the wrapped winding number
00897     int difference = newWindingNumberWrapped - windingNumberWrapped_;
00898 
00899     // wrap it into the interval [-WN/2,WN/2]
00900     if (2 * difference <= -Grid()->WindingNumber())
00901         difference += Grid()->WindingNumber();
00902     if (2 * difference >= Grid()->WindingNumber())
00903         difference -= Grid()->WindingNumber();
00904 
00905     // commit changes
00906     windingNumberWrapped_ = newWindingNumberWrapped;
00907     windingNumber_ += difference;
00908 }
00909 
00910 // *******************************************************************************************
00911 // *
00912 // *    Direction
00913 // *
00914 // *******************************************************************************************
00918 // *******************************************************************************************
00919 
00920 eCoord gCycleMovement::Direction( void ) const
00921 {
00922     return dirDrive;
00923 }
00924 
00926 
00927 eCoord gCycleMovement::SpawnDirection() const {
00928     return dirSpawn;
00929 }
00930 
00931 // *******************************************************************************************
00932 // *
00933 // *    LastDirection
00934 // *
00935 // *******************************************************************************************
00939 // *******************************************************************************************
00940 
00941 eCoord gCycleMovement::LastDirection( void ) const
00942 {
00943     return lastDirDrive;
00944 }
00945 
00946 // *******************************************************************************************
00947 // *
00948 // *    Speed
00949 // *
00950 // *******************************************************************************************
00954 // *******************************************************************************************
00955 
00956 REAL gCycleMovement::Speed( void ) const
00957 {
00958     REAL ret = verletSpeed_ + .5f * lastTimestep_ * acceleration;
00959     return ret > 0 ? ret : 0;
00960 }
00961 
00962 // *******************************************************************************************
00963 // *
00964 // *    Alive
00965 // *
00966 // *******************************************************************************************
00970 // *******************************************************************************************
00971 
00972 bool gCycleMovement::Alive() const
00973 {
00974     return alive_ > 0;
00975 }
00976 
00977 // *******************************************************************************************
00978 // *
00979 // *    Vulnerable
00980 // *
00981 // *******************************************************************************************
00985 // *******************************************************************************************
00986 
00987 bool gCycleMovement::Vulnerable() const
00988 {
00989     return true;
00990 }
00991 
00992 // *******************************************************************************************
00993 // *
00994 // *    CanMakeTurn
00995 // *
00996 // *******************************************************************************************
01001 // *******************************************************************************************
01002 
01003 bool gCycleMovement::CanMakeTurn( int direction ) const
01004 {
01005     return pendingTurns.empty() && CanMakeTurn( lastTime, direction );
01006 }
01007 
01008 // *******************************************************************************************
01009 // *
01010 // *    CanMakeTurn
01011 // *
01012 // *******************************************************************************************
01018 // *******************************************************************************************
01019 
01020 bool gCycleMovement::CanMakeTurn( REAL time, int direction ) const
01021 {
01022     return time >= GetNextTurn(direction);
01023 }
01024 
01025 // *******************************************************************************************
01026 // *
01027 // *    GetTurnDelay
01028 // *
01029 // *******************************************************************************************
01033 // *******************************************************************************************
01034 
01035 REAL gCycleMovement::GetTurnDelay( void ) const
01036 {
01037     // the basic delay as it was before 0.2.8 looked like this:
01038     REAL baseDelay   = sg_delayCycle*sg_delayCycleBonus/SpeedMultiplier();
01039 
01040     // we're modifying it by a power law to make speed turns easier or harder:
01041     REAL speedFactor = verletSpeed_/(sg_speedCycle*SpeedMultiplier());
01042 
01043     return baseDelay * pow( speedFactor, sg_delayCycleTimeBased-1 );
01044 }
01045 
01047 REAL gCycleMovement::GetTurnDelayDb( void ) const
01048 {
01049     // the basic delay as it was before 0.2.8 looked like this:
01050     REAL baseDelay   = sg_delayCycle*sg_delayCycleBonus/SpeedMultiplier()*sg_delayCycleDoublebindBonus;
01051 
01052     // we're modifying it by a power law to make speed turns easier or harder:
01053     REAL speedFactor = verletSpeed_/(sg_speedCycle*SpeedMultiplier());
01054 
01055     return baseDelay * pow( speedFactor, sg_delayCycleTimeBased-1 );
01056 }
01057 
01058 // *******************************************************************************************
01059 // *
01060 // *    GetNextTurn
01061 // *
01062 // *******************************************************************************************
01066 // *******************************************************************************************
01067 
01068 REAL gCycleMovement::GetNextTurn( int direction ) const
01069 {
01070     float right,left;
01071 #ifdef DEBUG_X
01072     std::cerr << "GetNextTurn: " << direction << std::endl;
01073 #endif
01074     if(direction == 1) {
01075         right = lastTurnTimeRight_ + GetTurnDelayDb();
01076         left = lastTurnTimeLeft_ + GetTurnDelay();
01077     } else {
01078         right = lastTurnTimeLeft_ + GetTurnDelayDb();
01079         left = lastTurnTimeRight_ + GetTurnDelay();
01080     }
01081 #ifdef DEBUG_X
01082     std::cerr << "GetTurnDelay: " << GetTurnDelay() << std::endl;
01083     std::cerr << "GetTurnDelayDb: " << GetTurnDelayDb() << std::endl;
01084     std::cerr << "lastTurnTimeRight_: " << lastTurnTimeRight_ << std::endl;
01085     std::cerr << "lastTurnTimeLeft_: " << lastTurnTimeLeft_ << std::endl;
01086     std::cerr << "right: " << right << std::endl;
01087     std::cerr << "left: " << left << std::endl;
01088 #endif
01089     return left > right ? left : right;
01090 }
01091 
01092 // *******************************************************************************************
01093 // *
01094 // *    AddDestination
01095 // *
01096 // *******************************************************************************************
01099 // *******************************************************************************************
01100 
01101 void gCycleMovement::AddDestination( void )
01102 {
01103     if ( sn_GetNetState() == nCLIENT )
01104     {
01105         gDestination* dest = tNEW(gDestination)(*this);
01106         //      dest->position = dest->position + dest->direction.Turn( 0, 10.0f );
01107         AddDestination( dest );
01108     }
01109 }
01110 
01111 // *******************************************************************************************
01112 // *
01113 // *    AddDestination
01114 // *
01115 // *******************************************************************************************
01119 // *******************************************************************************************
01120 
01121 void gCycleMovement::AddDestination( gDestination * dest )
01122 {
01123     //  con << "got new dest " << dest->position << "," << dest->direction
01124     // << "," << dest->distance << "\n";
01125 
01126     dest->InsertIntoList(&destinationList);
01127 
01128     // if the next destination already has been used, this destination will never be used
01129     if (dest->next && dest->next->hasBeenUsed){
01130         delete dest;
01131         return;
01132     }
01133 
01134     this->NotifyNewDestination( dest );
01135 
01136     // repeat insertion: position may have changed
01137     dest->InsertIntoList(&destinationList);
01138 }
01139 
01140 // *******************************************************************************************
01141 // *
01142 // *    GetCurrentDestination
01143 // *
01144 // *******************************************************************************************
01148 // *******************************************************************************************
01149 
01150 gDestination * gCycleMovement::GetCurrentDestination( void ) const
01151 {
01152     return currentDestination;
01153 }
01154 
01155 // *******************************************************************************************
01156 // *
01157 // *    AdvanceDestination
01158 // *
01159 // *******************************************************************************************
01162 // *******************************************************************************************
01163 
01164 void gCycleMovement::AdvanceDestination( void )
01165 {
01166     // not implemented
01167     tASSERT(0);
01168 }
01169 
01170 // *******************************************************************************************
01171 // *
01172 // *    NotifyNewDestination
01173 // *
01174 // *******************************************************************************************
01178 // *******************************************************************************************
01179 
01180 void gCycleMovement::NotifyNewDestination( gDestination * dest )
01181 {
01182     this->OnNotifyNewDestination( dest );
01183 }
01184 
01185 // *******************************************************************************************
01186 // *
01187 // *    DoIsDestinationUsed
01188 // *
01189 // *******************************************************************************************
01194 // *******************************************************************************************
01195 
01196 bool gCycleMovement::DoIsDestinationUsed( const gDestination * dest ) const
01197 {
01198     return ( destinationList == currentDestination || destinationList == lastDestination );
01199 }
01200 
01201 // *******************************************************************************************
01202 // *
01203 // *    DistanceToDestination
01204 // *
01205 // *******************************************************************************************
01210 // *******************************************************************************************
01211 
01212 REAL gCycleMovement::DistanceToDestination( gDestination & dest ) const
01213 {
01214     // read future direction from destination
01215     eCoord dirTurned = dest.direction;
01216 
01217     REAL divisor = ( dirDrive * dirTurned );
01218     if ( divisor < EPS && divisor > -EPS )
01219     {
01220         REAL F = eCoord::F( dirTurned, dirDrive );
01221         if ( F > 0 )
01222         {
01223             // destination direction and driving direction coincide; we have to
01224             // make up a new turned direction
01225 
01226             // no need to worry if brake status changed
01227             if ( ( braking != 0 ) != dest.braking )
01228             {
01229                 return eCoord::F( dest.position - pos, dirDrive )/dirDrive.NormSquared();
01230             }
01231 
01232             // we'd have to turn in this direction to reach the destination
01233             int side = (dest.position - pos) * dirDrive > 0 ? -1 : 1;
01234 
01235             // pretend to turn in that direction and fetch driving vector
01236             int w = windingNumberWrapped_;
01237             Grid()->Turn(w, side);
01238             dirTurned = Grid()->GetDirection( w );
01239 
01240             // recalculate divisor
01241             divisor = ( dirDrive * dirTurned );
01242             tASSERT( fabs( divisor ) > EPS );
01243         }
01244         else
01245         {
01246             // destination direction and driving direction are opposed. This must be a grave error,
01247             // so we'll make something up
01248             return eCoord::F( dest.position - pos, dirDrive )/dirDrive.NormSquared();
01249         }
01250     }
01251 
01252     // calculate when a turn would need to be made that aligns
01253     // this cycle with the destination
01254     return ( ( dest.position - pos ) * dirTurned ) / divisor;
01255 }
01256 
01257 // *******************************************************************************************
01258 // *
01259 // *    OnNotifyNewDestination
01260 // *
01261 // *******************************************************************************************
01265 // *******************************************************************************************
01266 
01267 void gCycleMovement::OnNotifyNewDestination( gDestination * dest )
01268 {
01269     // go back one destination if the new destination appears to be older than the current one
01270     if ((!currentDestination || currentDestination == dest->next ) &&
01271             sn_GetNetState()!=nSTANDALONE && ( Owner() != ::sn_myNetID || !destinationList ) )
01272     {
01273         currentDestination=dest;
01274         // con << "setting new cd\n";
01275     }
01276 }
01277 
01278 // *******************************************************************************************
01279 // *
01280 // *    OnDropTempWall
01281 // *
01282 // *******************************************************************************************
01288 // *******************************************************************************************
01289 
01290 void gCycleMovement::OnDropTempWall( gPlayerWall * wall, eCoord const & pos, eCoord const & dir )
01291 {
01292 }
01293 
01294 // *******************************************************************************************
01295 // *
01296 // *   GetDestinationBefore
01297 // *
01298 // *******************************************************************************************
01304 // *******************************************************************************************
01305 
01306 gDestination * gCycleMovement::GetDestinationBefore( const SyncData & sync, gDestination * first )
01307 {
01308     // message IDs smaller than 16 don't exist
01309     if ( sync.messageID != 1 )
01310     {
01311         gDestination * ret = first;
01312 
01313         // deterimine last passed destination by the message ID
01314         while ( ret && ret->messageID != sync.messageID )
01315             ret = ret->next;
01316 
01317         // return match
01318         return ret;
01319     }
01320     else
01321     {
01322         // calculate the distance of the last turn of the sync
01323         REAL syncLastTurnDistance = sync.distance - ( sync.pos - sync.lastTurn ).Norm();
01324 
01325         // message ID not available; must use heuristics
01326         gDestination * run = first;         // destination iterator
01327         gDestination * bestMatch = NULL;    // the best message that fit the sync data and that lies before the sync
01328         REAL bestMatchDistance = 1E+20;     // the distance of the best message to the sync
01329         bool braking = false;               // braking causes trouble here. Activate extra checks if brakes are involved
01330         while ( run )
01331         {
01332             // calculate discrepancy of last turn distance of sync to the current message's distance
01333             REAL distanceBefore = syncLastTurnDistance - run->distance;
01334             REAL distanceAfter  = run->distance - sync.distance;
01335 
01336 
01337             // the allowed values for run->distance are inside the interval [ syncLastTurnDistance,sync.distance ]
01338             // distance is a metric that is positive outside of the interval and
01339             // negative inside it, with minimum in the center.
01340             REAL distance = distanceBefore < distanceAfter ? distanceBefore : distanceAfter;
01341 
01342             // activate brake trouble compensation
01343             if ( distance < 0 && ( run->braking || sync.braking ) )
01344             {
01345                 // void previous match
01346                 if ( !braking )
01347                     bestMatchDistance += 1;
01348                 braking = true;
01349             }
01350 
01351             // clamp distance to nonnegative values to give points inside the allowed interval
01352             // equal chances
01353             if ( distance < 0 )
01354                 distance = 0;
01355 
01356             // prefer destinations close to the end of the allowed interval if braking is involved, else destinations close to the beginning
01357             if ( braking )
01358             {
01359                 distance += fabs( distanceAfter + .01 * distanceBefore ) * .0001;
01360             }
01361             else
01362             {
01363                 distance += fabs( distanceBefore ) * .1;
01364             }
01365 
01366             // see if brake status and driving direction match; this is a must
01367             if ( eCoord::F( run->direction, sync.dir ) > .9*sync.dir.NormSquared() && run->braking == ( sync.braking != 0 ) )
01368             {
01369                 if ( !bestMatch || distance < bestMatchDistance )
01370                 {
01371                     bestMatch = run;
01372                     bestMatchDistance = distance;
01373                 }
01374             }
01375             run = run->next;
01376         }
01377 
01378         // con << bestMatchDistance << "\n";
01379 
01380         // return match
01381         return bestMatch;
01382     }
01383 }
01384 
01385 // *******************************************************************************************
01386 // *
01387 // *    EdgeIsDangerous
01388 // *
01389 // *******************************************************************************************
01396 // *******************************************************************************************
01397 
01398 bool gCycleMovement::EdgeIsDangerous( const eWall * wall, REAL time, REAL alpha ) const
01399 {
01400     if (!wall)
01401         return false;
01402 
01403     const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(wall);
01404     if (w)
01405     {
01406         // have we entered a hole? Is the wall breaking down? Have we passed behind the end?
01407         if ( !w->IsDangerous( alpha, time ) )
01408             return false;
01409 
01410         // get time the wall was built
01411         // REAL builtTime = w->Time(alpha);
01412 
01413         //gCycleMovement *otherPlayer=w->CycleMovement();
01414         //if (otherPlayer==this && time < builtTime+2.5*GetTurnDelay() )
01415         //    return false;        // impossible to make such a small circle
01416         // no, not impossible, just moderately unlikely.
01417     }
01418 
01419     return true; // it is a real eWall.
01420 }
01421 
01422 // *******************************************************************************************
01423 // *
01424 // *    Turn
01425 // *
01426 // *******************************************************************************************
01431 // *******************************************************************************************
01432 
01433 bool gCycleMovement::Turn( REAL dir )
01434 {
01435     if (dir>0)
01436         return Turn(1);
01437     else if (dir<0)
01438         return Turn(-1);
01439     else
01440         return false;
01441 }
01442 
01443 // *******************************************************************************************
01444 // *
01445 // *    Turn
01446 // *
01447 // *******************************************************************************************
01452 // *******************************************************************************************
01453 
01454 bool gCycleMovement::Turn( int dir )
01455 {
01456     return DoTurn( dir );
01457 }
01458 
01459 static void sg_DropTempWall( eCoord const & dir, gSensor const & sensor )
01460 {
01461     tASSERT( sensor.ehit );
01462 
01463     if (sn_GetNetState() != nCLIENT )
01464     {
01465         // if the wall is temporary, let its cycle drop it so it gets copied into the grid
01466         // and makes less phasing problems
01467         // don't drop parallel walls
01468         eCoord vec = sensor.ehit->Vec();
01469         if ( fabs( dir * vec ) < vec.Norm() * .5 )
01470             return;
01471 
01472         // get the wall
01473         eWall * ew = sensor.ehit->GetWall();
01474         tASSERT( ew );
01475         gPlayerWall* w = dynamic_cast< gPlayerWall * >( ew );
01476 
01477         // check if wall exists
01478         if ( w )
01479         {
01480             // get the cycle
01481             gCycleMovement* other = w->CycleMovement();
01482 
01483             // let it drop wall
01484             if ( other )
01485                 other->DropTempWall( w, sensor.before_hit, dir );
01486         }
01487     }
01488 }
01489 
01491 struct gMaxSpaceAheadHitInfo
01492 {
01493     eCoord            pos;                           
01494     REAL              offset;                        
01495 
01496     tJUST_CONTROLLED_PTR< eHalfEdge const > edge;    
01497     tJUST_CONTROLLED_PTR< gPlayerWall > playerWall;  
01498     REAL wallAlpha;                                  
01499 
01500     gMaxSpaceAheadHitInfo()
01501             : offset(0), wallAlpha(.5)
01502     {}
01503 };
01504 
01505 // clearer of that data
01506 gMaxSpaceAheadHitInfoClearer::gMaxSpaceAheadHitInfoClearer( gMaxSpaceAheadHitInfo * & info )
01507         : info_( info ){}
01508 
01509 gMaxSpaceAheadHitInfoClearer::~gMaxSpaceAheadHitInfoClearer()
01510 {
01511     gMaxSpaceAheadHitInfo * info = info_;
01512     if ( info )
01513     {
01514         // we can't have the edges lingering around with possibly incomplete data
01515         info->edge = NULL;
01516         info->playerWall = NULL;
01517     }
01518 }
01519 
01520 static REAL sg_Gap( gSensor const & front, gSensor const & side, eCoord const & dir, REAL norm, REAL def, REAL & tolerance )
01521 {
01522     if ( side.ehit && side.ehit->Other() )
01523     {
01524         // determine the adistance of the two endpoints of the side edge
01525         // to the wall in front of us
01526         REAL gap1 = ( front.ehit->Vec()*( *side.ehit->Point() - *front.ehit->Point() ) )/norm;
01527         REAL gap2 = ( front.ehit->Vec()*( *side.ehit->Other()->Point() - *front.ehit->Point() ) )/norm;
01528 
01529         // correct for orientation using the current driving direction
01530         REAL sign = (dir * front.ehit->Vec())/norm;
01531         if ( sign != 0 )
01532         {
01533             sign = 1/sign;
01534             gap1 *= sign;
01535             gap2 *= sign;
01536         }
01537 
01538         // if both values are positive, update the cache with the smaller one
01539         tolerance = ( fabs(gap1) + fabs(gap2) ) * EPS * 10;
01540         REAL minGap = gap1 < gap2 ? gap1 : gap2;
01541 
01542         return minGap;
01543     }
01544     else
01545     {
01546         // return something close to the front wall
01547         tolerance = EPS * 10 * front.hit;
01548         return def;
01549     }
01550 }
01551 
01552 static REAL sg_rubberCycleMinDistanceGap = .0f;        // if != 0, CYCLE_RUBBER_MINDISTANCE effectively is never bigger than this value times the size of any detected gaps the cylce can squeeze through.
01553 static REAL sg_rubberCycleMinDistanceGapSide = .5f;   // Gaps may be detected only if the cycle is able to drive into them in this time
01554 
01555 static nSettingItemWatched<REAL> c_rcmdg("CYCLE_RUBBER_MINDISTANCE_GAP",
01556         sg_rubberCycleMinDistanceGap, nConfItemVersionWatcher::Group_Bumpy, 14 );
01557 static nSettingItem<REAL> c_rcmdgs("CYCLE_RUBBER_MINDISTANCE_GAP_SIDE",
01558                                    sg_rubberCycleMinDistanceGapSide);
01559 
01560 // *******************************************************************************************
01561 // *
01562 // *    MaxSpaceAhead
01563 // *
01564 // *******************************************************************************************
01573 // *******************************************************************************************
01574 
01575 // *******************************************************************************************
01576 // *
01577 // *    GetMaxSpaceAhead
01578 // *
01579 // *******************************************************************************************
01585 // *******************************************************************************************
01586 
01587 REAL gCycleMovement::GetMaxSpaceAhead( REAL maxReport ) const
01588 {
01589     // refresh hit info if required
01590     if ( refreshSpaceAhead_ )
01591     {
01592         refreshSpaceAhead_ = false;
01593 
01594         // make sure the raycast is long enoigh
01595         REAL lookAhead = maxSpaceMaxCast_;
01596         if ( maxReport > lookAhead )
01597         {
01598             lookAhead = maxReport;
01599         }
01600 
01601         sg_ArchiveReal( lookAhead, 9 );
01602 
01603         // store data here for later
01604         gMaxSpaceAheadHitInfo info;
01605 
01606         // calculate the relevant minimal distance
01607         REAL mindistance = sg_rubberCycleMinDistance;
01608         {
01609             // get rubber values
01610             REAL rubber_granted, rubberEffectiveness;
01611             sg_RubberValues( player, verletSpeed_, rubber_granted, rubberEffectiveness );
01612 
01613             // add the reservoir dependant term
01614             if ( rubber_granted > 0 )
01615             {
01616                 // rubber usage speed
01617                 REAL rubberUsageSpeed = verletSpeed_ * ( 1 - rubberSpeedFactor ) / rubberEffectiveness;
01618                 // rubber used till end of frame
01619                 REAL rubberUsed = rubberUsageSpeed * lastTimestep_;
01620 
01621                 // fill ratio of rubber at the end of the next frame
01622                 REAL filling = ( GetRubber() + rubberUsed )/rubber_granted;
01623                 if ( filling > 1 )
01624                     filling = 1;
01625                 mindistance += sg_rubberCycleMinDistanceReservoir * (1-filling);
01626             }
01627 
01628             // add the bad preparation dependant term
01629             if ( sg_rubberCycleMinDistancePreparation > 0 )
01630             {
01631                 REAL badPreparation = sg_rubberCycleMinDistancePreparation/( sg_rubberCycleMinDistancePreparation + ( this->LastTime() - this->GetLastTurnTime() ) );
01632                 mindistance += sg_rubberCycleMinDistanceUnprepared * badPreparation;
01633             }
01634         }
01635         sg_ArchiveReal( mindistance, 9 );
01636 
01637         // since we are going to subtract the rubber min distance from the found hit, we'll still have to llok a bit further:
01638         lookAhead += mindistance * sg_rubberCycleMinDistanceLegacy * 2;
01639 
01640         // be a little nice and don't drive into the eWall if turning is allowed
01641         gSensor fr( const_cast< gCycleMovement* >(this), this->Position(), this->Direction() );
01642         {
01643             REAL speed = this->Speed();
01644             if ( speed > 0 )
01645                 fr.SetInverseSpeed( 1 / speed );
01646         }
01647         fr.detect( lookAhead );
01648 
01649         info.edge = fr.ehit;
01650         info.pos  = fr.before_hit;
01651 
01652         if ( fr.ehit )
01653         {
01654             {
01655                 // get the wall of the hit
01656                 eWall * w = info.edge->GetWall();
01657                 if ( !w && info.edge->Other() )
01658                 {
01659                     info.edge = info.edge->Other();
01660                     w = info.edge->GetWall();
01661                 }
01662 
01663                 gPlayerWall * wall = dynamic_cast< gPlayerWall * >( w );
01664                 if ( wall && wall->Cycle() )
01665                 {
01666                     // get the position of the hit and store everything
01667                     info.wallAlpha = info.edge->Ratio( info.pos );
01668                     info.playerWall = wall;
01669                 }
01670             }
01671 
01672 #ifdef DEBUG
01673             {
01674                 gSensor fr2( const_cast< gCycleMovement* >( this ), this->Position(), this->Direction() );
01675                 fr2.detect( lookAhead );
01676             }
01677 #endif
01678 
01679             REAL stopDistance = 0.1;
01680             if ( fr.ehit )
01681             {
01682                 REAL norm = fr.ehit->Vec().Norm();
01683                 stopDistance = mindistance + sg_rubberCycleMinDistanceRatio * norm;
01684 
01685                 ::sg_DropTempWall( this->Direction(), fr );
01686 
01687                 // enforce "open" play: every successive grind to a wall can get closer and closer.
01688 
01689                 REAL rubberCycleMinDistanceGapDistance = sg_rubberCycleMinDistanceGapSide * Speed();
01690 
01691 
01692                 if ( sg_rubberCycleMinDistanceGap > 0 )
01693                 {
01694                     // determine the width of the gap previous grinders left
01695                     for ( int dir = -1; dir < 2; dir += 2 )
01696                     {
01697                         // see if cached value is still good
01698                         REAL & gapCache = gap_[(dir+1)/2];
01699                         bool & keepLooking = keepLookingForGap_[(dir+1)/2];
01700 
01701                         if ( gapCache > fr.hit && keepLooking )
01702                         {
01703                             // determine next direction when turning into dir
01704                             int wn = windingNumberWrapped_;
01705                             Grid()->Turn(wn, dir);
01706                             eCoord dirCast = Grid()->GetDirection(wn);
01707 
01708                             bool gapFound = false;
01709                             for ( int back = -1; back <= 2; ++back )
01710                             {
01711                                 // determine next direction when turning into dir
01712                                 int wn2 = wn;
01713                                 Grid()->Turn(wn2, back);
01714                                 eCoord dirCast2 = Grid()->GetDirection(wn2);
01715 
01716                                 // send out a side sensor
01717                                 gSensor side( const_cast< gCycleMovement * >( this ),
01718                                               this->Position(),
01719                                               ( dirCast + dirCast2 ) * .5 );
01720 
01721                                 side.detect( rubberCycleMinDistanceGapDistance );
01722 
01723                                 // only allow non-hit default search for the ray that goes straight to the side
01724                                 if ( back != 0 && !side.ehit )
01725                                     continue;
01726 
01727                                 REAL tolerance;
01728                                 REAL minGap = sg_Gap( fr, side, dirDrive, norm, fr.hit * .5, tolerance );
01729 
01730                                 while ( minGap > tolerance )
01731                                 {
01732                                     // last test: see if there really is a gap after that wall ends
01733                                     gSensor side2( const_cast< gCycleMovement * >( this ),
01734                                                    this->Position() + this->Direction() * ( fr.hit - minGap * .9 ),
01735                                                    dirCast );
01736                                     side2.detect( rubberCycleMinDistanceGapDistance );
01737 
01738                                     // if this sensor did not hit or hit farther than the first sensor, the gap is real
01739                                     if ( fabs(side2.hit - side.hit) < tolerance )
01740                                     {
01741                                         // true gap not found yet
01742                                         REAL dumpTolerance;
01743                                         REAL lastMinGap = minGap;
01744                                         minGap = sg_Gap( fr, side2, dirDrive, norm, minGap * .5, dumpTolerance );
01745                                         // no improvement? give up.
01746                                         if ( minGap >= lastMinGap * .9 )
01747                                             break;
01748                                     }
01749                                     else
01750                                     {
01751                                         gapFound = true;
01752 
01753                                         // true gap found, is it smaller than the last one?
01754                                         if ( minGap < gapCache )
01755                                         {
01756                                             gapCache = minGap;
01757 
01758                                             // bail out of outer loop
01759                                             back = 100;
01760                                         }
01761 
01762                                         // bail out of inner loop
01763                                         break;
01764                                     }
01765                                 }
01766                             }
01767 
01768                             // no gap to see anywhere
01769                             if ( ! gapFound )
01770                             {
01771                                 // don't waste time looking from now on
01772                                 keepLooking = false;
01773 
01774                                 // if there was no gap detected so far, there is no gap.
01775                                 if ( gapCache > 5E+19 )
01776                                     gapCache = 0;
01777                             }
01778                         }
01779                     }
01780 
01781                     // fetch cache, ignoring zeroes
01782                     REAL gap = ( ( gap_[0] > 0 ? gap_[0] : 1E+30 ) < ( gap_[1] > 0 ? gap_[1] : 1E+30 ) ) ? gap_[0] : gap_[1];
01783                     if ( gap > 0 )
01784                     {
01785                         REAL minDistanceGap = gap * sg_rubberCycleMinDistanceGap;
01786                         if ( stopDistance > minDistanceGap )
01787                             stopDistance = minDistanceGap;
01788                     }
01789                 }
01790             }
01791             sg_ArchiveReal( stopDistance, 9 );
01792 
01793 
01794             // revert to almost old rubber logic if old clients are connected. This may cause rips, but we don't care.
01795             if ( sg_rubberCycleLegacy && !sg_nonRippable.Supported() && stopDistance > .001 )
01796                 stopDistance = .001;
01797 
01798             // if there is a rippable peer connected and the wall is the rim wall, add extra distance
01799             //if ( fr.type == gSENSOR_RIM && !sg_nonRippable.Supported() )
01800             //    stopDistance *= sg_rubberCycleMinDistanceLegacy;
01801 
01802             REAL space = fr.hit;
01803             sg_ArchiveReal( space, 9 );
01804 
01805             // see if we just did a turn
01806             REAL distSinceLastTurn = this->GetDistanceSinceLastTurn();
01807 
01808             // we want to get closer to the wall by at least some percentage
01809             REAL maxStop = ( distSinceLastTurn + space ) * ( 1 - sg_rubberCycleMinAdjust );
01810             if ( maxStop < stopDistance )
01811             {
01812                 stopDistance = maxStop;
01813             }
01814 
01815             sg_ArchiveReal( stopDistance, 9 );
01816 
01817             // add safety
01818             REAL safety = this->Position().Norm() * 2 * EPS;
01819 
01820             info.offset = stopDistance + safety;
01821 
01822             sg_ArchiveReal( space, 9 );
01823 
01824             // create new hit info
01825             if ( !maxSpaceHit_ )
01826                 maxSpaceHit_ = tNEW( gMaxSpaceAheadHitInfo );
01827 
01828             // store information
01829             *maxSpaceHit_ = info;
01830         }
01831         else
01832         {
01833             // delete information
01834             delete maxSpaceHit_;
01835             maxSpaceHit_ = NULL;
01836         }
01837     }
01838 
01839     // information up to date? Good, just take the distance to the collision point.
01840     REAL ret = 1E+30;
01841     if ( maxSpaceHit_ )
01842     {
01843         ret = eCoord::F( dirDrive, maxSpaceHit_->pos - pos ) - maxSpaceHit_->offset;
01844     }
01845 
01846     // clamp it and return.
01847     if ( ret > maxReport )
01848         ret = maxReport;
01849     return ret;
01850 }
01851 
01852 // *******************************************************************************************
01853 // *
01854 // *    MaxSpaceAhead
01855 // *
01856 // *******************************************************************************************
01858 
01865 // *******************************************************************************************
01866 
01867 /*
01868 float MaxSpaceAhead( const gCycleMovement* cycle, float ts, float lookAhead, float maxReport )
01869 {
01870     // lookahead should be at least the next expected timestep ( times two for safety )
01871     REAL step=cycle->Speed() * ts * 2;
01872     if ( lookAhead < step )
01873         lookAhead = step;
01874 
01875     // lookahead should be at least maxReport
01876     if ( lookAhead < maxReport )
01877         lookAhead = maxReport;
01878 
01879     REAL space = MaxSpaceAhead( cycle, lookAhead );
01880 
01881     if ( space < maxReport )
01882         return space;
01883     else
01884         return maxReport;
01885 }
01886 */
01887 
01888 // feature indicating that a client sends the time of turn commands
01889 static nVersionFeature sg_CommandTime( 4 );
01890 
01891 // server side feature of lag sliding correction code
01892 static nVersionFeature sg_AntiLag( 5 );
01893 
01894 // flag indicating whether to use the logic to prevent lag sliding only when every connected client can benefit from it
01895 static bool sg_fairAntiLagSliding=true;
01896 static nSettingItem<bool> c_fals("CYCLE_FAIR_ANTILAG",sg_fairAntiLagSliding);
01897 
01898 // check if we should apply anti-lag-sliding code
01899 static bool sg_UseAntiLagSliding( const eNetGameObject* obj )
01900 {
01901     tASSERT( obj );
01902 
01903     // cant do anyting for old clients that don't send the time of commands
01904     if ( !sg_CommandTime.Supported( obj->Owner() ) )
01905         return false;
01906 
01907     // likewise if the server does not support anti lag code and this is the client
01908     if ( sn_GetNetState() == nCLIENT && !sg_AntiLag.Supported( obj->Owner() ) )
01909         return false;
01910 
01911     // check whether the command time is sent by everyone or at least the object owner ( depending on fairness )
01912     if ( sg_fairAntiLagSliding )
01913     {
01914         return sg_CommandTime.Supported();
01915     }
01916     else
01917     {
01918         // we already checked whether the command time was sent
01919         return true;
01920     }
01921 }
01922 
01923 // while an object of this class exists, turn delay is ignored
01924 class gTurnDelayOverride
01925 {
01926 public:
01927     explicit gTurnDelayOverride( bool override )
01928     {
01929         delay_ = sg_delayCycle;
01930         if ( override )
01931             sg_delayCycle = 0.0f;
01932     }
01933 
01934     explicit gTurnDelayOverride( REAL factor )
01935     {
01936         delay_ = sg_delayCycle;
01937         sg_delayCycle *= factor;
01938     }
01939 
01940     ~gTurnDelayOverride()
01941     {
01942         sg_delayCycle = delay_;
01943     }
01944 private:
01945     REAL delay_;
01946 };
01947 
01948 static nVersionFeature sg_noRedundantBrakeCommands( 13 );
01949 
01950 // *******************************************************************************************
01951 // *
01952 // *    Timestep
01953 // *
01954 // *******************************************************************************************
01959 // *******************************************************************************************
01960 
01961 bool gCycleMovement::Timestep( REAL currentTime )
01962 {
01963     // request regeneration of maximum space
01964     refreshSpaceAhead_ = true;
01965 
01966     // clear out dangerous info when we're done
01967     gMaxSpaceAheadHitInfoClearer hitInfoClearer( maxSpaceHit_ );
01968 
01969     // clamp stuff to finite values
01970     clamp( rubber, 0, sg_rubberCycle );
01971 
01972     // keep this cycle alive
01973     tJUST_CONTROLLED_PTR< gCycleMovement > keep( this->GetRefcount()>0 ? this : 0 );
01974 
01975     // don't make a fuss about negative timesteps
01976     if ( currentTime < lastTime )
01977         return TimestepCore( currentTime );
01978 
01979     // remove old destinations
01980     //REAL lag = 1;
01981     //if ( player )
01982     //    lag = player->ping;
01983     // REAL lagDistance = lag * Speed() * 10;
01984 
01985     while(destinationList && destinationList->hasBeenUsed && !IsDestinationUsed( destinationList ) )
01986         delete destinationList;
01987 
01988     // calculate timestep
01989     REAL dt = currentTime - lastTime;
01990 
01991     sg_ArchiveReal( dt, 9 );
01992 
01993     // if (currentTime > lastTime)
01994     {
01995         int timeout=10;
01996         bool forceTurn = false; // force turning at the next iteration
01997         bool overrideTurnDelay=false; // override the turn delay system, turn immediately
01998 
01999         // only simulate forward in time
02000         while (pendingTurns.empty() && currentDestination && timeout > 0 )
02001         {
02002             timeout --;
02003 
02004             // the distance to the next destination
02005             REAL dist_to_dest = DistanceToDestination( *currentDestination );
02006             sg_ArchiveReal( dist_to_dest, 9 );
02007 
02008             REAL ts=currentTime - lastTime;
02009             sg_ArchiveReal( ts, 9 );
02010 
02011             sg_ArchiveReal( verletSpeed_, 9 );
02012             sg_ArchiveReal( acceleration, 9 );
02013 
02014             // our speed
02015             REAL avgspeed=verletSpeed_;
02016             CalculateAcceleration();
02017             if (acceleration > 0)
02018                 avgspeed += acceleration * SpeedMultiplier() * ts * .5;
02019 
02020             // will rubber slow the cycle down to a crawl, so that the command time will be
02021             // a better indication when to turn than the position?
02022             bool rubberActive = false;
02023 
02024             // check if the path to the destination is clear for the next timesteps
02025             sg_ArchiveReal( rubberSpeedFactor, 9 );
02026             REAL distToWall=1E+30;
02027             if ( rubberSpeedFactor < .999 )
02028             {
02029                 // take rubber activity into account
02030                 rubberActive = true;
02031                 avgspeed *= rubberSpeedFactor;
02032                 if ( avgspeed < EPS )
02033                     avgspeed = EPS;
02034 
02035                 sg_ArchiveReal( avgspeed, 9 );
02036 
02037                 // don't drive into a wall, turn before getting too close
02038                 REAL lookahead = ts * avgspeed * 2;
02039 
02040                 REAL dist_to_wall = GetMaxSpaceAhead( lookahead );
02041 
02042                 if ( dist_to_dest > dist_to_wall )
02043                     dist_to_dest = dist_to_wall;
02044             }
02045 
02046             static bool breakp = false;
02047 
02048             // the time left until the turn happened on the client
02049             // REAL timeLeft = currentDestination->GetGameTime() - lastTime;
02050 
02051             // the earliest and latest allowed time to turn
02052             REAL turnTime = currentDestination->GetGameTime();
02053             REAL earliestTurnTime = turnTime - sg_timeTolerance - 100;
02054             REAL latestTurnTime   = turnTime + sg_timeTolerance;
02055 
02056             // if rubber is active and anti lag sliding code should be enabled,
02057             // then allow no too early or late turns
02058             if ( sg_UseAntiLagSliding( this ) )
02059             {
02060                 if ( rubberActive )
02061                 {
02062                     // smoothly make the allowed time interval smaller with increased rubber use
02063                     earliestTurnTime = turnTime - sg_timeTolerance * rubberSpeedFactor;
02064                     latestTurnTime = turnTime + sg_timeTolerance * rubberSpeedFactor;
02065 
02066                     // clamp latest turn time so we don't drive into the wall
02067                     if ( verletSpeed_ > 0 )
02068                     {
02069                         REAL maxRubber, effectiveness;
02070                         sg_RubberValues( Player(), verletSpeed_, maxRubber, effectiveness );
02071 
02072                         // see when we'll die (be a little careful, the .9 is a fudge factor)
02073                         REAL rubberLeft = (maxRubber - rubber)*.9;
02074                         REAL stepLeft   = rubberLeft + distToWall;
02075                         REAL timeLeft   = stepLeft/verletSpeed_;
02076                         REAL deathTime  = lastTime + timeLeft;
02077 
02078                         // can't do a thing if the player wants to die
02079                         if ( deathTime < turnTime )
02080                             deathTime = turnTime;
02081 
02082                         // clamp latest possible time
02083                         if ( latestTurnTime > deathTime )
02084                             latestTurnTime = deathTime;
02085                     }
02086                 }
02087                 else
02088                 {
02089                     // just clamp the latest turn time
02090                     earliestTurnTime = turnTime - sg_timeTolerance;
02091                 }
02092             }
02093 
02094             sg_ArchiveReal( dist_to_dest, 9 );
02095 
02096             REAL simulateAhead = MaxSimulateAhead();
02097 
02098             if ( dist_to_dest > ( ts + simulateAhead ) * avgspeed && currentTime < latestTurnTime )
02099                 break; // no need to worry; we won't reach the next destination
02100 
02101             if ( currentTime < earliestTurnTime && sg_CommandTime.Supported( Owner() ) )
02102                 break; // the turn is too far in the future
02103 
02104             // if ( currentTime < turnTime + EPS )
02105             //    simulateAhead = 0;
02106 
02107             // determine whether to turn left or right (coping with weird axis settings)
02108             int turnTo=0;
02109             // and whether between the last and next destination, there was one missing that
02110             // we didn't receive.
02111             bool missed=false;
02112 
02113             {
02114                 REAL t = currentDestination->direction * dirDrive;
02115                 bool turn = true;
02116 
02117                 missed  = (fabs(t)<.01);
02118                 if (int(braking) != int(currentDestination->braking))
02119                 {
02120                     turn = false;
02121                     missed=!missed;
02122                 }
02123 
02124                 // detect false misses: if the last destination's message ID is just
02125                 // one below the current destination's message ID, it's a fake
02126                 if ( missed && lastDestination && lastDestination->messageID == currentDestination->messageID-1 )
02127                 {
02128                     missed = false;
02129                     if ( ( dirDrive - currentDestination->direction ).NormSquared() < EPS )
02130                     {
02131                         turn = false;
02132                         turnTo = 0;
02133                     }
02134                 }
02135 
02136                 // if the destination is dead ahead, it can't be a fake, either
02137                 if ( missed && sn_GetNetState() == nSERVER && !sg_noRedundantBrakeCommands.Supported( Owner() ) )
02138                 {
02139                     // calculate difference in expected position at the destination's time and the transmitted position
02140                     REAL timeToDest = currentDestination->GetGameTime() - lastTime;
02141                     eCoord posDelta = pos + dirDrive * ( timeToDest * ( verletSpeed_ + .5f * acceleration * timeToDest ) ) - currentDestination->position;
02142                     REAL deltaParallel = eCoord::F( posDelta, dirDrive );
02143                     REAL deltaOrthogonal = posDelta * dirDrive;
02144 
02145                     // if it's small, that's not a miss
02146                     REAL tolerance = verletSpeed_ * GetTurnDelay();
02147                     if ( fabs(deltaParallel) < tolerance && fabs(deltaOrthogonal) < tolerance * .5 )
02148                     {
02149                         missed = false;
02150                         if ( ( dirDrive - currentDestination->direction ).NormSquared() < EPS )
02151                         {
02152                             turn = false;
02153                         }
02154                     }
02155                 }
02156 
02157                 // see if we missed a turn by, say, just counting?
02158                 if ( turns < currentDestination->turns - 1 )
02159                     missed = true;
02160 
02161                 if ( turn )
02162                 {
02163                     // the direction we need to drive in
02164                     // see which direction we drive into after a left or right turn
02165                     int wn = windingNumberWrapped_;
02166                     Grid()->Turn(wn, 1);
02167                     eCoord dirPlus = Grid()->GetDirection(wn);
02168                     wn = windingNumberWrapped_;
02169                     Grid()->Turn(wn, -1);
02170                     eCoord dirMinus = Grid()->GetDirection(wn);
02171 
02172                     if ( missed )
02173                     {
02174                         eCoord dirTurn = (currentDestination->position - pos);
02175 
02176                         // see witch of the alternatives comes closer to the desired direction
02177                         turnTo = ( ( fabs( dirMinus * dirTurn ) - .1 * eCoord::F( dirMinus, dirTurn ) )/dirTurn.NormSquared() < ( fabs( dirPlus * dirTurn ) - .1 * eCoord::F( dirPlus, dirTurn ) )/dirTurn.NormSquared() ) ? -1 : +1;
02178                     }
02179                     else
02180                     {
02181                         // just see which axis gets closer
02182                         eCoord dirTurn = currentDestination->direction;
02183 
02184                         turnTo = ( ( dirMinus - dirTurn ).NormSquared() < ( dirPlus - dirTurn ).NormSquared() ) ? -1 : +1;
02185 
02186                     }
02187                 }
02188             }
02189 
02190             // can we turn already?
02191             bool canTurn = ( turnTo == 0 || CanMakeTurn(turnTo) || overrideTurnDelay );
02192 
02193             if ( lastTime >= earliestTurnTime && canTurn && ( forceTurn || dist_to_dest < 0.01 || timeout <= 0 || lastTime >= latestTurnTime ) ){
02194                 forceTurn = false;
02195 
02196 #ifdef DEBUG
02197                 if ( turnTo != 0 )
02198                 {
02199                     static REAL checkFactor = .9f;
02200                     gTurnDelayOverride check( checkFactor );
02201                     if ( !CanMakeTurn( turnTo ) )
02202                     {
02203                         con << "Early turn!\n";
02204                         st_Breakpoint();
02205                     }
02206                 }
02207 #endif
02208 
02209 
02210                 // con << timeLeft << ", " << earlyTurnTolerance << ", " << rubberActive << ", " << dist_to_dest << "\n";
02211 
02212                 // the destination is very close or we gave up. Now is the time to turn towards
02213                 // it or turn to the direction it gives us
02214 
02215                 // bring us to the exact location to avoid lag sliding due to
02216                 // disagreement between client and server.
02217                 // but only if we are reasonably close ( don't want cheating )
02218                 // and use a correct move that kills us if we cross a wall.
02219 
02220                 /*
02221                          if ( dist_to_dest < .1 && dist_to_dest > -.1 )
02222                          {
02223                              Move( pos + dirDrive * dist_to_dest, currentTime, currentTime );
02224                          }
02225                 #ifdef DEBUG
02226                          else
02227                          {
02228                              if ( breakp )
02229                              {
02230                                  int x;
02231                                  x = 0;
02232                              }
02233                              con << "gCycle::Timestep: Could not completely reach destination.\n";
02234                              breakp = true;
02235                          }
02236                 #endif                  
02237                 */
02238 
02239                 // con << "Turn: " << lastTime << ", " << dist_to_dest << ", " << currentDestination->position << ", " << pos << "\n";
02240 
02241                 //eDebugLine::SetTimeout(2);
02242                 //eDebugLine::SetColor( 0,1,1 );
02243                 //eDebugLine::Draw( currentDestination->position, 0, currentDestination->position, 8 );
02244                 //eDebugLine::SetColor( 0,0,1 );
02245                 //eDebugLine::Draw( pos, 0, pos, 8 );
02246 
02247                 bool used = false; // flag indicating whether the current destination has been used
02248 
02249                 if (!missed){ // then we did not miss a destination
02250                     used = true;
02251 
02252                     if (turnTo != 0)
02253                     {
02254 #ifdef DEBUG
02255 #ifdef DEDICATED
02256                         eCoord slide = this->pos - currentDestination->position;
02257                         if ( Player() && slide.NormSquared() > .01 )
02258                             con << "Lag slide for " << Player()->GetUserName() << ": "  << slide << ", rubberSpeedFactor " << rubberSpeedFactor << "\n";
02259 #endif
02260 #endif
02261                         gTurnDelayOverride override( overrideTurnDelay );
02262                         Turn(turnTo);
02263                     }
02264                     else{
02265                         AccelerationDiscontinuity();
02266                         braking = currentDestination->braking;
02267                         if (sn_GetNetState()!=nCLIENT)
02268                             RequestSync();
02269                     }
02270 
02271                     /*
02272                       con << "turning alon " << currentDestination->position << "," 
02273                       << currentDestination->direction << "," 
02274                       << currentDestination->distance << "\n";
02275                     */
02276                 }
02277                 else
02278                 {
02279 
02280                     // Uh oh. Turn commands are missing. We should wait as long as possible, it must
02281                     // already be on its way.
02282                     if ( lastTime > currentTime - Lag()*sg_packetMissTolerance )
02283                         return !Alive();
02284 
02285                     if ( turns >= currentDestination->turns - 1 )
02286                     {
02287                         // OK, we missed exactly one turn. Don't panic. Just turn
02288                         // towards the destination:
02289                         REAL side = (currentDestination->position - pos) * dirDrive;
02290                         if ( fabs(side)>verletSpeed_ * GetTurnDelay() * .2 )
02291                         {
02292                             gTurnDelayOverride override( overrideTurnDelay );
02293                             Turn(turnTo);
02294                         }
02295                         else
02296                             used = true;
02297                     }
02298                     /*
02299                       con << "turning to   " << currentDestination->position << "," 
02300                       << currentDestination->direction << "," 
02301                       << currentDestination->distance << "\n";
02302                     */
02303 
02304                 }
02305 
02306                 overrideTurnDelay = false;
02307 
02308                 if ( used )
02309                 {
02310                     // only the server marks destinations as used; the client has to reuse them sometimes.
02311                     currentDestination->hasBeenUsed = (sn_GetNetState()!=nCLIENT);
02312                     lastDestination = currentDestination;
02313 
02314                     // advance
02315                     currentDestination = currentDestination->next;
02316                 }
02317 
02318 
02319                 while (currentDestination && currentDestination->hasBeenUsed)
02320                 {
02321                     breakp = false;
02322                     currentDestination = currentDestination->next;
02323                 }
02324             }
02325             else
02326             {
02327                 // ok, the dest is right ahead, but not close enough.
02328                 // how long does it take at least
02329                 // (therefore the strange average speed) to get there?
02330                 sg_ArchiveReal( avgspeed, 9 );
02331                 REAL tsTodo = dist_to_dest/avgspeed;
02332                 /*
02333                          if ( tsTodo > timeLeft )
02334                          {
02335                              tsTodo = timeLeft;
02336                          }
02337                 */
02338                 sg_ArchiveReal( tsTodo, 9 );
02339 
02340                 // we can't turn now, simulate until we can
02341                 if ( !canTurn )
02342                 {
02343                     REAL nextTurn = GetNextTurn(turnTo);
02344                     REAL turnStep = nextTurn - lastTime;
02345 
02346                     // clamp timestep values
02347                     if ( turnTime < nextTurn )
02348                         turnTime = nextTurn;
02349                     if ( earliestTurnTime < nextTurn )
02350                         earliestTurnTime = nextTurn;
02351                     if ( latestTurnTime < nextTurn )
02352                         latestTurnTime = nextTurn;
02353 
02354                     if ( currentTime - lastTime > turnStep )
02355                     {
02356                         tsTodo = turnStep;
02357 
02358                         // if we can simulate to the turn in the next step, do so, overriding
02359                         // the turn delay then.
02360                         if ( tsTodo < ts + simulateAhead && tsTodo > 0 )
02361                         {
02362                             overrideTurnDelay = true;
02363                         }
02364                     }
02365                     else
02366                     {
02367                         // not enough time to simulate to turn possibility; skip out of loop
02368                         break;
02369                     }
02370                 }
02371                 else
02372                 {
02373                     sg_ArchiveReal( tsTodo, 9 );
02374                     // don't turn too late
02375                     REAL maxts = latestTurnTime - lastTime;
02376                     sg_ArchiveReal( maxts, 9 );
02377                     if ( tsTodo > maxts )
02378                     {
02379                         // force turn on next iteration, we'll be there
02380                         forceTurn = true;
02381                         tsTodo = maxts;
02382                     }
02383 
02384                     // don't turn too early
02385                     REAL mints = earliestTurnTime - lastTime;
02386                     // sg_ArchiveReal( mints, 9 );
02387                     if ( tsTodo < mints )
02388                     {
02389                         tsTodo = mints;
02390                     }
02391                 }
02392 
02393                 if ( tsTodo < 0 )
02394                 {
02395                     // should never happen
02396                     st_Breakpoint();
02397                     return !Alive();
02398                 }
02399                 if ( tsTodo > ts + simulateAhead )
02400                 {
02401                     tsTodo = ts + simulateAhead ;
02402                     forceTurn = false;
02403 
02404                     // quit from here if there is nothing to do
02405                     if ( tsTodo <= EPS )
02406                         break;
02407                 }
02408         #ifdef DEBUG
02409                 if ( tsTodo < 0 )
02410                     con << "Negative timestep!\n";
02411         #endif
02412                 sg_ArchiveReal( tsTodo, 9 );
02413 
02414                 // core simulation
02415                 if ( tsTodo > EPS )
02416                 {
02417                     REAL lastTimeBack = lastTime;
02418                     bool ret = TimestepCore( lastTime + tsTodo, false );
02419                     if ( lastTime <= lastTimeBack )
02420                         return ret;
02421                 }
02422                 else
02423                 {
02424                     // already nothing to do, turn on next iteration
02425                     forceTurn = true;
02426                 }
02427             }
02428         }
02429     }
02430 
02431     // simulate exactly to the time of the next turn if it is in reach
02432     if ( !pendingTurns.empty())
02433     {
02434         REAL nextTurn = GetNextTurn(pendingTurns.front());
02435         if(currentTime>nextTurn) {
02436             if ( nextTurn > lastTime )
02437                 TimestepCore( nextTurn );
02438 
02439             //con << "Executing delayed turn at time " << lastTime << "\n";
02440             Turn(pendingTurns.front());
02441             pendingTurns.pop_front();
02442         }
02443     }
02444 
02445     // do the rest of the timestep
02446     bool ret = false;
02447     if ( currentTime > lastTime )
02448         ret = TimestepCore( currentTime );
02449 
02450     return ret;
02451 }
02452 
02453 // *******************************************************************************************
02454 // *
02455 // *    AddRef
02456 // *
02457 // *******************************************************************************************
02460 // *******************************************************************************************
02461 
02462 void gCycleMovement::AddRef( void )
02463 {
02464     eNetGameObject::AddRef();
02465     if ( GetRefcount() > sg_cycleMaxRefCount && Alive() )
02466     {
02467         // during the kill, further refcounts will be added, so we need to pump
02468         // up the limit
02469         int backup = sg_cycleMaxRefCount;
02470         sg_cycleMaxRefCount += 100;
02471         Kill();
02472         sg_cycleMaxRefCount = backup;
02473     }
02474 }
02475 
02476 // *******************************************************************************************
02477 // *
02478 // *    gCycleMovement
02479 // *
02480 // *******************************************************************************************
02488 // *******************************************************************************************
02489 
02490 gCycleMovement::gCycleMovement( eGrid * grid, const eCoord & pos, const eCoord & dir, ePlayerNetID * player, bool autodelete )
02491         :eNetGameObject(grid, pos,dir,player,autodelete),
02492         destinationList(NULL),currentDestination(NULL),lastDestination(NULL),
02493         dirDrive(dir),
02494         acceleration(0),
02495         totalZoneAcceleration(0),
02496         lastTimestep_(0),
02497         verletSpeed_(sg_speedCycleStart * SpeedMultiplier()),
02498         pendingTurns()
02499 {
02500     windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dir);
02501 
02502     MyInitAfterCreation();
02503 }
02504 
02505 // *******************************************************************************************
02506 // *
02507 // *    gCycleMovement
02508 // *
02509 // *******************************************************************************************
02513 // *******************************************************************************************
02514 
02515 gCycleMovement::gCycleMovement( nMessage & message )
02516         :eNetGameObject(message),
02517         destinationList(NULL),currentDestination(NULL),lastDestination(NULL),
02518         dirDrive(1,0),
02519         acceleration(0),
02520         totalZoneAcceleration(0),
02521         lastTimestep_(0),
02522         verletSpeed_(5)
02523 {
02524     windingNumberWrapped_ = windingNumber_ = 2;
02525 
02526     // MyInitAfterCreation();
02527 }
02528 
02529 // *******************************************************************************************
02530 // *
02531 // *    ~gCycleMovement
02532 // *
02533 // *******************************************************************************************
02536 // *******************************************************************************************
02537 
02538 gCycleMovement::~gCycleMovement( void )
02539 {
02540     lastDestination = NULL;
02541     currentDestination = NULL;
02542 
02543     while(destinationList)
02544     {
02545         gDestination* dest = destinationList;
02546         delete dest;
02547     }
02548 
02549     verletSpeed_=distance=0;
02550 
02551     delete maxSpaceHit_;
02552     maxSpaceHit_ = NULL;
02553 }
02554 
02555 void gCycleMovement::RequestSync(bool ack)
02556 {
02557     // no more syncs when you're dead
02558     if ( !Alive() )
02559     { 
02560        return;
02561     }
02562 
02563     // delegate
02564     eNetGameObject::RequestSync( ack );
02565 }
02566 
02567 void gCycleMovement::RequestSync(int user,bool ack)
02568 {
02569     // no more syncs when you're dead
02570     if ( !Alive() )
02571     {
02572         return;
02573     }
02574 
02575     // delegate
02576     eNetGameObject::RequestSync( user, ack );
02577 }
02578 
02579 void gCycleMovement::OnRemoveFromGame()
02580 {
02581     delete maxSpaceHit_;
02582     maxSpaceHit_ = NULL;
02583 
02584     eNetGameObject::OnRemoveFromGame();
02585 }
02586 
02587 // *******************************************************************************************
02588 // *
02589 // *    CopyFrom
02590 // *
02591 // *******************************************************************************************
02595 // *******************************************************************************************
02596 
02597 void gCycleMovement::CopyFrom( const gCycleMovement & other )
02598 {
02599     // calculate position update
02600     eCoord posUpdate = other.Position() - this->Position();
02601 
02602 #ifdef DEBUG_X
02603     // only update direction if the positions are out of sync
02604     REAL lag = 1;
02605     if ( player )
02606         lag = player->ping;
02607 
02608     REAL tol = this->speed * lag;
02609     if ( posUpdate.NormSquared() > tol*tol )// && eCoord::F( dirDrive, other.dirDrive ) < .5 )
02610     {
02611         con << "Out of sync!\n";
02612         //              dir                     = other.Direction();
02613 
02614         // get second opinion
02615         tJUST_CONTROLLED_PTR<gCycleExtrapolator> extrapolator = tNEW( gCycleExtrapolator )(grid, pos, dir );
02616         gCycleExtrapolator& secondOpinion = *extrapolator;
02617         secondOpinion.CopyFrom( sg_usedMessage, *this );
02618         eGameObject::TimestepThis( other.lastTime, &secondOpinion );
02619     }
02620 #endif
02621 
02622     dirDrive            = other.dirDrive;
02623 
02624     // transfer position and time
02625     currentFace = other.currentFace;
02626     pos = other.Position();
02627     lastTime = other.LastTime();
02628     // Move( other.Position(), LastTime(), other.LastTime() );
02629     // Move( other.Position() + other.Direction() * ( ( lastTime - other.LastTime() ) * other.Speed() ), LastTime(), other.LastTime() );
02630 
02631     // std::cout << "copy: " << brakingReservoir << ":" << braking << "\n";
02632 
02633     // transfer additional data
02634     team            = other.team;
02635     distance        = other.distance;
02636     lastTimestep_   = other.lastTimestep_;
02637     verletSpeed_    = other.verletSpeed_;
02638     acceleration    = other.acceleration;
02639     rubber              = other.rubber;
02640     rubberMalus     = other.rubberMalus;
02641     brakingReservoir= other.brakingReservoir;
02642     windingNumber_          = other.windingNumber_;
02643     windingNumberWrapped_   = other.windingNumberWrapped_;
02644 
02645     tASSERT(finite(distance));
02646 
02647     // std::cout << "copy: " << brakingReservoir << ":" << braking << "\n";
02648 
02649 #ifdef DEBUG_X
02650     if ( turns != other.turns )
02651     {
02652         con << "Client/Server turn mismatch:" << turns << " != " << other.turns << "\n";
02653     }
02654 #endif
02655 
02656     // update number of turns if the player is not turning wildly
02657     REAL right = GetNextTurn(1);
02658     REAL left  = GetNextTurn(-1);
02659     if ( lastTime > (right > left ? right : left) + 2 * GetTurnDelay() )
02660         turns                   = other.turns;
02661 }
02662 
02663 static nVersionFeature sg_sendCorrectLastTurn(8);
02664 
02665 // *******************************************************************************************
02666 // *
02667 // *    CopyFrom
02668 // *
02669 // *******************************************************************************************
02674 // *******************************************************************************************
02675 
02676 void gCycleMovement::CopyFrom( const SyncData & sync, const gCycleMovement & other )
02677 {
02678     // fetch values from sync
02679     dir = dirDrive      = sync.dir;
02680     lastTimestep_   = 0;
02681     verletSpeed_        = sync.speed;
02682     rubber                      = sync.rubber;
02683     rubberMalus     = sync.rubberMalus;
02684     braking                     = sync.braking;
02685     distance            = sync.distance;
02686     turns                       = sync.turns;
02687     brakingReservoir= sync.brakingReservoir;
02688     // std::cout << "fromsync: " << brakingReservoir << ":" << braking << "\n";
02689 
02690     tASSERT(finite(distance));
02691 
02692     // reset winding number and acceleration
02693     this->SetWindingNumberWrapped( Grid()->DirectionWinding(dirDrive) );
02694     acceleration        = 0;
02695 
02696     // fetch values from other
02697     // rubber = other.rubber;
02698     SetPlayer( other.Player() );
02699     currentFace         = other.currentFace;
02700 
02701     {
02702         //this->currentDestination = other.destinationList;
02703         //while ( currentDestination && currentDestination->messageID != sync.messageID )
02704         //    currentDestination = currentDestination->next;
02705 
02706         // deterimine last passed destination by the message ID
02707         this->currentDestination = GetDestinationBefore( sync, other.destinationList );
02708 
02709         bool trustDestination = true;
02710         if ( currentDestination && sn_GetNetState() == nCLIENT && !sg_sendCorrectLastTurn.Supported(0) )
02711         {
02712             // the server may send wrong information in the rare case that
02713             // our last turn command was not promptly executed. Sanity check:
02714             // if the relevant information from the alleged last destination
02715             // differs from the current state, it is already the next destination.
02716             if ( ( currentDestination->braking != (bool)braking ) || fabs( currentDestination->direction * dirDrive ) > .01 )
02717                 trustDestination = false;
02718         }
02719 
02720         // we only need the next one
02721         if ( trustDestination && currentDestination )
02722             currentDestination = currentDestination->next;
02723     }
02724 
02725     // let extrapolator find its face ( and set position and time )
02726     MoveSafely( sync.pos, sync.time, sync.time );
02727 
02728     // set last turn
02729     lastTurnTimeRight_ = lastTurnTimeLeft_ = -100;
02730 }
02731 
02732 // *******************************************************************************************
02733 // *
02734 // *    InitAfterCreation
02735 // *
02736 // *******************************************************************************************
02739 // *******************************************************************************************
02740 
02741 void gCycleMovement::InitAfterCreation( void )
02742 {
02743 #ifdef DEBUG
02744     if (!finite(verletSpeed_))
02745         st_Breakpoint();
02746 #endif
02747     eNetGameObject::InitAfterCreation();
02748 #ifdef DEBUG
02749     if (!finite(verletSpeed_))
02750         st_Breakpoint();
02751 #endif
02752     MyInitAfterCreation();
02753 }
02754 
02755 // version feature indicating that proper scaling of the base acceleration with the speed multiplier is used
02756 static nVersionFeature sg_correctAccelerationScaling( 8 );
02757 
02758 // calculate essential rubber values
02759 void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness )
02760 {
02761     // base values
02762     max=sg_rubberCycle;
02763     effectiveness=1;
02764 
02765     // make rubber more effective for high ping players
02766     if ( player )
02767     {
02768         if ( max > 0 )
02769             // either by increasing the effectiveness...
02770             effectiveness *= ( max + player->ping * sg_rubberCyclePing )/max;
02771         else
02772             // or the reservoir.
02773             max += player->ping * sg_rubberCyclePing;
02774     }
02775 
02776     {
02777         // modify rubber effectiveness by a speed dependant power law
02778         REAL speedFactor = speed/(sg_speedCycle*gCycleMovement::SpeedMultiplier());
02779 
02780         effectiveness *= pow( speedFactor, sg_rubberCycleTimeBased );
02781     }
02782 }
02783 
02784 // *******************************************************************************************
02785 // *
02786 // *    AccelerationDiscontinuity
02787 // *
02788 // *******************************************************************************************
02791 // *******************************************************************************************
02792 
02793 void gCycleMovement::AccelerationDiscontinuity()
02794 {
02795     // make fake 0 timestep
02796     verletSpeed_ = Speed();
02797     lastTimestep_ = 0;
02798 }
02799 
02800 // *******************************************************************************************
02801 // *
02802 // *    CalculateAcceleration
02803 // *
02804 // *******************************************************************************************
02807 // *******************************************************************************************
02808 
02809 void gCycleMovement::CalculateAcceleration()
02810 {
02811     // reset usage variables
02812     brakeUsage = 0.0f;
02813     rubberUsage = 0.0f;
02814 
02815     // calculate acceleration
02816     acceleration=0;
02817 
02818     // brake: it's only available since this version...
02819     static nVersionFeature brakeDepletion(2);
02820 
02821     // and servers starting from this version disable it my modifying config items
02822     static nVersionFeature brakeDepletionHandledWithConfig(10);
02823 
02824     // simply use the configured brake always on the server
02825     // and on the client if the server should have disabled it, but does not.
02826 
02827     if ( sn_GetNetState() != nCLIENT || brakeDepletion.Supported() || brakeDepletionHandledWithConfig.Supported(0) )
02828     {
02829         if(braking)
02830         {
02831             if ( brakingReservoir > 0.0 )
02832             {
02833                 brakeUsage = sg_cycleBrakeDeplete;
02834                 acceleration-=sg_brakeCycle * SpeedMultiplier();
02835             }
02836             else
02837                 brakingReservoir = 0.0f;
02838         }
02839         else
02840         {
02841             if ( brakingReservoir < 1.0 )
02842             {
02843                 brakeUsage = -sg_cycleBrakeRefill;
02844             }
02845             else
02846                 brakingReservoir = 1.0f;
02847         }
02848     }
02849     else
02850     {
02851         if(braking)
02852         {
02853             acceleration-=sg_brakeCycle * SpeedMultiplier();
02854         }
02855     }
02856 
02857     sg_ArchiveReal( acceleration, 9 );
02858 
02859     REAL baseSpeed = sg_speedCycle * SpeedMultiplier();
02860     if ( verletSpeed_ <= ( sg_correctAccelerationScaling.Supported() ? baseSpeed : sg_speedCycle ) )
02861         acceleration+=( baseSpeed - verletSpeed_) * sg_speedCycleDecayBelow;
02862     else
02863         acceleration+=( baseSpeed - verletSpeed_) * sg_speedCycleDecayAbove;
02864 
02865     tASSERT( good( acceleration ) );
02866     sg_ArchiveReal( acceleration, 9 );
02867 
02868     // sense near wall behind us, accelerate more
02869     REAL totalWallAcceleration = 0; // total acceleration by walls
02870     REAL tunnelWidth           = 0; // with of the tunnel the cycle is in
02871     REAL sideWidth             = sg_cycleWidthSide * 2; // minimal distance to wall
02872     bool slingshot  = true;         // flag indicating whether the cycle is between two walls
02873     bool oneOwnWall = false;        // flag indicating whether one of the walls is your own
02874     for(int d=1;d>=-1;d-=2){
02875         // the direction to cast the acceleration rays in
02876         eCoord dirCast = dirDrive.Turn(-1,d);
02877         gSensor rear(this,pos,dirCast);
02878         rear.detect(sg_nearCycle);
02879 
02880         enemyInfluence.AddSensor( rear, 0, this );
02881 
02882         if (rear.ehit && rear.hit < sg_cycleWidth + .1f )
02883             blocks(rear, this, -d);
02884 
02885         if ( 0 != rear.ehit )
02886         {
02887             sg_ArchiveReal( rear.hit, 9 );
02888 
02889             // update the minimal wall distance
02890             if ( sideWidth > rear.hit )
02891                 sideWidth = rear.hit;
02892 
02893             // drop walls that are grinded
02894             if ( rear.hit < verletSpeed_ * .01 )
02895                 ::sg_DropTempWall( dirCast, rear );
02896 
02897             // see if the wall is parallel to the driving direction, only then should it add speed
02898             eCoord wallVec = rear.ehit->Vec();
02899             if ( fabs( eCoord::F( wallVec, dirDrive  ) ) > .9 * dirDrive.NormSquared() )
02900             {
02901                 // enemyInfluence.AddSensor( rear, 1 );
02902                 REAL wallAcceleration=SpeedMultiplier() * sg_accelerationCycle * ((1/(rear.hit+sg_accelerationCycleOffs))
02903                                       -(1/(sg_nearCycle+sg_accelerationCycleOffs)));
02904 
02905                 tunnelWidth += rear.hit;
02906 
02907                 // apply modificators
02908                 switch (rear.type)
02909                 {
02910                 case gSENSOR_SELF:
02911                     wallAcceleration *= sg_accelerationCycleSelf;
02912                     oneOwnWall = true;
02913                     break;
02914                 case gSENSOR_TEAMMATE:
02915                     wallAcceleration *= sg_accelerationCycleTeam;
02916                     break;
02917                 case gSENSOR_ENEMY:
02918                     wallAcceleration *= sg_accelerationCycleEnemy;
02919                     break;
02920                 case gSENSOR_RIM:
02921                     wallAcceleration *= sg_accelerationCycleRim;
02922                     break;
02923                 case gSENSOR_NONE:
02924                     wallAcceleration = 0;
02925                     slingshot = false;
02926                     break;
02927 
02928                 }
02929 
02930                 sg_ArchiveReal( wallAcceleration, 9 );
02931                 totalWallAcceleration += wallAcceleration;
02932             }
02933             else
02934             {
02935                 slingshot = false;
02936             }
02937         }
02938         else
02939         {
02940             slingshot = false;
02941         }
02942 
02943         sg_ArchiveReal( totalWallAcceleration, 9 );
02944     }
02945 
02946     // kill cycle if it is inside a too narrow channel
02947     if ( slingshot && tunnelWidth < sg_cycleWidth || sideWidth < sg_cycleWidthSide )
02948     {
02949         tunnelWidth = 0;
02950         REAL sideWidth = sg_cycleWidthSide * 2;
02951 
02952         // check again with sensors to the front, both sensor pairs need
02953         // to see a narrow tunnel
02954         for(int d=1;d>=-1;d-=2)
02955         {
02956             // the direction to cast the acceleration rays in
02957             eCoord dirCast = dirDrive.Turn(1,d);
02958             gSensor front(this,pos,dirCast);
02959             front.detect(sg_nearCycle);
02960 
02961             if ( front.ehit && front.ehit->Other() )
02962             {
02963                 sg_ArchiveReal( front.hit, 9 );
02964 
02965                 // update the minimal wall distance
02966                 if ( sideWidth > front.hit )
02967                     sideWidth = front.hit;
02968 
02969                 tunnelWidth += front.hit;
02970             }
02971             else
02972             {
02973                 tunnelWidth += sg_cycleWidth;
02974             }
02975         }
02976 
02977         if ( tunnelWidth < sg_cycleWidth || sideWidth < sg_cycleWidthSide )
02978         {
02979             // determine the space available measured in the space allowed
02980             REAL available1 = 1;
02981             REAL available2 = 1;
02982             if ( sg_cycleWidth > 0 )
02983                 available1 = tunnelWidth/sg_cycleWidth;
02984             if ( sg_cycleWidthSide > 0 )
02985                 available2 = sideWidth/sg_cycleWidthSide;
02986             REAL available = available1 < available2 ? available1 : available2;
02987 
02988             // get rubber values
02989             // REAL rubberGranted, rubberEffectiveness;
02990             // sg_RubberValues( player, verletSpeed_, rubberGranted, rubberEffectiveness );
02991 
02992             // calculate rubber usage from squeezing
02993             rubberUsage = sg_cycleWidthRubberMax + ( sg_cycleWidthRubberMin - sg_cycleWidthRubberMax ) * available;
02994         }
02995     }
02996 
02997     // apply slingshot/tunnel multiplier
02998     if ( slingshot )
02999     {
03000         if ( oneOwnWall )
03001             totalWallAcceleration *= sg_accelerationCycleSlingshot;
03002         else
03003             totalWallAcceleration *= sg_accelerationCycleTunnel;
03004     }
03005 
03006     // apply wall acceleration
03007     acceleration += totalWallAcceleration;
03008     acceleration += totalZoneAcceleration;
03009     totalZoneAcceleration = 0.0; // This comes from external influence and should be applied only once per Timestep
03010 
03011     tASSERT( good( acceleration ) );
03012     sg_ArchiveReal( acceleration, 9 );
03013 }
03014 
03015 // *******************************************************************************************
03016 // *
03017 // *    ApplyAcceleration
03018 // *
03019 // *******************************************************************************************
03023 // *******************************************************************************************
03024 
03025 void gCycleMovement::ApplyAcceleration( REAL dt )
03026 {
03027     sg_ArchiveReal( verletSpeed_, 9 );
03028     sg_ArchiveReal( dt, 9 );
03029     sg_ArchiveReal( acceleration, 9 );
03030 
03031     // the speed needs to be simulated for this half frame and half of the last frame
03032     REAL verletTimestep = sg_verletIntegration.Supported() ? .5 * ( dt + lastTimestep_ ) : dt;
03033     lastTimestep_ = dt;
03034 
03035     sg_ArchiveReal( verletTimestep, 9 );
03036 
03037     // don't use euler timesteps for large cycle speed decays
03038     bool properDecay = false;
03039     REAL maxTimestep = verletTimestep > dt ? verletTimestep : dt;
03040     if ( sg_speedCycleDecayBelow * maxTimestep > .1 || sg_speedCycleDecayAbove * maxTimestep > .1 )
03041     {
03042         REAL speedDecay = 0;
03043         REAL baseSpeed = sg_speedCycle * SpeedMultiplier();
03044         if ( verletSpeed_ < ( sg_correctAccelerationScaling.Supported() ? baseSpeed : sg_speedCycle ) )
03045             speedDecay = sg_speedCycleDecayBelow;
03046         else
03047             speedDecay = sg_speedCycleDecayAbove;
03048 
03049         if ( speedDecay * maxTimestep > .1 && dt > EPS )
03050         {
03051             // ok, really, a  better simulation is needed
03052             properDecay = true;
03053 
03054             // that's what CalculateAcceleration extrapolates
03055             REAL decayAcceleration = ( baseSpeed - verletSpeed_) * speedDecay;
03056             // throw it away
03057             acceleration -= decayAcceleration;
03058 
03059             tASSERT( good( acceleration ) );
03060 
03061             // adapt base speed as the limit speed with the current decay and acceleration
03062             baseSpeed += acceleration/speedDecay;
03063 
03064             // do a proper decay
03065             verletSpeed_ = baseSpeed + ( verletSpeed_ - baseSpeed ) * exp( -speedDecay * verletTimestep );
03066 
03067             // calculate new acceleration based purely on the decay, the external acceleration
03068             // is factored into baseSpeed now. Add extra decay factor so that
03069             // Speed() returns the most accurate current speed available.
03070             acceleration = ( baseSpeed - verletSpeed_) * ( 1 - exp( -speedDecay * dt * .5f ) ) / ( .5f * dt );
03071 
03072             tASSERT( good( acceleration ) );
03073         }
03074     }
03075 
03076     // if decay wasn't handled properly (because it didn't need to), use euler/verlet
03077     if ( !properDecay )
03078         verletSpeed_+=acceleration*verletTimestep;
03079 
03080     // clamp speed
03081     REAL minSpeed = sg_speedCycle*SpeedMultiplier()*sg_speedCycleMin;
03082     REAL maxSpeed = ( 100 + sg_speedCycle*SpeedMultiplier() )* 100000;
03083     if ( sg_speedCycleMax > 0 )
03084     {
03085         maxSpeed = sg_speedCycle*SpeedMultiplier()*sg_speedCycleMax;
03086     }
03087 
03088     sg_ArchiveReal( minSpeed, 9 );
03089     sg_ArchiveReal( maxSpeed, 9 );
03090     sg_ArchiveReal( acceleration, 9 );
03091 
03092     if ( clamp( verletSpeed_, minSpeed, maxSpeed ) )
03093         acceleration = 0;
03094 
03095     sg_ArchiveReal( acceleration, 9 );
03096 
03097     sg_ArchiveReal( verletSpeed_, 9 );
03098 }
03099 
03100 // *******************************************************************************************
03101 // *
03102 // *    DoTurn
03103 // *
03104 // *******************************************************************************************
03109 // *******************************************************************************************
03110 
03111 bool gCycleMovement::DoTurn( int dir )
03112 {
03113     if ( turns == 0 )
03114         turns = 1;
03115 
03116     if (dir >  1) dir =  1;
03117     if (dir < -1) dir = -1;
03118 
03119     if ( CanMakeTurn( lastTime, dir ) )
03120     {
03121         // request regeneration of maximum space
03122         refreshSpaceAhead_ = true;
03123 
03124         // notify that no rubber is currently used (may be a lie, but a timestep correcting
03125         // it will surely follow)
03126         rubberSpeedFactor = 1;
03127 
03128         // store last postion
03129         lastTurnPos_ = pos;
03130 
03131         turns++;
03132 
03133         AccelerationDiscontinuity();
03134         verletSpeed_ *= sg_cycleTurnSpeedFactor;
03135         rubberMalus  += sg_rubberCycleMalusTurn;
03136 
03137         gap_[0] = gap_[1] = 1E+30;
03138         keepLookingForGap_[0] = keepLookingForGap_[1] = true;
03139 
03140         // turn winding numbers
03141         int wn = windingNumberWrapped_;
03142         Grid()->Turn(wn, dir);
03143         this->SetWindingNumberWrapped( wn );
03144 
03145         eCoord nextDirDrive = Grid()->GetDirection(windingNumberWrapped_);
03146 
03147         // send out a sensor a bit backwards and forwards into the turn direction to
03148         // copy all temporary walls into the grid
03149         {
03150             REAL range = .1 * Speed();
03151             eCoord dirCast = nextDirDrive;
03152             gSensor gridder1( this, Position(), dirCast );
03153             gridder1.detect( range );
03154             if ( gridder1.ehit )
03155                 ::sg_DropTempWall( nextDirDrive, gridder1 );
03156 
03157             gSensor gridder3( this, Position() - dirCast * (range*.5), dirCast );
03158             gridder3.detect( range );
03159             if ( gridder3.ehit )
03160                 ::sg_DropTempWall( nextDirDrive, gridder3 );
03161 
03162             // the ray backwards should detect walls that affected the acceleration;
03163             // they can also give a boost. Increase the range.
03164             if ( range < sg_nearCycle )
03165                 range = sg_nearCycle;
03166 
03167             gSensor gridder2( this, Position(), -dirCast );
03168             gridder2.detect( range );
03169             if ( gridder2.ehit )
03170             {
03171                 ::sg_DropTempWall( nextDirDrive, gridder2 );
03172 
03173                 // apply the boost. Calculate wall distance
03174                 REAL dist = gridder2.hit;
03175 
03176                 // calculate the factor acceleration would be multiplied with
03177                 REAL accellerationFactorOffset = 1/(sg_nearCycle+sg_accelerationCycleOffs);
03178                 REAL accelerationFactor = (1/(dist+sg_accelerationCycleOffs)) - accellerationFactorOffset;
03179                 // this would be the maximal acceleration factor
03180                 REAL accelerationFactorMax = (1/sg_accelerationCycleOffs) - accellerationFactorOffset;
03181 
03182                 // select boost settings according to wall type
03183                 // apply modificators
03184                 REAL boost = 0, boostFactor = 1;
03185                 switch (gridder2.type)
03186                 {
03187                 case gSENSOR_SELF:
03188                     boost = sg_boostCycleSelf;
03189                     boostFactor = sg_boostFactorCycleSelf;
03190                     break;
03191                 case gSENSOR_TEAMMATE:
03192                     boost = sg_boostCycleTeam;
03193                     boostFactor = sg_boostFactorCycleTeam;
03194                     break;
03195                 case gSENSOR_ENEMY:
03196                     boost = sg_boostCycleEnemy;
03197                     boostFactor = sg_boostFactorCycleEnemy;
03198                     break;
03199                 case gSENSOR_RIM:
03200                     boost = sg_boostCycleRim;
03201                     boostFactor = sg_boostFactorCycleRim;
03202                     break;
03203                 case gSENSOR_NONE:
03204                     break;
03205                 }
03206 
03207                 // apply acceleration factor to boost
03208                 boostFactor = 1 + ( boostFactor - 1 ) * accelerationFactor / accelerationFactorMax;
03209                 boost *= SpeedMultiplier() * accelerationFactor / accelerationFactorMax;
03210 
03211                 // apply boost to speed
03212                 verletSpeed_ = verletSpeed_ * boostFactor + boost;
03213             }
03214 
03215             // if edges have been inserted into the grid, find a new current face.
03216             FindCurrentFace();
03217         }
03218 
03219         // update driving directions
03220         lastDirDrive = dirDrive;
03221 
03222         if(dir == 1)
03223             lastTurnTimeRight_ = lastTime;
03224         else
03225             lastTurnTimeLeft_ = lastTime;
03226 
03227         dirDrive = nextDirDrive;
03228 
03229 #ifdef DEBUGOUTPUT
03230         if ( sg_cycleDebugPrintLevel > 0 )
03231             con << Player()->GetName() << " turned " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
03232 #endif
03233 
03234         return true;
03235     }
03236     else {
03237         int maxPendingTurns=sg_cycleTurnMemory;
03238         int size = pendingTurns.size();
03239         // std::cerr << "size of " << &pendingTurns << ": " << size << std::endl;
03240         if (size <= maxPendingTurns)
03241             pendingTurns.push_back(dir);
03242         else {
03243             if(pendingTurns.empty()) return false; //just to be sure
03244             if(pendingTurns.back() != dir) {
03245                 pendingTurns.pop_back(); //opposite turns cancel so the cycle still moves into the expected direction
03246             }
03247             else {
03248                 pendingTurns.push_back(dir); //add it anyways...
03249             }
03250         }
03251     }
03252 
03253     return false;
03254 }
03255 
03256 // *******************************************************************************************
03257 // *
03258 // *    RightBeforeDeath
03259 // *
03260 // *******************************************************************************************
03264 // *******************************************************************************************
03265 
03266 void gCycleMovement::RightBeforeDeath( int numTries )
03267 {
03268 }
03269 
03270 // *******************************************************************************************
03271 // *
03272 // *    Die
03273 // *
03274 // *******************************************************************************************
03278 // *******************************************************************************************
03279 
03280 void gCycleMovement::Die( REAL time )
03281 {
03282     // only do something if you are alive
03283     if ( alive_ == 1 )
03284     {
03285         alive_ = -1;
03286         deathTime = time;
03287     }
03288 
03289     // or complete death if you died only recently
03290     if ( alive_ == -1 )
03291     {
03292         alive_ = 0;
03293     }
03294 }
03295 
03296 class gRecursionGuard
03297 {
03298 public:
03299     gRecursionGuard( bool & guard )
03300             : guard_( guard )
03301     {
03302         guard_ = false;
03303     }
03304     ~gRecursionGuard()
03305     {
03306         guard_ = true;
03307     }
03308 private:
03309     bool & guard_;
03310 };
03311 
03312 // *******************************************************************************************
03313 // *
03314 // *    TimestepCore
03315 // *
03316 // *******************************************************************************************
03321 // *******************************************************************************************
03322 
03323 bool gCycleMovement::TimestepCore( REAL currentTime, bool calculateAcceleration )
03324 {
03325     eCoord oldpos=pos;
03326     REAL lastSpeed=verletSpeed_;
03327 
03328     REAL ts=(currentTime-lastTime);
03329 
03330     // calculate acceleration
03331     if ( calculateAcceleration )
03332         this->CalculateAcceleration();
03333 
03334     // ApplyAcceleration modifies the acceleration, so we need to back it up
03335     REAL lastAcceleration=acceleration;
03336 
03337     // calculate when the braking reservoir will run dry and simulate to that point
03338     {
03339         static bool recurse = true;
03340         if (recurse && brakingReservoir > 0 && brakeUsage > 0 && brakingReservoir - ts * brakeUsage < 0 )
03341         {
03342             gRecursionGuard guard( recurse );
03343 
03344             // calculate the time the brake will run out
03345             REAL brakeTime = lastTime + brakingReservoir/brakeUsage;
03346             if ( TimestepCore( brakeTime, false ) )
03347                 return true;
03348             AccelerationDiscontinuity();
03349             brakingReservoir = -EPS;
03350             return TimestepCore( currentTime );
03351         }
03352     }
03353 
03354     // apply acceleration
03355     if ( sg_verletIntegration.Supported() )
03356         this->ApplyAcceleration( ts );
03357 
03358     //eDebugLine::SetTimeout( 2 );
03359     //eDebugLine::SetColor(1,1,0);
03360     //eDebugLine::Draw(pos, 4, pos, 4 + 20 * ts);
03361 
03362     sg_ArchiveCoord( pos, 9 );
03363     sg_ArchiveReal( ts, 9 );
03364     sg_ArchiveReal( verletSpeed_, 9 );
03365 
03366 #ifdef DEBUG
03367     if ( ts > 2.0f )
03368     {
03369         int x;
03370         x = 0;
03371     }
03372 
03373     if ( verletSpeed_ > 30.0f )
03374     {
03375         int x;
03376         x = 0;
03377     }
03378 
03379     if ( acceleration > 100.0f )
03380     {
03381         int x;
03382         x = 0;
03383     }
03384 #endif
03385 
03386     clamp(ts, -10, 10);
03387 
03388     REAL step=verletSpeed_*ts;
03389     tASSERT(finite(step));
03390 
03391     int numTries = 0;
03392     bool emergency = false;
03393 
03394     rubberSpeedFactor = 1;
03395 
03396     // be a little nice and don't drive into the wall
03397     REAL rubber_granted, rubberEffectiveness;
03398 
03399     // get rubber values
03400     sg_RubberValues( player, verletSpeed_, rubber_granted, rubberEffectiveness );
03401 
03402     // rubber effectiveness right now
03403     rubberEffectiveness /= (1 + rubberMalus );
03404 
03405     // reduce it further if cycle turned recently
03406     {
03407         REAL delayTime = (lastTurnTimeRight_ > lastTurnTimeLeft_ ? lastTurnTimeRight_ : lastTurnTimeLeft_) + GetTurnDelay() * sg_rubberCycleDelay;
03408         if ( lastTime < delayTime )
03409         {
03410             rubberEffectiveness *= sg_rubberCycleDelayBonus;
03411 
03412             // if the target time is after the rubber delay ends...
03413             if( currentTime > delayTime )
03414             {
03415                 static bool recurse = true;
03416                 if (recurse)
03417                 {
03418                     gRecursionGuard guard( recurse );
03419 
03420                     verletSpeed_=lastSpeed;
03421                     acceleration=lastAcceleration;
03422                     // do two small timesteps
03423                     return TimestepCore( delayTime, false ) || TimestepCore( currentTime );
03424                 }
03425             }
03426         }
03427     }
03428 
03429     sg_ArchiveReal( rubberEffectiveness, 9 );
03430 
03431     tASSERT( rubber >= 0 );
03432 
03433     // TODO: solve smooth position correction trouble with rubber
03434     if ( player && ( rubber_granted > rubber || sn_GetNetState() == nCLIENT || !Vulnerable() ) && sg_rubberCycleSpeed > 0 && step > -EPS && ( sn_GetNetState() == nCLIENT || rubberEffectiveness > 0 ) )
03435     {
03436         // ignore zero effectiveness, this happens only on the client
03437         if ( rubberEffectiveness <= 0 )
03438             rubberEffectiveness = 1E+20;
03439 
03440         // formerly: rubberFactor = .5
03441         REAL beta = ts * sg_rubberCycleSpeed;
03442         REAL neededSpace = 0;
03443         REAL rubberFactor;
03444         if ( beta > .001 )
03445         {
03446             rubberFactor = 1 - exp( -beta );
03447             neededSpace = step/rubberFactor;
03448         }
03449         else
03450         {
03451             rubberFactor = beta;        // better accuracy than the full formula
03452 
03453             // a lot of factors can be cut out of this one (avoiding a division by zero for ts=0)
03454             neededSpace = verletSpeed_/sg_rubberCycleSpeed;
03455         }
03456 
03457         // rubberFactor must not be too close to 1, otherwise we get precision trouble
03458         if ( rubberFactor > .999 )
03459             rubberFactor = .999;
03460 
03461         // revert to old rubber logic if old clients are connected
03462         if ( sg_rubberCycleLegacy && !sg_nonRippable.Supported() && rubberFactor < .5f )
03463             rubberFactor = .5f;
03464 
03465         // space we need to look ahead
03466         if ( neededSpace < step*3 || ts < -EPS )
03467             neededSpace = step*3;
03468 
03469         // determine how long we can drive on
03470         // REAL space = GetMaxSpaceAhead( this, neededSpace, ts * step * rubberFactor / rubberEffectiveness, &hitInfo );
03471         REAL space = GetMaxSpaceAhead( neededSpace );
03472 
03473 #ifdef DEBUG_RUBBER
03474         if ( Player() && space < 1E+15)
03475         {
03476             std::ofstream f( Player()->GetUserName() + "_rubber", std::ios::app );
03477             f << lastTime << " " << space << "\n";
03478         }
03479 #endif
03480 
03481         // if the available space in front is less than the space needed to slow down via
03482         // the rubber brake, activate rubber and slow down
03483         if ( space < neededSpace )
03484         {
03485             // the minimal space rubber gets active at
03486             REAL rubberStartSpace = verletSpeed_/sg_rubberCycleSpeed;
03487             static bool recurse = true;
03488             if ( space > rubberStartSpace && recurse )
03489             {
03490                 // rubber will not be active immediately, simulate to the time it will
03491                 gRecursionGuard guard( recurse );
03492 
03493                 // calculate the time rubber will get active at
03494                 REAL ratio = ( space - rubberStartSpace )/step;
03495                 if ( ratio > EPS && ratio < 1 - EPS )
03496                 {
03497                     REAL rubberGetsActiveTime = lastTime + ( currentTime - lastTime ) * ratio;
03498 
03499                     verletSpeed_=lastSpeed;
03500                     acceleration=lastAcceleration;
03501                     return TimestepCore( rubberGetsActiveTime, false ) || TimestepCore( currentTime );
03502                 }
03503             }
03504 #ifdef DEDICATED
03505             else
03506             {
03507                 // see if the wall we're about to hit comes from its cycle's future. If so,
03508                 // it is a prediction wall and we shouldn't actually use rubber before we
03509                 // have to.
03510                 if ( maxSpaceHit_ && maxSpaceHit_->playerWall )
03511                 {
03512                     gPlayerWall * wall = maxSpaceHit_->playerWall;
03513 
03514                     // get the position of the hit
03515                     REAL alpha = maxSpaceHit_->wallAlpha;
03516 
03517                     // get the distance of the wall
03518                     REAL wallDist = wall->Pos( alpha );
03519                     // get the distance the cycle is simulated up to
03520                     REAL cycleDist = wall->CycleMovement()->distance;
03521                     // comparing these two gives an accurate criterion whether the wall is extrapolated
03522 
03523                     REAL minLag = se_GameTime() - lastTime - LagThreshold();
03524                     if ( cycleDist < wallDist && ( minLag < Lag() || minLag < wall->CycleMovement()->Lag() ) )
03525                     {
03526                         // it is an extrapolation wall and we are allowed to delay simulation a bit.
03527                         // so let's abort here.
03528                         verletSpeed_=lastSpeed;
03529                         acceleration=lastAcceleration;
03530 
03531                         return false;
03532                     }
03533                 }
03534             }
03535 #endif
03536 
03537             // see if the obstacle will go away during this timestep.
03538             // if it does, simulate in two steps to make the simulation more accurate.
03539             {
03540                 // get the wall
03541                 if ( maxSpaceHit_ && maxSpaceHit_->playerWall )
03542                 {
03543                     gPlayerWall * wall = maxSpaceHit_->playerWall;
03544 
03545                     // get the position of the hit
03546                     REAL alpha = maxSpaceHit_->wallAlpha;
03547 
03548                     // use binary search to find the time the wall goes away. Not
03549                     // the fastest way, but it doesn't depend on wall internals, and
03550                     // it shouldn't be called often anyway.
03551                     REAL tolerance = 0.001;
03552                     if ( !wall->IsDangerous( alpha, currentTime ) && currentTime > lastTime + tolerance )
03553                     {
03554                         // take movement speed into account, we won't hit the wall for
03555                         // another distanceOffset seconds
03556                         REAL distanceOffset = 0;
03557                         {
03558                             REAL speed = Speed();
03559                             if ( speed > 0 )
03560                                 distanceOffset = space/speed;
03561                         }
03562 
03563                         REAL minTime = lastTime + distanceOffset;
03564                         REAL maxTime = currentTime + distanceOffset;
03565                         while ( minTime + tolerance < maxTime )
03566                         {
03567                             REAL midTime = .5 * ( minTime + maxTime );
03568                             if ( wall->IsDangerous( alpha, midTime ) )
03569                                 minTime = midTime;
03570                             else
03571                                 maxTime = midTime;
03572                         }
03573 
03574                         maxTime -= distanceOffset;
03575                         // minTime -= distanceOffset;
03576 
03577                         // split simulation into two parts, one up to the point the wall turns harmless
03578                         {
03579                             static bool recurse = true;
03580                             if (recurse)
03581                             {
03582                                 gRecursionGuard guard( recurse );
03583 
03584                                 verletSpeed_=lastSpeed;
03585                                 acceleration=lastAcceleration;
03586                                 return TimestepCore( maxTime, false ) || TimestepCore( currentTime );
03587                             }
03588                         }
03589                     }
03590                 }
03591             }
03592 
03593             /*
03594             // debug output for sensitive space/time diagrams
03595             static REAL lastTimePrinted = 0;
03596             if ( currentTime > lastTimePrinted && Player() )
03597             {
03598                 lastTimePrinted = currentTime;
03599                 std::ofstream f( Player()->GetUserName() + "_space", std::ios::app );
03600                 f << lastTime << " " << log(space) << "\n";
03601             }
03602             */
03603 
03604             // notify AIs of it
03605             emergency = true;
03606 
03607             // calculate the step the rubber code should do based on the decay factor
03608             // calculated earler
03609             REAL rubberStep = space * rubberFactor;
03610             if ( rubberStep > step )
03611                 rubberStep = step;
03612 
03613             // clamp the step
03614             if (step<0)
03615                 step=0;
03616 
03617             // calculate the amount of rubber needed for the desired brake effect
03618             REAL rubberneeded = step - rubberStep;
03619             if (rubberneeded < 0)
03620                 rubberneeded = 0;
03621 
03622             // clamp rubberneeded to the amout of rubber available
03623             REAL rubberAvailable = ( rubber_granted - rubber ) * rubberEffectiveness;
03624             if ( sn_GetNetState() != nCLIENT && rubberneeded > rubberAvailable && Vulnerable() )
03625             {
03626                 // rubber will run out this frame.
03627                 // split simulation into two parts, one up to the point rubber runs out
03628                 {
03629                     REAL ratio = rubberAvailable/rubberneeded;
03630 
03631                     if ( ratio > .01 && ratio < .99 && currentTime - lastTime > .001 )
03632                     {
03633                         REAL runOutTime = lastTime + ( currentTime - lastTime ) * ratio;
03634                         static bool recurse = true;
03635                         if (recurse)
03636                         {
03637                             gRecursionGuard guard( recurse );
03638                             // need many attempts
03639                             verletSpeed_=lastSpeed;
03640                             acceleration=lastAcceleration;
03641                             return TimestepCore( runOutTime, false ) || TimestepCore( currentTime );
03642                         }
03643                     }
03644                 }
03645 
03646                 rubberneeded = rubberAvailable;
03647             }
03648 
03649             // update rubber usage
03650             rubber += rubberneeded / rubberEffectiveness;
03651 
03652             numTries = int((sg_rubberCycleTime * ( rubber_granted - rubber ) - 1 )/(sg_rubberCycleTime * step*1.5 + 1));
03653             int numTriesSpace = int(space*10/verletSpeed_);
03654             if ( numTriesSpace < numTries )
03655                 numTriesSpace = 0;
03656 
03657             if ( step > 0 )
03658                 rubberSpeedFactor = 1 - rubberneeded/step;
03659             else
03660                 // better algorithm for zero steps
03661                 rubberSpeedFactor = space / neededSpace;
03662 
03663             // clamp
03664             if ( rubberSpeedFactor < 0 )
03665                 rubberSpeedFactor = 0;
03666 
03667             // correct the step to take, don't go backwards.
03668             step -= rubberneeded;
03669             if (step<0)
03670                 step=0;
03671 
03672             //{
03673             //    rubber+=step;
03674             //    step=0;
03675             //}
03676         }
03677     }
03678 
03679     tASSERT( rubber >= 0 );
03680 
03681     sg_ArchiveReal( step, 9 );
03682 
03683     // move forward
03684     eCoord nextpos;
03685     if ( verletSpeed_ >0 )
03686         nextpos=pos+dirDrive*step;
03687     else
03688         nextpos=pos;
03689 
03690     eCoord lastPos = pos;
03691     tJUST_CONTROLLED_PTR< eFace > lastFace = currentFace;
03692     try
03693     {
03694 #ifdef DEBUG
03695         static int run = 0;
03696         run++;
03697         if ( run == -1 )
03698         {
03699             st_Breakpoint();
03700         }
03701 #endif
03702         Move(nextpos,lastTime,currentTime);
03703 #ifdef DEBUG
03704         {
03705             if ( step > 0 && ( nextpos - pos ).NormSquared() > 1 )
03706             {
03707                 con << "Wrong move! run = " << run << ", nextpos = " << nextpos << ", pos = " << pos << "\n";
03708             }
03709         }
03710 #endif
03711 
03712         tASSERT(finite(distance));
03713         tASSERT(finite(step));
03714         distance += step;
03715         lastTimeAlive_ = currentTime;
03716     }
03717     catch ( gCycleStop const & )
03718     {
03719         // undo simulation done so far and stop
03720         pos = lastPos;
03721         verletSpeed_ = lastSpeed;
03722         acceleration = lastAcceleration;
03723         currentFace = lastFace;
03724         numTries = 0;
03725 
03726         // don't simulate further
03727         return false;
03728     }
03729     catch ( gCycleDeath const & )
03730     {
03731         rubberSpeedFactor = 0;
03732 
03733         // the cycle should die in this movement. Prevent it if there is rubber left.
03734         // if RUBBER_MINDISTANCE is negative and the player is not an AI, the cycle dies anyway.
03735         if ( rubberEffectiveness <= 0 || step >= (rubber_granted-rubber)*rubberEffectiveness || ( sg_rubberCycleMinDistance < 0 && Player() && Player()->IsHuman() ) )
03736         {
03737             // last survival chance: packet loss protection. Determine whether it should be in effect..
03738             bool toleratePacketLoss = false;
03739             if (!currentDestination)
03740             {
03741                 // calculate time tolerance to capture packet loss...
03742                 REAL tolerance = Lag() * sg_packetLossTolerance;
03743 
03744                 // add lag credit on top of that
03745                 if ( Owner() > 0 )
03746                     tolerance += eLag::Credit( Owner() );
03747 
03748                 // add lag fluctuation to the mix
03749                 if ( sn_GetNetState() == nSERVER && player && player->Owner() != 0 )
03750                 {
03751                     REAL varianceTolerance = 2 * sqrtf( sn_Connections[ player->Owner() ].ping.GetSnailAverager().GetDataVariance() );
03752                     // clamp it, high fluctuations are the player's own problem
03753                     if ( varianceTolerance > tolerance )
03754                         varianceTolerance = tolerance;
03755                     tolerance += varianceTolerance;
03756                 }
03757 
03758                 // if time has not progressed beyond tolerance, protection may be in effect
03759                 toleratePacketLoss = ( se_GameTime() - Lag() - lastTimeAlive_ < tolerance );
03760             }
03761 
03762             // ... and apply it.
03763             if ( toleratePacketLoss )
03764             {
03765                 pos = lastPos;
03766                 verletSpeed_ = lastSpeed;
03767                 acceleration = lastAcceleration;
03768                 currentFace = lastFace;
03769                 numTries = 0;
03770                 emergency = true;
03771 
03772                 // don't simulate further
03773                 return false;
03774             }
03775             else
03776             {
03777                 // no, no straw left. Rethrow and get killed.
03778                 rubber = rubber_granted;
03779 
03780                 // update distance to include the really covered space
03781                 tASSERT(finite(distance));
03782                 distance += eCoord::F( dirDrive, pos - lastPos )/dirDrive.NormSquared();
03783                 tASSERT(finite(distance));
03784 
03785                 throw;
03786             }
03787         }
03788         else
03789         {
03790             pos = lastPos;
03791             currentFace = lastFace;
03792             rubber += step/rubberEffectiveness;
03793             if ( rubber < 0 )
03794                 rubber = 0;
03795 
03796             numTries = 0;
03797             emergency = true;
03798         }
03799     }
03800 
03801     tASSERT( rubber >= 0 );
03802 
03803     // use up rubber from tunneling (calculated by CalculateAcceleration
03804     if ( rubberEffectiveness > 0 )
03805     {
03806         rubber += rubberUsage * ts * verletSpeed_ / rubberEffectiveness;            }
03807     else if ( rubberUsage > 0 )
03808     {
03809         rubber = rubber_granted + 10;
03810     }
03811     rubberUsage = 0;
03812 
03813     // decide over kill
03814     if ( rubber > rubber_granted || ( sg_cycleWidthRubberMax == 0 && sg_cycleWidthRubberMin == 0 ) )
03815     {
03816         if ( sn_GetNetState() != nCLIENT )
03817         {
03818             throw gCycleDeath( pos );
03819         }
03820         else
03821             rubber = rubber_granted;
03822     }
03823 
03824     // use up brake
03825     brakingReservoir -= brakeUsage * ts;
03826     clamp( brakingReservoir, 0, 1 );
03827 
03828     // let rubber decay
03829     if ( sg_rubberCycleTime > 0 )
03830         rubber /= (1+ts/sg_rubberCycleTime);
03831     else
03832         rubber = 0;
03833 
03834     // let rubber decay
03835     if ( sg_rubberCycleMalusTime > 0 )
03836         rubberMalus /= (1+ts/sg_rubberCycleMalusTime);
03837     else
03838         rubberMalus = 0;
03839 
03840 
03841     // clamp rubber ( mostly for client side HUD display )
03842     if ( rubber > rubber_granted )
03843         rubber = rubber_granted;
03844 
03845 
03846     lastTime=currentTime;
03847 
03848     // give the AI a chance to evade just in time
03849     if (emergency)
03850     {
03851         RightBeforeDeath(numTries);
03852     }
03853 
03854 #ifdef DEBUGOUTPUT
03855     if ( sg_cycleDebugPrintLevel > 1 )
03856         con << Player()->GetName() << " moved " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
03857 #endif
03858 
03859     /*
03860     // debug output for sensitive rubber/time diagrams
03861     static REAL lastTimePrinted = 0;
03862     if ( currentTime > lastTimePrinted && Player() )
03863     {
03864         lastTimePrinted = currentTime;
03865         std::ofstream f( Player()->GetUserName() + "_rubber", std::ios::app );
03866         f << currentTime << " " << rubber << "\n";
03867     }
03868     */
03869 
03870     // apply acceleration
03871     if ( !sg_verletIntegration.Supported() )
03872         this->ApplyAcceleration( ts );
03873 
03874     tASSERT(finite(distance));
03875 
03876     tASSERT( rubber >= 0 );
03877 
03878     // call base timestep
03879     return eNetGameObject::Timestep(currentTime);
03880 }
03881 
03882 // *******************************************************************************************
03883 // *
03884 // *    MyInitAfterCreation
03885 // *
03886 // *******************************************************************************************
03889 // *******************************************************************************************
03890 
03891 void gCycleMovement::MyInitAfterCreation( void )
03892 {
03893 #ifdef DEBUG
03894     // con << "creating cycle.\n";
03895 #endif
03896     brakingReservoir = 1.0f;
03897 
03898     braking = false;
03899 
03900     acceleration = 0;
03901 
03902     refreshSpaceAhead_ = true;
03903     maxSpaceMaxCast_ = 0.0;
03904     maxSpaceHit_ = NULL;
03905 
03906     dir=dirDrive;
03907     lastDirDrive=dirDrive;
03908     lastTurnPos_=pos;
03909 
03910     distance=0;
03911     // wallContDistance = 5;
03912     rubber=0.0f;
03913     rubberMalus=0.0f;
03914     rubberSpeedFactor=1.0f;
03915 
03916     gap_[0] = gap_[1] = 1E+30;
03917     keepLookingForGap_[0] = keepLookingForGap_[1] = true;
03918 
03919     alive_ = 1;
03920 
03921     z=.75;
03922 
03923     turns=1;
03924 
03925     pendingTurns.clear();
03926     lastTurnTimeRight_ = lastTurnTimeLeft_=lastTime-10;
03927 
03928     lastTimeAlive_ = lastTime;
03929 
03930     if (!finite(verletSpeed_)){
03931         st_Breakpoint();
03932         verletSpeed_ = 1;
03933     }
03934 
03935     if (verletSpeed_ < .1)
03936         verletSpeed_=.1;
03937 
03938 #ifdef DEBUGOUTPUT
03939     if ( sg_cycleDebugPrintLevel > 0 )
03940         con << Player()->GetName() << " created " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
03941 #endif
03942 }
03943 
03944 // *******************************************************************************************
03945 // *
03946 // *    Init_gCycleCore
03947 // *
03948 // *******************************************************************************************
03951 // *******************************************************************************************
03952 
03953 //void gCycleMovement::Init_gCycleCore( void )
03954 //{
03955 //    assert(0); // implement me
03956 //}
03957 
03958 // *******************************************************************************************
03959 // *
03960 // *    Finit_gCycleCore
03961 // *
03962 // *******************************************************************************************
03965 // *******************************************************************************************
03966 
03967 //void gCycleMovement::Finit_gCycleCore( void )
03968 //{
03969 //    assert(0); // implement me
03970 //}
03971 
03972 // *******************************************************************************************
03973 // *
03974 // *    gCycleMovement
03975 // *
03976 // *******************************************************************************************
03979 // *******************************************************************************************
03980 
03981 //gCycleMovement::gCycleMovement( void )
03982 //{
03983 //    assert(0); // implement me
03984 //}
03985 
03986 // *******************************************************************************************
03987 // *
03988 // *    gCycleMovement
03989 // *
03990 // *******************************************************************************************
03994 // *******************************************************************************************
03995 
03996 //gCycleMovement::gCycleMovement( gCycleMovement const & other )
03997 //{
03998 //    assert(0); // implement me
03999 //}
04000 
04001 // *******************************************************************************************
04002 // *
04003 // *    operator =
04004 // *
04005 // *******************************************************************************************
04010 // *******************************************************************************************
04011 
04012 //gCycleMovement & gCycleMovement::operator =( gCycleMovement const & other )
04013 //{
04014 //    assert(0); // implement me
04015 //    return gCycleMovement();
04016 //}
04017 
04018 // *******************************************************************************************
04019 // *
04020 // *    CopyFrom
04021 // *
04022 // *******************************************************************************************
04026 // *******************************************************************************************
04027 
04028 //void gCycleMovement::CopyFrom( const gCycleMovement & other )
04029 //{
04030 //   assert(0); // implement me
04031 //}
04032 
04033 // *******************************************************************************************
04034 // *
04035 // *    DoGetDistanceSinceLastTurn
04036 // *
04037 // *******************************************************************************************
04041 // *******************************************************************************************
04042 
04043 REAL gCycleMovement::DoGetDistanceSinceLastTurn( void ) const
04044 {
04045     return eCoord::F( dirDrive, pos - lastTurnPos_ )/dirDrive.NormSquared();
04046 }
04047 
04048 // *******************************************************************************************
04049 // *
04050 // *    RubberMalusActive
04051 // *
04052 // *******************************************************************************************
04056 // *******************************************************************************************
04057 
04058 bool gCycleMovement::RubberMalusActive( void )
04059 {
04060     return sg_rubberCycleMalusTurn > 0;
04061 }
04062 
04063 // *******************************************************************************************
04064 // *
04065 // *    MoveSafely
04066 // *
04067 // *******************************************************************************************
04073 // *******************************************************************************************
04074 
04075 void gCycleMovement::MoveSafely( const eCoord & dest, REAL startTime, REAL endTime )
04076 {
04077     static bool recursing = false;
04078     if ( !recursing )
04079     {
04080         recursing = true;
04081         try
04082         {
04083             // try a regular move
04084             Move( dest, startTime, endTime );
04085         }
04086         catch( eDeath & death )
04087         {
04088             // and play dead if that doesn't work right
04089             short lastAlive = alive_;
04090             alive_ = 0;
04091             Move( dest, startTime, endTime );
04092             alive_ = lastAlive;
04093         }
04094     }
04095     else
04096     {
04097         // play dead if another safe move is already in process. Sometimes,
04098         // crossing a wall of a live cycle causes that cycle to be moved with
04099         // this function, and "killing" it temporarily avoids an endless
04100         // recursion in that case.
04101         short lastAlive = alive_;
04102         alive_ = 0;
04103         Move( dest, startTime, endTime );
04104         alive_ = lastAlive;
04105     }
04106 }
04107 
04108 REAL GetTurnSpeedFactor(void) {
04109     return sg_cycleTurnSpeedFactor;
04110 }
04111 
04112 // *******************************************************************************
04113 // *
04114 // *    NextInterestingTime
04115 // *
04116 // *******************************************************************************
04120 // *******************************************************************************
04121 
04122 REAL gCycleMovement::NextInterestingTime( void ) const
04123 {
04124     // default to the last time
04125     REAL ret = LastTime();
04126 
04127     // look for a later destination
04128     gDestination * run = currentDestination;
04129     while ( run )
04130     {
04131         REAL time = run->GetGameTime();
04132         if ( time > ret )
04133             ret = time;
04134         run = run->next;
04135     }
04136 
04137     return ret;
04138 }
04139 
04140 void gCycleMovement::AddZoneAcceleration( REAL zoneAcceleration )
04141 {
04142     totalZoneAcceleration += zoneAcceleration;
04143 }

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