1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2004  Armagetron Advanced Team (http://sourceforge.net/projects/armagetronad/)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "rSDL.h"
29 
30 // declaration
31 #ifndef		ARMAGETRONAD_SRC_TRON_GCYCLEMOVEMENT_H_INCLUDED
32 #include	"gCycleMovement.h"
33 #endif
34 
35 #include "tMath.h"
36 
37 #include "nConfig.h"
38 
39 #include "ePlayer.h"
40 #include "eDebugLine.h"
41 #include "eGrid.h"
42 #include "eLagCompensation.h"
43 #include "eTeam.h"
44 
45 #include "eTimer.h"
46 
47 #include "gWall.h"
48 #include "gSensor.h"
49 #include "gAIBase.h"
50 
51 #include "tRecorder.h"
52 
53 // #define DEBUG_RUBBER
54 
55 #ifdef DEBUG_RUBBER
56 #include <fstream>
57 #endif
58 
59 #undef 	INLINE_DEF
60 #define INLINE_DEF
61 
62 #ifndef DEDICATED
63 #define MAXRUBBER 1
64 #else
65 #define MAXRUBBER 3
66 #endif
67 
68 #ifdef DEBUG
69 #define DEBUGOUTPUT
70 #endif
71 
72 #ifdef DEBUGOUTPUT
73 #include "tSysTime.h"
74 static int sg_cycleDebugPrintLevel = 0;
75 #endif
76 
77 // get rubber values in effect
78 void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness );
79 
80 //  *****************************************************************
81 
sg_ArchiveCoord(eCoord & coord,int level)82 static void sg_ArchiveCoord( eCoord & coord, int level )
83 {
84     static char const * section = "_COORD";
85     tRecorderSync< eCoord >::Archive( section, level, coord );
86 }
87 
sg_ArchiveReal(REAL & real,int level)88 static void sg_ArchiveReal( REAL & real, int level )
89 {
90     static char const * section = "_REAL";
91     tRecorderSync< REAL >::Archive( section, level, real );
92 }
93 
94 //  *****************************************************************
95 // version feature indicating that proper verlet integration should be used
96 static nVersionFeature sg_verletIntegration( 7 );
97 
98 // strength of brake
99 REAL sg_brakeCycle=30;
100 static nSettingItem<REAL> c_ab("CYCLE_BRAKE",
101                                sg_brakeCycle);
102 
103 REAL sg_cycleBrakeRefill  = 0.0;
104 REAL sg_cycleBrakeDeplete = 0.0;
105 
106 // it should look this way, but a VersionFeature is used to control the application of these settings,
107 // so the nonwatched itmes suffice.
108 static nSettingItemWatched<REAL> sg_cycleBrakeRefillConf("CYCLE_BRAKE_REFILL",sg_cycleBrakeRefill, nConfItemVersionWatcher::Group_Annoying, 2 );
109 static nSettingItemWatched<REAL> sg_cycleBrakeDepleteConf("CYCLE_BRAKE_DEPLETE",sg_cycleBrakeDeplete, nConfItemVersionWatcher::Group_Annoying, 2 );
110 
111 // static nSettingItem<REAL> sg_cycleBrakeRefillConf("CYCLE_BRAKE_REFILL",sg_cycleBrakeRefill );
112 // static nSettingItem<REAL> sg_cycleBrakeDepleteConf("CYCLE_BRAKE_DEPLETE",sg_cycleBrakeDeplete );
113 
114 // cycle width: it won't fit into tunnels that are smaller than this
115 REAL sg_cycleWidth = 0;
116 static nSettingItemWatched<REAL> c_cw("CYCLE_WIDTH",
117                                       sg_cycleWidth, nConfItemVersionWatcher::Group_Bumpy, 14 );
118 
119 REAL sg_cycleWidthSide = 0;
120 static nSettingItemWatched<REAL> c_cws("CYCLE_WIDTH_SIDE",
121                                        sg_cycleWidthSide, nConfItemVersionWatcher::Group_Bumpy, 14 );
122 // calculate the gridning distance sparks should start flying at
sg_GetSparksDistance()123 REAL sg_GetSparksDistance()
124 {
125     if ( sg_cycleWidth < 2 * sg_cycleWidthSide )
126         return sg_cycleWidth;
127     else if ( sg_cycleWidthSide > 0 )
128         return sg_cycleWidthSide * 2;
129     else
130         return .25; // return 0.2.8.2 default
131 }
132 
133 // amout of rubber you use per meter when you squeeze inside a too tight tunnel
134 // when just barely squeezed
135 REAL sg_cycleWidthRubberMin = 1;
136 static nSettingItemWatched<REAL> c_cwrmax("CYCLE_WIDTH_RUBBER_MIN",
137         sg_cycleWidthRubberMin, nConfItemVersionWatcher::Group_Bumpy, 14 );
138 // when squeezed to a point
139 REAL sg_cycleWidthRubberMax = 1;
140 static nSettingItemWatched<REAL> c_cwrmin("CYCLE_WIDTH_RUBBER_MAX",
141         sg_cycleWidthRubberMax, nConfItemVersionWatcher::Group_Bumpy, 14 );
142 
143 // base speed of cycle im m/s
144 static REAL sg_speedCycle=10;
145 static nSettingItem<REAL> c_s("CYCLE_SPEED",sg_speedCycle);
146 
147 // minimal speed
148 static REAL sg_speedCycleMin=.25;
149 static nSettingItemWatched<REAL> c_smin("CYCLE_SPEED_MIN",
150                                         sg_speedCycleMin,
151                                         nConfItemVersionWatcher::Group_Bumpy,
152                                         9);
153 
154 // maximal speed
155 static REAL sg_speedCycleMax=0;
156 static nSettingItemWatched<REAL> c_smax("CYCLE_SPEED_MAX",
157                                         sg_speedCycleMax,
158                                         nConfItemVersionWatcher::Group_Bumpy,
159                                         14);
160 
161 REAL sg_speedCycleDecayBelow = 5;
162 static nSettingItemWatched<REAL> c_sdb("CYCLE_SPEED_DECAY_BELOW",
163                                        sg_speedCycleDecayBelow,
164                                        nConfItemVersionWatcher::Group_Bumpy,
165                                        8);
166 
167 REAL sg_speedCycleDecayAbove = .1;
168 static nSettingItemWatched<REAL> c_sda("CYCLE_SPEED_DECAY_ABOVE",
169                                        sg_speedCycleDecayAbove,
170                                        nConfItemVersionWatcher::Group_Bumpy,
171                                        8);
172 
173 // start speed of cycle im m/s
174 static REAL sg_speedCycleStart=20;
175 static tSettingItem<REAL> c_st("CYCLE_START_SPEED",
176                                sg_speedCycleStart);
177 
178 // min time between turns
179 REAL sg_delayCycle = .1;
180 static nSettingItem<REAL> c_d("CYCLE_DELAY",
181                               sg_delayCycle);
182 //bonus for turns in the same direcion
183 REAL sg_delayCycleDoublebindBonus = 1.;
184 static nSettingItemWatched<REAL> c_d_d_b("CYCLE_DELAY_DOUBLEBIND_BONUS",
185         sg_delayCycleDoublebindBonus, nConfItemVersionWatcher::Group_Bumpy, 14 );
186 
187 // number of turns buffered exactly
188 int sg_cycleTurnMemory = 3;
189 static tSettingItem<int> c_tm("CYCLE_TURN_MEMORY",
190                               sg_cycleTurnMemory);
191 
192 REAL sg_delayCycleTimeBased = 1;
193 static nSettingItemWatched<REAL> c_dt("CYCLE_DELAY_TIMEBASED",
194                                       sg_delayCycleTimeBased,
195                                       nConfItemVersionWatcher::Group_Bumpy,
196                                       7);
197 
198 // extra factor to sg_delayCycle applied locally
199 #ifdef DEDICATED
200 REAL sg_delayCycleBonus=.95;
201 static tSettingItem<REAL> c_db("CYCLE_DELAY_BONUS",
202                                sg_delayCycleBonus);
203 #else
204 REAL sg_delayCycleBonus=1;
205 #endif
206 
207 REAL sg_cycleTurnSpeedFactor=.95;
208 static nSettingItemWatched<REAL> c_ctf("CYCLE_TURN_SPEED_FACTOR",
209                                        sg_cycleTurnSpeedFactor,
210                                        nConfItemVersionWatcher::Group_Bumpy,
211                                        7);
212 
213 // wall acceleration
214 static REAL sg_accelerationCycle=10;
215 static nSettingItem<REAL> c_a("CYCLE_ACCEL",
216                               sg_accelerationCycle);
217 
218 // acceleration multiplicators
219 REAL sg_accelerationCycleSelf = 1;
220 static nSettingItemWatched<REAL> c_aco("CYCLE_ACCEL_SELF",
221                                        sg_accelerationCycleSelf,
222                                        nConfItemVersionWatcher::Group_Bumpy,
223                                        8);
224 
225 REAL sg_accelerationCycleTeam = 1;
226 static nSettingItemWatched<REAL> c_act("CYCLE_ACCEL_TEAM",
227                                        sg_accelerationCycleTeam,
228                                        nConfItemVersionWatcher::Group_Bumpy,
229                                        14);
230 
231 REAL sg_accelerationCycleEnemy = 1;
232 static nSettingItemWatched<REAL> c_ace("CYCLE_ACCEL_ENEMY",
233                                        sg_accelerationCycleEnemy,
234                                        nConfItemVersionWatcher::Group_Bumpy,
235                                        14);
236 
237 REAL sg_accelerationCycleRim = 0;
238 static nSettingItemWatched<REAL> c_acr("CYCLE_ACCEL_RIM",
239                                        sg_accelerationCycleRim,
240                                        nConfItemVersionWatcher::Group_Bumpy,
241                                        8);
242 
243 REAL sg_accelerationCycleSlingshot = 1;
244 static nSettingItemWatched<REAL> c_acs("CYCLE_ACCEL_SLINGSHOT",
245                                        sg_accelerationCycleSlingshot,
246                                        nConfItemVersionWatcher::Group_Bumpy,
247                                        8);
248 
249 REAL sg_accelerationCycleTunnel = 1;
250 static nSettingItemWatched<REAL> c_acu("CYCLE_ACCEL_TUNNEL",
251                                        sg_accelerationCycleTunnel,
252                                        nConfItemVersionWatcher::Group_Bumpy,
253                                        14);
254 
255 // acceleration offset
256 static REAL sg_accelerationCycleOffs=2;
257 static nSettingItem<REAL> c_ao("CYCLE_ACCEL_OFFSET",
258                                sg_accelerationCycleOffs);
259 
260 
261 // when is a eWall near?
262 static REAL sg_nearCycle=6;
263 static nSettingItem<REAL> c_n("CYCLE_WALL_NEAR",
264                               sg_nearCycle);
265 
266 // boost settings, absolute speed increase applied when you break from a wall
267 REAL sg_boostCycleSelf = 0;
268 static nSettingItemWatched<REAL> c_bco("CYCLE_BOOST_SELF",
269                                        sg_boostCycleSelf,
270                                        nConfItemVersionWatcher::Group_Bumpy,
271                                        14);
272 
273 REAL sg_boostCycleTeam = 0;
274 static nSettingItemWatched<REAL> c_bct("CYCLE_BOOST_TEAM",
275                                        sg_boostCycleTeam,
276                                        nConfItemVersionWatcher::Group_Bumpy,
277                                        14);
278 
279 REAL sg_boostCycleEnemy = 0;
280 static nSettingItemWatched<REAL> c_bce("CYCLE_BOOST_ENEMY",
281                                        sg_boostCycleEnemy,
282                                        nConfItemVersionWatcher::Group_Bumpy,
283                                        14);
284 
285 REAL sg_boostCycleRim = 0;
286 static nSettingItemWatched<REAL> c_bcr("CYCLE_BOOST_RIM",
287                                        sg_boostCycleRim,
288                                        nConfItemVersionWatcher::Group_Bumpy,
289                                        14);
290 
291 // boostFactor settings, speed factor when you break from a wall
292 REAL sg_boostFactorCycleSelf = 1;
293 static nSettingItemWatched<REAL> c_bfco("CYCLE_BOOSTFACTOR_SELF",
294                                         sg_boostFactorCycleSelf,
295                                         nConfItemVersionWatcher::Group_Bumpy,
296                                         14);
297 
298 REAL sg_boostFactorCycleTeam = 1;
299 static nSettingItemWatched<REAL> c_bfct("CYCLE_BOOSTFACTOR_TEAM",
300                                         sg_boostFactorCycleTeam,
301                                         nConfItemVersionWatcher::Group_Bumpy,
302                                         14);
303 
304 REAL sg_boostFactorCycleEnemy = 1;
305 static nSettingItemWatched<REAL> c_bfce("CYCLE_BOOSTFACTOR_ENEMY",
306                                         sg_boostFactorCycleEnemy,
307                                         nConfItemVersionWatcher::Group_Bumpy,
308                                         14);
309 
310 REAL sg_boostFactorCycleRim = 1;
311 static nSettingItemWatched<REAL> c_bfcr("CYCLE_BOOSTFACTOR_RIM",
312                                         sg_boostFactorCycleRim,
313                                         nConfItemVersionWatcher::Group_Bumpy,
314                                         14);
315 
316 // tolerance for packet loss
317 static REAL sg_packetLossTolerance = 0;
318 static tSettingItem<REAL> conf_packetLossTolerance ("CYCLE_PACKETLOSS_TOLERANCE", sg_packetLossTolerance);
319 
320 // tolerance for packet misses: if an intermediate packet is missing
321 static REAL sg_packetMissTolerance = 3;
322 static tSettingItem<REAL> conf_packetMissTolerance ("CYCLE_PACKETMISS_TOLERANCE", sg_packetMissTolerance);
323 
324 // niceness when crashing a eWall
325 REAL sg_rubberCycle=MAXRUBBER;
326 static nSettingItem<REAL> c_r("CYCLE_RUBBER",
327                               sg_rubberCycle);
328 
329 REAL sg_rubberCycleTimeBased = 0;
330 static nSettingItemWatched<REAL> c_rtb("CYCLE_RUBBER_TIMEBASED",
331                                        sg_rubberCycleTimeBased,
332                                        nConfItemVersionWatcher::Group_Visual,
333                                        7);
334 
335 // allow legacy rubber code
336 bool sg_rubberCycleLegacy=true;
337 static nSettingItem<bool> c_rl("CYCLE_RUBBER_LEGACY",
338                                sg_rubberCycleLegacy);
339 
340 // niceness when crashing a eWall, influence of your ping
341 static REAL sg_rubberCyclePing=3;
342 static nSettingItem<REAL> c_rp("CYCLE_PING_RUBBER",
343                                sg_rubberCyclePing);
344 
345 // timescale rubber is restored on
346 REAL sg_rubberCycleTime=10;
347 static nSettingItemWatched<REAL> c_rt("CYCLE_RUBBER_TIME",
348                                       sg_rubberCycleTime,
349                                       nConfItemVersionWatcher::Group_Visual,
350                                       7);
351 
352 // max logarithmic approximation speed when rubber is in effect
353 static REAL sg_rubberCycleSpeed=40;
354 static nSettingItemWatched<REAL> c_rs("CYCLE_RUBBER_SPEED",
355                                       sg_rubberCycleSpeed,
356                                       nConfItemVersionWatcher::Group_Cheating,
357                                       4);
358 
359 #ifdef DEDICATED
360 #define MINDISTANCE_FACTOR 1
361 #else
362 #define MINDISTANCE_FACTOR 0
363 #endif
364 
365 // minimal distance to a wall rubber will allow
366 static REAL sg_rubberCycleMinDistance=.001 * MINDISTANCE_FACTOR;
367 static nSettingItemWatched<REAL> c_rmd("CYCLE_RUBBER_MINDISTANCE",
368                                        sg_rubberCycleMinDistance,
369                                        nConfItemVersionWatcher::Group_Annoying,
370                                        4);
371 
372 // the length of the wall times this value is added to the previous value
373 static REAL sg_rubberCycleMinDistanceRatio=.0001 * MINDISTANCE_FACTOR;
374 static nSettingItemWatched<REAL> c_rmdr("CYCLE_RUBBER_MINDISTANCE_RATIO",
375                                         sg_rubberCycleMinDistanceRatio,
376                                         nConfItemVersionWatcher::Group_Annoying,
377                                         4);
378 
379 // on an empty rubber reservoir, this value is added as well
380 static REAL sg_rubberCycleMinDistanceReservoir=0.005 * MINDISTANCE_FACTOR;
381 static nSettingItemWatched<REAL> c_rmdres("CYCLE_RUBBER_MINDISTANCE_RESERVOIR",
382         sg_rubberCycleMinDistanceReservoir,
383         nConfItemVersionWatcher::Group_Annoying,
384         7);
385 
386 // and on a not so well prepared grind (if your last turn was not too long ago), this value.
387 static REAL sg_rubberCycleMinDistanceUnprepared=0.005 * MINDISTANCE_FACTOR;
388 static nSettingItemWatched<REAL> c_rmdup("CYCLE_RUBBER_MINDISTANCE_UNPREPARED",
389         sg_rubberCycleMinDistanceUnprepared ,
390         nConfItemVersionWatcher::Group_Annoying,
391         7);
392 
393 // this is the distance the preparation lengt is compared with
394 static REAL sg_rubberCycleMinDistancePreparation=0.2;
395 static nSettingItemWatched<REAL> c_rmdp("CYCLE_RUBBER_MINDISTANCE_PREPARATION",
396                                         sg_rubberCycleMinDistancePreparation,
397                                         nConfItemVersionWatcher::Group_Annoying,
398                                         7);
399 
400 // when connecting to older peers that may be rippable, multiply the minimum distance by this number if the
401 // wall in front of you is the rim
402 static REAL sg_rubberCycleMinDistanceLegacy=1;
403 static nSettingItem<REAL> c_rmdl("CYCLE_RUBBER_MINDISTANCE_LEGACY",
404                                  sg_rubberCycleMinDistanceLegacy);
405 
406 // this feature tracks whether the rip bug was fixed
407 static nVersionFeature sg_nonRippable(4);
408 
409 // when adjusting to a wall, allow to get closer at least by this amount
410 static REAL sg_rubberCycleMinAdjust=.05;
411 static nSettingItemWatched<REAL> c_rma("CYCLE_RUBBER_MINADJUST",
412                                        sg_rubberCycleMinAdjust,
413                                        nConfItemVersionWatcher::Group_Annoying,
414                                        4);
415 
416 // during this fraction of the cycle delay time, rubber effectiveness will be reduced...
417 static REAL sg_rubberCycleDelay=0;
418 static nSettingItemWatched<REAL> c_rcd("CYCLE_RUBBER_DELAY",
419                                        sg_rubberCycleDelay,
420                                        nConfItemVersionWatcher::Group_Visual,
421                                        6);
422 
423 // by this factor ( meaning that rubber usage goes up by the inverse )
424 static REAL sg_rubberCycleDelayBonus=.5;
425 static nSettingItemWatched<REAL> c_rcdb("CYCLE_RUBBER_DELAY_BONUS",
426                                         sg_rubberCycleDelayBonus,
427                                         nConfItemVersionWatcher::Group_Visual,
428                                         6);
429 
430 // rubber usage gets increased by this amout after each turn...
431 static REAL sg_rubberCycleMalusTurn=0;
432 static nSettingItemWatched<REAL> c_rctm("CYCLE_RUBBER_MALUS_TURN",
433                                         sg_rubberCycleMalusTurn,
434                                         nConfItemVersionWatcher::Group_Visual,
435                                         6);
436 
437 // but the effect wears off after about this many seconds
438 static REAL sg_rubberCycleMalusTime=5;
439 static nSettingItem<REAL> c_rcmd("CYCLE_RUBBER_MALUS_TIME",
440                                  sg_rubberCycleMalusTime);
441 
442 // time tolerance when interpreting client commands
443 static REAL sg_timeTolerance=.1;
444 static nSettingItemWatched<REAL> c_tt( "CYCLE_TIME_TOLERANCE",
445                                        sg_timeTolerance,
446                                        nConfItemVersionWatcher::Group_Visual,
447                                        6 );
448 
449 static int sg_cycleMaxRefCount = 30000;
450 static tSettingItem<int> conf_sgCycleMaxRefCount ("CYCLE_MAX_REFCOUNT", sg_cycleMaxRefCount );
451 
clamp(REAL & c,REAL min,REAL max)452 static inline bool clamp(REAL &c, REAL min, REAL max){
453     tASSERT(min <= max);
454 
455     if (!finite(c))
456     {
457         c = 0;
458         return true;
459     }
460 
461     if (c<min)
462     {
463         c = min;
464         return true;
465     }
466 
467     if (c>max)
468     {
469         c = max;
470         return true;
471     }
472 
473     return false;
474 }
475 
blocks(const gSensor & s,const gCycleMovement * c,int lr)476 static void blocks(const gSensor &s, const gCycleMovement *c, int lr)
477 {
478     if ( nCLIENT == sn_GetNetState() )
479         return;
480 
481     if (s.type == gSENSOR_RIM)
482         gAIPlayer::CycleBlocksRim(c, lr);
483     else if (s.type == gSENSOR_TEAMMATE || ( s.type == gSENSOR_ENEMY && s.ehit ) )
484     {
485         gPlayerWall *w = dynamic_cast<gPlayerWall*>(s.ehit->GetWall());
486         if (w)
487         {
488             // int turn     = c->Grid()->WindingNumber();
489             //	  int halfTurn = turn >> 1;
490 
491             // calculate the winding number.
492             int windingBefore = c->WindingNumber();  // we start driving in c's direction
493             // we need to make a sharp turn in the lr-direction
494             //	  windingBefore   += lr * halfTurn;
495 
496             // after the transfer, we need to drive in the direction of the other
497             // wall:
498             int windingAfter = w->WindingNumber();
499 
500             // if the other wall drives in the opposite direction, we
501             // need to turn around again:
502             //	  if (s.lr == lr)
503             // windingAfter -= lr * halfTurn;
504 
505             // make the winding difference a multiple of the winding number
506             /*
507               int compensation = ((windingAfter - windingBefore - halfTurn) % turn)
508               + halfTurn;
509               while (compensation < -halfTurn)
510               compensation += turn;
511             */
512 
513             // only if the two walls are parallel/antiparallel, there is true blocking.
514             if (((windingBefore - windingAfter) & 1) == 0)
515                 gAIPlayer::CycleBlocksWay(c, w->Cycle(),
516                                           lr, s.lr,
517                                           w->Pos(s.ehit->Ratio(s.before_hit)),
518                                           - windingAfter + windingBefore);
519         }
520     }
521 }
522 
523 // enemy influence settings
524 static REAL sg_enemyFriendTimePenalty = 2500.0f;  //!< penalty for teammate influence
525 // REAL sg_enemySelfTimePenalty = 5000.0f;    //!< penalty for self influence
526 static REAL sg_enemyDeadTimePenalty = 0.0f;       //!< penalty for influence from dead players
527 REAL sg_suicideTimeout = 10000.0f;         //!< influences older than this don't count as kill cause
528 static REAL sg_enemyCurrentTimeInfluence = 0.0f;  //!< blends in the current time into the relevant time
529 
530 static tSettingItem<REAL> sg_enemyFriendTimePenaltyConf( "ENEMY_TEAMMATE_PENALTY", sg_enemyFriendTimePenalty );
531 static tSettingItem<REAL> sg_enemyDeadTimePenaltyConf( "ENEMY_DEAD_PENALTY", sg_enemyDeadTimePenalty );
532 static tSettingItem<REAL> sg_suicideTimeoutConf( "ENEMY_SUICIDE_TIMEOUT", sg_suicideTimeout );
533 static tSettingItem<REAL> sg_enemyCurrentTimeInfluenceConf( "ENEMY_CURRENTTIME_INFLUENCE", sg_enemyCurrentTimeInfluence );
534 
535 // the last enemy possibly responsible for our death
GetEnemy() const536 const ePlayerNetID* gEnemyInfluence::GetEnemy() const
537 {
538     return lastEnemyInfluence.GetPointer();
539 }
540 
GetTime() const541 REAL gEnemyInfluence::GetTime() const
542 {
543     return lastTime;
544 }
545 
gEnemyInfluence()546 gEnemyInfluence::gEnemyInfluence()
547 {
548     lastTime = -sg_suicideTimeout;
549 }
550 
551 // add the result of the sensor scan to our data
AddSensor(const gSensor & sensor,REAL timePenalty,gCycleMovement * thisCycle)552 void gEnemyInfluence::AddSensor( const gSensor& sensor, REAL timePenalty, gCycleMovement * thisCycle )
553 {
554     // the client has no need for this, it does not execute AI code
555     if ( sn_GetNetState() == nCLIENT )
556         return;
557 
558     // check if the sensor hit an enemy wall
559     // if ( sensor.type != gSENSOR_ENEMY )
560     //    return;
561 
562     // get the wall
563     if ( !sensor.ehit )
564         return;
565 
566     eWall* wall = sensor.ehit->GetWall();
567     if ( !wall )
568         return;
569 
570     AddWall( wall, sensor.before_hit, timePenalty, thisCycle );
571 }
572 
573 // add the interaction with a wall to our data
AddWall(const eWall * wall,eCoord const & pos,REAL timePenalty,gCycleMovement * thisCycle)574 void gEnemyInfluence::AddWall( const eWall * wall, eCoord const & pos, REAL timePenalty, gCycleMovement * thisCycle )
575 {
576     // the client has no need for this, it does not execute AI code
577     if ( sn_GetNetState() == nCLIENT )
578         return;
579 
580     // see if it is a player wall
581     gPlayerWall const * playerWall = dynamic_cast<gPlayerWall const *>( wall );
582     if ( !playerWall )
583         return;
584 
585     // get the approximate time the wall was drawn
586     REAL alpha = .5f;
587     // try to get a more accurate value
588     if ( playerWall->Edge() )
589     {
590         // get the position of the collision point
591         alpha = playerWall->Edge()->Ratio( pos );
592     }
593     REAL timeBuilt = playerWall->Time( alpha );
594 
595     AddWall( playerWall, timeBuilt - timePenalty, thisCycle );
596 }
597 
598 // add the interaction with a wall to our data
AddWall(const gPlayerWall * wall,REAL timeBuilt,gCycleMovement * thisCycle)599 void gEnemyInfluence::AddWall( const gPlayerWall * wall, REAL timeBuilt, gCycleMovement * thisCycle )
600 {
601     // the client has no need for this, it does not execute AI code
602     if ( sn_GetNetState() == nCLIENT )
603         return;
604 
605     if ( !wall )
606         return;
607 
608     // get the cycle
609     gCycle *cycle = wall->Cycle();
610     if ( !cycle )
611         return;
612 
613     // don't count self influence
614     if ( thisCycle == cycle )
615         return;
616 
617     REAL time = timeBuilt;
618     if ( thisCycle )
619     {
620         REAL currentTime = thisCycle->LastTime();
621         time += ( currentTime - time ) * sg_enemyCurrentTimeInfluence;
622     }
623 
624     // get the player
625     ePlayerNetID* player = cycle->Player();
626     if ( !player )
627         return;
628 
629     // don't accept milkers.
630     if ( thisCycle && !ePlayerNetID::Enemies( thisCycle->Player(), player ) )
631     {
632         return;
633     }
634 
635     // if the player is not our enemy, add extra time penalty
636     if ( thisCycle->Player() && player->CurrentTeam() == thisCycle->Player()->CurrentTeam() )
637     {
638         // the time shall be at most the time of the last turn, it should count as suicide if
639         // I drive into your three mile long wall
640         if ( time > cycle->GetLastTurnTime() )
641             time = cycle->GetLastTurnTime();
642         time -= sg_enemyFriendTimePenalty;
643     }
644     const ePlayerNetID* pInfluence = this->lastEnemyInfluence.GetPointer();
645 
646     // calculate effective last time. Add malus if the player is dead.
647     REAL lastEffectiveTime = lastTime;
648     if ( !pInfluence  || !pInfluence->Object() ||  !pInfluence->Object()->Alive() )
649     {
650         lastEffectiveTime -= sg_enemyDeadTimePenalty;
651     }
652 
653     // same for the current influence
654     REAL effectiveTime = time;
655     if ( !cycle->Alive() )
656     {
657         effectiveTime -= sg_enemyDeadTimePenalty;
658     }
659 
660     // if the new influence is newer, take it.
661     if ( effectiveTime > lastEffectiveTime || !bool(lastEnemyInfluence) )
662     {
663         lastEnemyInfluence = player;
664         lastTime		   = time;
665     }
666 }
667 
668 // *******************************************************************************************
669 // *
670 // *	gCycleMovement
671 // *
672 // *******************************************************************************************
673 //!
674 //!
675 // *******************************************************************************************
676 
677 // gCycleMovement::gCycleMovement()
678 // {
679 //	this->Init_gCycleCore();
680 // }
681 
682 // *******************************************************************************************
683 // *
684 // *	gCycleMovement
685 // *
686 // *******************************************************************************************
687 //!
688 //!		@param	other	the source to copy from
689 //!
690 // *******************************************************************************************
691 
692 // gCycleMovement::gCycleMovement( gCycleMovement const & other )
693 // {
694 // 	this->Init_gCycleCore();
695 //	this->CopyFrom( other );
696 //}
697 
698 // *******************************************************************************************
699 // *
700 // *	operator =
701 // *
702 // *******************************************************************************************
703 //!
704 //!		@param	other	the source to copy from
705 //!		@return			a reference to this
706 //!
707 // *******************************************************************************************
708 
operator =(gCycleMovement const & other)709 gCycleMovement & gCycleMovement::operator= ( gCycleMovement const & other )
710 {
711     this->CopyFrom( other );
712     return *this;
713 }
714 
715 // *******************************************************************************************
716 // *
717 // *	init_gCycleCore
718 // *
719 // *******************************************************************************************
720 //!
721 //!
722 // *******************************************************************************************
723 
724 //void gCycleMovement::Init_gCycleCore()
725 //{
726 //    assert(0); // TODO: implement me
727 //}
728 
729 // *******************************************************************************************
730 // *
731 // *	finit_gCycleCore
732 // *
733 // *******************************************************************************************
734 //!
735 //!
736 // *******************************************************************************************
737 
738 //void gCycleMovement::Finit_gCycleCore()
739 //{
740 //    assert(0); // TODO: implement me
741 //}
742 
743 // ----------------------------------------------------------------------------------------------------------
744 
745 static float sg_speedMultiplier = 1.0f;
746 static nSettingItem<float> conf_mult ("REAL_CYCLE_SPEED_FACTOR", sg_speedMultiplier);
747 
748 // *******************************************************************************************
749 // *
750 // *	RubberSpeed
751 // *
752 // *******************************************************************************************
753 //!
754 //!		@return 	the rubber speed (decay rate of the distance to the wall in front)
755 //!
756 // *******************************************************************************************
757 
RubberSpeed()758 float gCycleMovement::RubberSpeed()
759 {
760     return sg_rubberCycleSpeed;
761 }
762 
763 // *******************************************************************************************
764 // *
765 // *	SpeedMultiplier
766 // *
767 // *******************************************************************************************
768 //!
769 //!		@return 	the number all speed settings get multiplied by
770 //!
771 // *******************************************************************************************
772 
SpeedMultiplier(void)773 float gCycleMovement::SpeedMultiplier( void )
774 {
775     return sg_speedMultiplier;
776 }
777 
778 // *******************************************************************************************
779 // *
780 // *	SetSpeedMultiplier
781 // *
782 // *******************************************************************************************
783 //!
784 //!		@param	mul	the number all speed settings get multiplied by
785 //!
786 // *******************************************************************************************
787 
SetSpeedMultiplier(REAL mul)788 void gCycleMovement::SetSpeedMultiplier( REAL mul )
789 {
790     conf_mult.Set( mul );
791 }
792 
793 // for the given maximal acceleration, calculate the top speed
sg_MaxSpeed(REAL maxAcceleration)794 static REAL sg_MaxSpeed( REAL maxAcceleration )
795 {
796     if ( sg_speedCycleDecayAbove > 0 )
797         return sg_speedCycle + maxAcceleration / sg_speedCycleDecayAbove;
798     else
799         return sg_speedCycle * 100;
800 }
801 
802 // *******************************************************************************************
803 // *
804 // *	MaximalSpeed
805 // *
806 // *******************************************************************************************
807 //!
808 //!		@return 	the maximal speed a cycle can reach
809 //!
810 // *******************************************************************************************
811 
MaximalSpeed(void)812 float gCycleMovement::MaximalSpeed( void )
813 {
814     // determine the maximal acceleration by walls
815     REAL maxWallAcceleration = 0;
816     REAL wallAcceleration = sg_accelerationCycleTeam * sg_accelerationCycle;
817     if ( wallAcceleration > maxWallAcceleration )
818         maxWallAcceleration = wallAcceleration;
819     wallAcceleration = sg_accelerationCycleEnemy * sg_accelerationCycle;
820     if ( wallAcceleration > maxWallAcceleration )
821         maxWallAcceleration = wallAcceleration;
822     wallAcceleration = sg_accelerationCycleRim * sg_accelerationCycle;
823     if ( wallAcceleration > maxWallAcceleration )
824         maxWallAcceleration = wallAcceleration;
825 
826     // self acceleration is tricky: slingshot countermeasures have to be taken into account
827     REAL wallAccelerationSelf = sg_accelerationCycleSelf * sg_accelerationCycle;
828 
829     {
830         // different combinations are now possible to get a maximum. It could be a single wall:
831         REAL wallAccelerationSingle = maxWallAcceleration;
832         if ( wallAccelerationSingle < wallAccelerationSelf )
833             wallAccelerationSingle = wallAccelerationSelf;
834 
835         // it could be a slingshot, one arbitrary wall and one own wall:
836         REAL wallAccelerationSlingshot = ( wallAccelerationSingle + wallAccelerationSelf ) * sg_accelerationCycleSlingshot;
837 
838         // or a tunnel, two foreign walls:
839         REAL wallAccelerationTunnel = ( maxWallAcceleration ) * sg_accelerationCycleTunnel;
840 
841 
842         // take the maximum
843         if ( maxWallAcceleration < wallAccelerationSlingshot )
844             maxWallAcceleration = wallAccelerationSlingshot;
845         if ( maxWallAcceleration < wallAccelerationTunnel )
846             maxWallAcceleration = wallAccelerationTunnel;
847     }
848 
849     // use wall accel formula to take wall distance into account
850     maxWallAcceleration *= ( 1/sg_accelerationCycleOffs - 1/(sg_accelerationCycleOffs+sg_nearCycle ) );
851 
852     // maximal sustainable speed from that
853     REAL maxSpeed = sg_MaxSpeed( maxWallAcceleration );
854 
855     // what if the brake is a booster?
856     if ( sg_brakeCycle < 0 )
857     {
858         // what if it can be applied infinitely?
859         REAL maxSpeedBoost = sg_MaxSpeed( maxWallAcceleration - sg_brakeCycle );
860 
861         // but the boost can permanently only be applied at this efficiency
862         REAL efficiency = 1;
863         if ( sg_cycleBrakeRefill + sg_cycleBrakeDeplete > 0 )
864             efficiency = sg_cycleBrakeRefill/(sg_cycleBrakeRefill + sg_cycleBrakeDeplete);
865 
866         // maximal permanent speed
867         REAL maxSpeedPermanent = sg_MaxSpeed( maxWallAcceleration - sg_brakeCycle*efficiency );
868 
869         // if the boost is limited, don't let the result be larger than what you can achieve by
870         // accelerating from the max speed attainable from walls
871         if ( sg_cycleBrakeDeplete > 0 )
872         {
873             REAL boostTime = 1/sg_cycleBrakeDeplete;
874             REAL maxSpeedBoost2 = maxSpeedPermanent - boostTime * sg_brakeCycle;
875             if ( maxSpeedBoost2 < maxSpeedBoost )
876                 maxSpeedBoost = maxSpeedBoost2;
877         }
878 
879         // take over the result
880         maxSpeed = maxSpeedBoost;
881     }
882 
883     // start speed
884     if ( sg_speedCycleStart > maxSpeed )
885         maxSpeed = sg_speedCycleStart;
886 
887     // apply multiplier
888     return sg_speedMultiplier * maxSpeed;
889 }
890 
891 // ----------------------------------------------------------------------------------------------------------
892 
893 // *******************************************************************************************
894 // *
895 // *	WindingNumber
896 // *
897 // *******************************************************************************************
898 //!
899 //!		@return		number of right turns taken minus the number of left turns
900 //!
901 // *******************************************************************************************
902 
WindingNumber(void) const903 int gCycleMovement::WindingNumber( void ) const
904 {
905     return windingNumber_;
906 }
907 
908 // *******************************************************************************************
909 // *
910 // *	SetWindingNumberWrapped
911 // *
912 // *******************************************************************************************
913 //!
914 //!		@param newWindingNumberWrapped		new wrapped winding number taken from the current driving direction
915 //!
916 // *******************************************************************************************
917 
SetWindingNumberWrapped(int newWindingNumberWrapped)918 void gCycleMovement::SetWindingNumberWrapped ( int newWindingNumberWrapped )
919 {
920     // calculate the difference in the wrapped winding number
921     int difference = newWindingNumberWrapped - windingNumberWrapped_;
922 
923     // wrap it into the interval [-WN/2,WN/2]
924     if (2 * difference <= -Grid()->WindingNumber())
925         difference += Grid()->WindingNumber();
926     if (2 * difference >= Grid()->WindingNumber())
927         difference -= Grid()->WindingNumber();
928 
929     // commit changes
930     windingNumberWrapped_ = newWindingNumberWrapped;
931     windingNumber_ += difference;
932 }
933 
934 // *******************************************************************************************
935 // *
936 // *	Direction
937 // *
938 // *******************************************************************************************
939 //!
940 //!		@return	the currend driving direction
941 //!
942 // *******************************************************************************************
943 
Direction(void) const944 eCoord gCycleMovement::Direction( void ) const
945 {
946     return dirDrive;
947 }
948 
949 // *******************************************************************************************
950 // *
951 // *	LastDirection
952 // *
953 // *******************************************************************************************
954 //!
955 //!		@return	the last driving direction
956 //!
957 // *******************************************************************************************
958 
LastDirection(void) const959 eCoord gCycleMovement::LastDirection( void ) const
960 {
961     return lastDirDrive;
962 }
963 
964 // *******************************************************************************************
965 // *
966 // *	Speed
967 // *
968 // *******************************************************************************************
969 //!
970 //!		@return	the current speed
971 //!
972 // *******************************************************************************************
973 
Speed(void) const974 REAL gCycleMovement::Speed( void ) const
975 {
976     REAL ret = verletSpeed_ + .5f * lastTimestep_ * acceleration;
977     return ret > 0 ? ret : 0;
978 }
979 
980 // *******************************************************************************************
981 // *
982 // *	Alive
983 // *
984 // *******************************************************************************************
985 //!
986 //!		@return		true if the cycle is still alive
987 //!
988 // *******************************************************************************************
989 
Alive() const990 bool gCycleMovement::Alive() const
991 {
992     return alive_ > 0;
993 }
994 
995 // *******************************************************************************************
996 // *
997 // *	Vulnerable
998 // *
999 // *******************************************************************************************
1000 //!
1001 //!		@return		true if the cycle is vulnerable
1002 //!
1003 // *******************************************************************************************
1004 
Vulnerable() const1005 bool gCycleMovement::Vulnerable() const
1006 {
1007     return true;
1008 }
1009 
1010 // *******************************************************************************************
1011 // *
1012 // *	CanMakeTurn
1013 // *
1014 // *******************************************************************************************
1015 //!
1016 //!	@param  direction the direction of the planned turn
1017 //!		@return	true if a new turn is possible right now
1018 //!
1019 // *******************************************************************************************
1020 
CanMakeTurn(int direction) const1021 bool gCycleMovement::CanMakeTurn( int direction ) const
1022 {
1023     return pendingTurns.empty() && CanMakeTurn( lastTime, direction );
1024 }
1025 
1026 // *******************************************************************************************
1027 // *
1028 // *	CanMakeTurn
1029 // *
1030 // *******************************************************************************************
1031 //!
1032 //!     @param  time the time to check
1033 //!	@param  direction the direction of the planned turn
1034 //!		@return	true if a new turn is possible at the given time
1035 //!
1036 // *******************************************************************************************
1037 
CanMakeTurn(REAL time,int direction) const1038 bool gCycleMovement::CanMakeTurn( REAL time, int direction ) const
1039 {
1040     return time >= GetNextTurn(direction);
1041 }
1042 
1043 // *******************************************************************************************
1044 // *
1045 // *	GetTurnDelay
1046 // *
1047 // *******************************************************************************************
1048 //!
1049 //!		@return	the delay between turns in seconds
1050 //!
1051 // *******************************************************************************************
1052 
GetTurnDelay(void) const1053 REAL gCycleMovement::GetTurnDelay( void ) const
1054 {
1055     // the basic delay as it was before 0.2.8 looked like this:
1056     REAL baseDelay   = sg_delayCycle*sg_delayCycleBonus/SpeedMultiplier();
1057 
1058     // we're modifying it by a power law to make speed turns easier or harder:
1059     REAL speedFactor = verletSpeed_/(sg_speedCycle*SpeedMultiplier());
1060 
1061     return baseDelay * pow( speedFactor, sg_delayCycleTimeBased-1 );
1062 }
1063 
1064 //!		@return	the delay between turns in seconds
GetTurnDelayDb(void) const1065 REAL gCycleMovement::GetTurnDelayDb( void ) const
1066 {
1067     // the basic delay as it was before 0.2.8 looked like this:
1068     REAL baseDelay   = sg_delayCycle*sg_delayCycleBonus/SpeedMultiplier()*sg_delayCycleDoublebindBonus;
1069 
1070     // we're modifying it by a power law to make speed turns easier or harder:
1071     REAL speedFactor = verletSpeed_/(sg_speedCycle*SpeedMultiplier());
1072 
1073     return baseDelay * pow( speedFactor, sg_delayCycleTimeBased-1 );
1074 }
1075 
1076 // *******************************************************************************************
1077 // *
1078 // *	GetNextTurn
1079 // *
1080 // *******************************************************************************************
1081 //!
1082 //!		@return	the time of the next possible turn
1083 //!
1084 // *******************************************************************************************
1085 
GetNextTurn(int direction) const1086 REAL gCycleMovement::GetNextTurn( int direction ) const
1087 {
1088     float right,left;
1089 #ifdef DEBUG_X
1090     std::cerr << "GetNextTurn: " << direction << std::endl;
1091 #endif
1092     if(direction == 1) {
1093         right = lastTurnTimeRight_ + GetTurnDelayDb();
1094         left = lastTurnTimeLeft_ + GetTurnDelay();
1095     } else {
1096         right = lastTurnTimeLeft_ + GetTurnDelayDb();
1097         left = lastTurnTimeRight_ + GetTurnDelay();
1098     }
1099 #ifdef DEBUG_X
1100     std::cerr << "GetTurnDelay: " << GetTurnDelay() << std::endl;
1101     std::cerr << "GetTurnDelayDb: " << GetTurnDelayDb() << std::endl;
1102     std::cerr << "lastTurnTimeRight_: " << lastTurnTimeRight_ << std::endl;
1103     std::cerr << "lastTurnTimeLeft_: " << lastTurnTimeLeft_ << std::endl;
1104     std::cerr << "right: " << right << std::endl;
1105     std::cerr << "left: " << left << std::endl;
1106 #endif
1107     return left > right ? left : right;
1108 }
1109 
1110 // *******************************************************************************************
1111 // *
1112 // *	AddDestination
1113 // *
1114 // *******************************************************************************************
1115 //!
1116 //!
1117 // *******************************************************************************************
1118 
AddDestination(void)1119 void gCycleMovement::AddDestination( void )
1120 {
1121     if ( sn_GetNetState() == nCLIENT )
1122     {
1123         gDestination* dest = tNEW(gDestination)(*this);
1124         //	dest->position = dest->position + dest->direction.Turn( 0, 10.0f );
1125         AddDestination( dest );
1126     }
1127 }
1128 
1129 // *******************************************************************************************
1130 // *
1131 // *	AddDestination
1132 // *
1133 // *******************************************************************************************
1134 //!
1135 //!		@param	dest		the destination to add
1136 //!
1137 // *******************************************************************************************
1138 
AddDestination(gDestination * dest)1139 void gCycleMovement::AddDestination( gDestination * dest )
1140 {
1141     //  con << "got new dest " << dest->position << "," << dest->direction
1142     // << "," << dest->distance << "\n";
1143 
1144     dest->InsertIntoList(&destinationList);
1145 
1146     // if the next destination already has been used, this destination will never be used
1147     if (dest->next && dest->next->hasBeenUsed){
1148         delete dest;
1149         return;
1150     }
1151 
1152     this->NotifyNewDestination( dest );
1153 
1154     // repeat insertion: position may have changed
1155     dest->InsertIntoList(&destinationList);
1156 }
1157 
1158 // *******************************************************************************************
1159 // *
1160 // *	GetCurrentDestination
1161 // *
1162 // *******************************************************************************************
1163 //!
1164 //!		@return		the destination this cycle is driving towards right now
1165 //!
1166 // *******************************************************************************************
1167 
GetCurrentDestination(void) const1168 gDestination * gCycleMovement::GetCurrentDestination( void ) const
1169 {
1170     return currentDestination;
1171 }
1172 
1173 // *******************************************************************************************
1174 // *
1175 // *	AdvanceDestination
1176 // *
1177 // *******************************************************************************************
1178 //!
1179 //!
1180 // *******************************************************************************************
1181 
AdvanceDestination(void)1182 void gCycleMovement::AdvanceDestination( void )
1183 {
1184     // not implemented
1185     tASSERT(0);
1186 }
1187 
1188 // *******************************************************************************************
1189 // *
1190 // *	NotifyNewDestination
1191 // *
1192 // *******************************************************************************************
1193 //!
1194 //!		@param	dest	   the new destination the cycle is notified about
1195 //!
1196 // *******************************************************************************************
1197 
NotifyNewDestination(gDestination * dest)1198 void gCycleMovement::NotifyNewDestination( gDestination * dest )
1199 {
1200     this->OnNotifyNewDestination( dest );
1201 }
1202 
1203 // *******************************************************************************************
1204 // *
1205 // *	DoIsDestinationUsed
1206 // *
1207 // *******************************************************************************************
1208 //!
1209 //!		@param	dest	the destination to test
1210 //!		@return			true if the destination is still in active use
1211 //!
1212 // *******************************************************************************************
1213 
DoIsDestinationUsed(const gDestination * dest) const1214 bool gCycleMovement::DoIsDestinationUsed( const gDestination * dest ) const
1215 {
1216     return ( destinationList == currentDestination || destinationList == lastDestination );
1217 }
1218 
1219 // *******************************************************************************************
1220 // *
1221 // *	DistanceToDestination
1222 // *
1223 // *******************************************************************************************
1224 //!
1225 //!		@param	dest	the destination to measure the distance to
1226 //!		@return			the distance to the destination
1227 //!
1228 // *******************************************************************************************
1229 
DistanceToDestination(gDestination & dest) const1230 REAL gCycleMovement::DistanceToDestination( gDestination & dest ) const
1231 {
1232     // read future direction from destination
1233     eCoord dirTurned = dest.direction;
1234 
1235     REAL divisor = ( dirDrive * dirTurned );
1236     if ( divisor < EPS && divisor > -EPS )
1237     {
1238         REAL F = eCoord::F( dirTurned, dirDrive );
1239         if ( F > 0 )
1240         {
1241             // destination direction and driving direction coincide; we have to
1242             // make up a new turned direction
1243 
1244             // no need to worry if brake status changed
1245             if ( ( braking != 0 ) != dest.braking )
1246             {
1247                 return eCoord::F( dest.position - pos, dirDrive )/dirDrive.NormSquared();
1248             }
1249 
1250             // we'd have to turn in this direction to reach the destination
1251             int side = (dest.position - pos) * dirDrive > 0 ? -1 : 1;
1252 
1253             // pretend to turn in that direction and fetch driving vector
1254             int w = windingNumberWrapped_;
1255             Grid()->Turn(w, side);
1256             dirTurned = Grid()->GetDirection( w );
1257 
1258             // recalculate divisor
1259             divisor = ( dirDrive * dirTurned );
1260             tASSERT( fabs( divisor ) > EPS );
1261         }
1262         else
1263         {
1264             // destination direction and driving direction are opposed. This must be a grave error,
1265             // so we'll make something up
1266             return eCoord::F( dest.position - pos, dirDrive )/dirDrive.NormSquared();
1267         }
1268     }
1269 
1270     // calculate when a turn would need to be made that aligns
1271     // this cycle with the destination
1272     return ( ( dest.position - pos ) * dirTurned ) / divisor;
1273 }
1274 
1275 // *******************************************************************************************
1276 // *
1277 // *	OnNotifyNewDestination
1278 // *
1279 // *******************************************************************************************
1280 //!
1281 //!		@param	dest	   the new destination
1282 //!
1283 // *******************************************************************************************
1284 
OnNotifyNewDestination(gDestination * dest)1285 void gCycleMovement::OnNotifyNewDestination( gDestination * dest )
1286 {
1287     // go back one destination if the new destination appears to be older than the current one
1288     if ((!currentDestination || currentDestination == dest->next ) &&
1289             sn_GetNetState()!=nSTANDALONE && ( Owner() != ::sn_myNetID || !destinationList ) )
1290     {
1291         currentDestination=dest;
1292         // con << "setting new cd\n";
1293     }
1294 }
1295 
1296 // *******************************************************************************************
1297 // *
1298 // *	OnDropTempWall
1299 // *
1300 // *******************************************************************************************
1301 //!
1302 //!		@param	wall	   the wall the other cycle is grinding
1303 //!		@param	pos	       the position of the grind
1304 //!     @param  dir        the direction the raycast triggering the gridding comes from
1305 //!
1306 // *******************************************************************************************
1307 
OnDropTempWall(gPlayerWall * wall,eCoord const & pos,eCoord const & dir)1308 void gCycleMovement::OnDropTempWall( gPlayerWall * wall, eCoord const & pos, eCoord const & dir )
1309 {
1310 }
1311 
1312 // *******************************************************************************************
1313 // *
1314 // *   GetDestinationBefore
1315 // *
1316 // *******************************************************************************************
1317 //!
1318 //!        @param  sync     the sync data received from the server
1319 //!        @param  first    the first candidate for the return value
1320 //!        @return          the destination entry that was last processed on the server
1321 //!
1322 // *******************************************************************************************
1323 
GetDestinationBefore(const SyncData & sync,gDestination * first)1324 gDestination * gCycleMovement::GetDestinationBefore( const SyncData & sync, gDestination * first )
1325 {
1326     // message IDs smaller than 16 don't exist
1327     if ( sync.messageID != 1 )
1328     {
1329         gDestination * ret = first;
1330 
1331         // deterimine last passed destination by the message ID
1332         while ( ret && ret->messageID != sync.messageID )
1333             ret = ret->next;
1334 
1335         // return match
1336         return ret;
1337     }
1338     else
1339     {
1340         // calculate the distance of the last turn of the sync
1341         REAL syncLastTurnDistance = sync.distance - ( sync.pos - sync.lastTurn ).Norm();
1342 
1343         // message ID not available; must use heuristics
1344         gDestination * run = first;         // destination iterator
1345         gDestination * bestMatch = NULL;    // the best message that fit the sync data and that lies before the sync
1346         REAL bestMatchDistance = 1E+20;     // the distance of the best message to the sync
1347         bool braking = false;               // braking causes trouble here. Activate extra checks if brakes are involved
1348         while ( run )
1349         {
1350             // calculate discrepancy of last turn distance of sync to the current message's distance
1351             REAL distanceBefore = syncLastTurnDistance - run->distance;
1352             REAL distanceAfter  = run->distance - sync.distance;
1353 
1354 
1355             // the allowed values for run->distance are inside the interval [ syncLastTurnDistance,sync.distance ]
1356             // distance is a metric that is positive outside of the interval and
1357             // negative inside it, with minimum in the center.
1358             REAL distance = distanceBefore < distanceAfter ? distanceBefore : distanceAfter;
1359 
1360             // activate brake trouble compensation
1361             if ( distance < 0 && ( run->braking || sync.braking ) )
1362             {
1363                 // void previous match
1364                 if ( !braking )
1365                     bestMatchDistance += 1;
1366                 braking = true;
1367             }
1368 
1369             // clamp distance to nonnegative values to give points inside the allowed interval
1370             // equal chances
1371             if ( distance < 0 )
1372                 distance = 0;
1373 
1374             // prefer destinations close to the end of the allowed interval if braking is involved, else destinations close to the beginning
1375             if ( braking )
1376             {
1377                 distance += fabs( distanceAfter + .01 * distanceBefore ) * .0001;
1378             }
1379             else
1380             {
1381                 distance += fabs( distanceBefore ) * .1;
1382             }
1383 
1384             // see if brake status and driving direction match; this is a must
1385             if ( eCoord::F( run->direction, sync.dir ) > .9*sync.dir.NormSquared() && run->braking == ( sync.braking != 0 ) )
1386             {
1387                 if ( !bestMatch || distance < bestMatchDistance )
1388                 {
1389                     bestMatch = run;
1390                     bestMatchDistance = distance;
1391                 }
1392             }
1393             run = run->next;
1394         }
1395 
1396         // con << bestMatchDistance << "\n";
1397 
1398         // return match
1399         return bestMatch;
1400     }
1401 }
1402 
1403 // *******************************************************************************************
1404 // *
1405 // *	EdgeIsDangerous
1406 // *
1407 // *******************************************************************************************
1408 //!
1409 //!		@param	wall	the wall to check for danger
1410 //!		@param	time	the time of the possible collision
1411 //!		@param	alpha	the local wall coordinate of the collision
1412 //!		@return			true if the wall is dangerous
1413 //!
1414 // *******************************************************************************************
1415 
EdgeIsDangerous(const eWall * wall,REAL time,REAL alpha) const1416 bool gCycleMovement::EdgeIsDangerous( const eWall * wall, REAL time, REAL alpha ) const
1417 {
1418     if (!wall)
1419         return false;
1420 
1421     const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(wall);
1422     if (w)
1423     {
1424         // have we entered a hole? Is the wall breaking down? Have we passed behind the end?
1425         if ( !w->IsDangerous( alpha, time ) )
1426             return false;
1427 
1428         // get time the wall was built
1429         // REAL builtTime = w->Time(alpha);
1430 
1431         //gCycleMovement *otherPlayer=w->CycleMovement();
1432         //if (otherPlayer==this && time < builtTime+2.5*GetTurnDelay() )
1433         //    return false;        // impossible to make such a small circle
1434         // no, not impossible, just moderately unlikely.
1435     }
1436 
1437     return true; // it is a real eWall.
1438 }
1439 
1440 // *******************************************************************************************
1441 // *
1442 // *	Turn
1443 // *
1444 // *******************************************************************************************
1445 //!
1446 //!		@param	dir	negative for left turns, positive for right turns
1447 //!		@return		true if the turning was successful
1448 //!
1449 // *******************************************************************************************
1450 
Turn(REAL dir)1451 bool gCycleMovement::Turn( REAL dir )
1452 {
1453     if (dir>0)
1454         return Turn(1);
1455     else if (dir<0)
1456         return Turn(-1);
1457     else
1458         return false;
1459 }
1460 
1461 // *******************************************************************************************
1462 // *
1463 // *	Turn
1464 // *
1465 // *******************************************************************************************
1466 //!
1467 //!		@param	dir	+1 for right turns, -1 for left turns
1468 //!		@return		true if the turning was successful
1469 //!
1470 // *******************************************************************************************
1471 
Turn(int dir)1472 bool gCycleMovement::Turn( int dir )
1473 {
1474     return DoTurn( dir );
1475 }
1476 
sg_DropTempWall(eCoord const & dir,gSensor const & sensor)1477 static void sg_DropTempWall( eCoord const & dir, gSensor const & sensor )
1478 {
1479     tASSERT( sensor.ehit );
1480 
1481     if (sn_GetNetState() != nCLIENT )
1482     {
1483         // if the wall is temporary, let its cycle drop it so it gets copied into the grid
1484         // and makes less phasing problems
1485         // don't drop parallel walls
1486         eCoord vec = sensor.ehit->Vec();
1487         if ( fabs( dir * vec ) < vec.Norm() * .5 )
1488             return;
1489 
1490         // get the wall
1491         eWall * ew = sensor.ehit->GetWall();
1492         tASSERT( ew );
1493         gPlayerWall* w = dynamic_cast< gPlayerWall * >( ew );
1494 
1495         // check if wall exists
1496         if ( w )
1497         {
1498             // get the cycle
1499             gCycleMovement* other = w->CycleMovement();
1500 
1501             // let it drop wall
1502             if ( other )
1503                 other->DropTempWall( w, sensor.before_hit, dir );
1504         }
1505     }
1506 }
1507 
1508 //! information about obstacle encountered by MaxSpaceAhead
1509 struct gMaxSpaceAheadHitInfo
1510 {
1511     eCoord            pos;                           //!< the location it was hit at
1512     REAL              offset;                        //!< offset from mindistance values, to be subtracted from wall distacne
1513 
1514     tJUST_CONTROLLED_PTR< eHalfEdge const > edge;    //!< the edge that was hit
1515     tJUST_CONTROLLED_PTR< gPlayerWall > playerWall;  //!< the player wall that was hit
1516     REAL wallAlpha;                                  //!< the wall alpha value of the hit
1517 
gMaxSpaceAheadHitInfogMaxSpaceAheadHitInfo1518     gMaxSpaceAheadHitInfo()
1519             : offset(0), wallAlpha(.5)
1520     {}
1521 };
1522 
1523 // clearer of that data
gMaxSpaceAheadHitInfoClearer(gMaxSpaceAheadHitInfo * & info)1524 gMaxSpaceAheadHitInfoClearer::gMaxSpaceAheadHitInfoClearer( gMaxSpaceAheadHitInfo * & info )
1525         : info_( info ){}
1526 
~gMaxSpaceAheadHitInfoClearer()1527 gMaxSpaceAheadHitInfoClearer::~gMaxSpaceAheadHitInfoClearer()
1528 {
1529     gMaxSpaceAheadHitInfo * info = info_;
1530     if ( info )
1531     {
1532         // we can't have the edges lingering around with possibly incomplete data
1533         info->edge = NULL;
1534         info->playerWall = NULL;
1535     }
1536 }
1537 
sg_Gap(gSensor const & front,gSensor const & side,eCoord const & dir,REAL norm,REAL def,REAL & tolerance)1538 static REAL sg_Gap( gSensor const & front, gSensor const & side, eCoord const & dir, REAL norm, REAL def, REAL & tolerance )
1539 {
1540     if ( side.ehit && side.ehit->Other() )
1541     {
1542         // determine the adistance of the two endpoints of the side edge
1543         // to the wall in front of us
1544         REAL gap1 = ( front.ehit->Vec()*( *side.ehit->Point() - *front.ehit->Point() ) )/norm;
1545         REAL gap2 = ( front.ehit->Vec()*( *side.ehit->Other()->Point() - *front.ehit->Point() ) )/norm;
1546 
1547         // correct for orientation using the current driving direction
1548         REAL sign = (dir * front.ehit->Vec())/norm;
1549         if ( sign != 0 )
1550         {
1551             sign = 1/sign;
1552             gap1 *= sign;
1553             gap2 *= sign;
1554         }
1555 
1556         // if both values are positive, update the cache with the smaller one
1557         tolerance = ( fabs(gap1) + fabs(gap2) ) * EPS * 10;
1558         REAL minGap = gap1 < gap2 ? gap1 : gap2;
1559 
1560         return minGap;
1561     }
1562     else
1563     {
1564         // return something close to the front wall
1565         tolerance = EPS * 10 * front.hit;
1566         return def;
1567     }
1568 }
1569 
1570 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.
1571 static REAL sg_rubberCycleMinDistanceGapSide = .5f;   // Gaps may be detected only if the cycle is able to drive into them in this time
1572 
1573 static nSettingItemWatched<REAL> c_rcmdg("CYCLE_RUBBER_MINDISTANCE_GAP",
1574         sg_rubberCycleMinDistanceGap, nConfItemVersionWatcher::Group_Bumpy, 14 );
1575 static nSettingItem<REAL> c_rcmdgs("CYCLE_RUBBER_MINDISTANCE_GAP_SIDE",
1576                                    sg_rubberCycleMinDistanceGapSide);
1577 
1578 // *******************************************************************************************
1579 // *
1580 // *	MaxSpaceAhead
1581 // *
1582 // *******************************************************************************************
1583 //!     determines how much a given cycle is allowed to drive ahead without getting too close to the next wall. Looks exactly lookAhead into the future.
1584 //!
1585 //!		@param	cycle the cycle to investigate
1586 //!     @param  lookAhead minimum distance to look ahead
1587 //!     @param  rubber expected to be used up until we hit the wall
1588 //!     @param  extra info storage space
1589 //!		@return		distance from the cycle to the next wall
1590 //!
1591 // *******************************************************************************************
1592 
1593 // *******************************************************************************************
1594 // *
1595 // *	GetMaxSpaceAhead
1596 // *
1597 // *******************************************************************************************
1598 //!     determines how much this cycle is allowed to drive ahead without getting too close to the next wall. Looks exactly lookAhead into the future.
1599 //!
1600 //!     @param      maxReport maximal distance to report
1601 //!		@return		distance from the cycle to the next wall
1602 //!
1603 // *******************************************************************************************
1604 
GetMaxSpaceAhead(REAL maxReport) const1605 REAL gCycleMovement::GetMaxSpaceAhead( REAL maxReport ) const
1606 {
1607     // refresh hit info if required
1608     if ( refreshSpaceAhead_ )
1609     {
1610         refreshSpaceAhead_ = false;
1611 
1612         // make sure the raycast is long enoigh
1613         REAL lookAhead = maxSpaceMaxCast_;
1614         if ( maxReport > lookAhead )
1615         {
1616             lookAhead = maxReport;
1617         }
1618 
1619         sg_ArchiveReal( lookAhead, 9 );
1620 
1621         // store data here for later
1622         gMaxSpaceAheadHitInfo info;
1623 
1624         // calculate the relevant minimal distance
1625         REAL mindistance = sg_rubberCycleMinDistance;
1626         {
1627             // get rubber values
1628             REAL rubber_granted, rubberEffectiveness;
1629             sg_RubberValues( player, verletSpeed_, rubber_granted, rubberEffectiveness );
1630 
1631             // add the reservoir dependant term
1632             if ( rubber_granted > 0 )
1633             {
1634                 // rubber usage speed
1635                 REAL rubberUsageSpeed = verletSpeed_ * ( 1 - rubberSpeedFactor ) / rubberEffectiveness;
1636                 // rubber used till end of frame
1637                 REAL rubberUsed = rubberUsageSpeed * lastTimestep_;
1638 
1639                 // fill ratio of rubber at the end of the next frame
1640                 REAL filling = ( GetRubber() + rubberUsed )/rubber_granted;
1641                 if ( filling > 1 )
1642                     filling = 1;
1643                 mindistance += sg_rubberCycleMinDistanceReservoir * (1-filling);
1644             }
1645 
1646             // add the bad preparation dependant term
1647             if ( sg_rubberCycleMinDistancePreparation > 0 )
1648             {
1649                 REAL badPreparation = sg_rubberCycleMinDistancePreparation/( sg_rubberCycleMinDistancePreparation + ( this->LastTime() - this->GetLastTurnTime() ) );
1650                 mindistance += sg_rubberCycleMinDistanceUnprepared * badPreparation;
1651             }
1652         }
1653         sg_ArchiveReal( mindistance, 9 );
1654 
1655         // since we are going to subtract the rubber min distance from the found hit, we'll still have to llok a bit further:
1656         lookAhead += mindistance * sg_rubberCycleMinDistanceLegacy * 2;
1657 
1658         // be a little nice and don't drive into the eWall if turning is allowed
1659         gSensor fr( const_cast< gCycleMovement* >(this), this->Position(), this->Direction() );
1660         {
1661             REAL speed = this->Speed();
1662             if ( speed > 0 )
1663                 fr.SetInverseSpeed( 1 / speed );
1664         }
1665         fr.detect( lookAhead );
1666 
1667         info.edge = fr.ehit;
1668         info.pos  = fr.before_hit;
1669 
1670         if ( fr.ehit )
1671         {
1672             {
1673                 // get the wall of the hit
1674                 eWall * w = info.edge->GetWall();
1675                 if ( !w && info.edge->Other() )
1676                 {
1677                     info.edge = info.edge->Other();
1678                     w = info.edge->GetWall();
1679                 }
1680 
1681                 gPlayerWall * wall = dynamic_cast< gPlayerWall * >( w );
1682                 if ( wall && wall->Cycle() )
1683                 {
1684                     // get the position of the hit and store everything
1685                     info.wallAlpha = info.edge->Ratio( info.pos );
1686                     info.playerWall = wall;
1687                 }
1688             }
1689 
1690 #ifdef DEBUG
1691             {
1692                 gSensor fr2( const_cast< gCycleMovement* >( this ), this->Position(), this->Direction() );
1693                 fr2.detect( lookAhead );
1694             }
1695 #endif
1696 
1697             REAL stopDistance = 0.1;
1698             if ( fr.ehit )
1699             {
1700                 REAL norm = fr.ehit->Vec().Norm();
1701                 stopDistance = mindistance + sg_rubberCycleMinDistanceRatio * norm;
1702 
1703                 ::sg_DropTempWall( this->Direction(), fr );
1704 
1705                 // enforce "open" play: every successive grind to a wall can get closer and closer.
1706 
1707                 REAL rubberCycleMinDistanceGapDistance = sg_rubberCycleMinDistanceGapSide * Speed();
1708 
1709 
1710                 if ( sg_rubberCycleMinDistanceGap > 0 )
1711                 {
1712                     // determine the width of the gap previous grinders left
1713                     for ( int dir = -1; dir < 2; dir += 2 )
1714                     {
1715                         // see if cached value is still good
1716                         REAL & gapCache = gap_[(dir+1)/2];
1717                         bool & keepLooking = keepLookingForGap_[(dir+1)/2];
1718 
1719                         if ( gapCache > fr.hit && keepLooking )
1720                         {
1721                             // determine next direction when turning into dir
1722                             int wn = windingNumberWrapped_;
1723                             Grid()->Turn(wn, dir);
1724                             eCoord dirCast = Grid()->GetDirection(wn);
1725 
1726                             bool gapFound = false;
1727                             for ( int back = -1; back <= 2; ++back )
1728                             {
1729                                 // determine next direction when turning into dir
1730                                 int wn2 = wn;
1731                                 Grid()->Turn(wn2, back);
1732                                 eCoord dirCast2 = Grid()->GetDirection(wn2);
1733 
1734                                 // send out a side sensor
1735                                 gSensor side( const_cast< gCycleMovement * >( this ),
1736                                               this->Position(),
1737                                               ( dirCast + dirCast2 ) * .5 );
1738 
1739                                 side.detect( rubberCycleMinDistanceGapDistance );
1740 
1741                                 // only allow non-hit default search for the ray that goes straight to the side
1742                                 if ( back != 0 && !side.ehit )
1743                                     continue;
1744 
1745                                 REAL tolerance;
1746                                 REAL minGap = sg_Gap( fr, side, dirDrive, norm, fr.hit * .5, tolerance );
1747 
1748                                 while ( minGap > tolerance )
1749                                 {
1750                                     // last test: see if there really is a gap after that wall ends
1751                                     gSensor side2( const_cast< gCycleMovement * >( this ),
1752                                                    this->Position() + this->Direction() * ( fr.hit - minGap * .9 ),
1753                                                    dirCast );
1754                                     side2.detect( rubberCycleMinDistanceGapDistance );
1755 
1756                                     // if this sensor did not hit or hit farther than the first sensor, the gap is real
1757                                     if ( fabs(side2.hit - side.hit) < tolerance )
1758                                     {
1759                                         // true gap not found yet
1760                                         REAL dumpTolerance;
1761                                         REAL lastMinGap = minGap;
1762                                         minGap = sg_Gap( fr, side2, dirDrive, norm, minGap * .5, dumpTolerance );
1763                                         // no improvement? give up.
1764                                         if ( minGap >= lastMinGap * .9 )
1765                                             break;
1766                                     }
1767                                     else
1768                                     {
1769                                         gapFound = true;
1770 
1771                                         // true gap found, is it smaller than the last one?
1772                                         if ( minGap < gapCache )
1773                                         {
1774                                             gapCache = minGap;
1775 
1776                                             // bail out of outer loop
1777                                             back = 100;
1778                                         }
1779 
1780                                         // bail out of inner loop
1781                                         break;
1782                                     }
1783                                 }
1784                             }
1785 
1786                             // no gap to see anywhere
1787                             if ( ! gapFound )
1788                             {
1789                                 // don't waste time looking from now on
1790                                 keepLooking = false;
1791 
1792                                 // if there was no gap detected so far, there is no gap.
1793                                 if ( gapCache > 5E+19 )
1794                                     gapCache = 0;
1795                             }
1796                         }
1797                     }
1798 
1799                     // fetch cache, ignoring zeroes
1800                     REAL gap = ( ( gap_[0] > 0 ? gap_[0] : 1E+30 ) < ( gap_[1] > 0 ? gap_[1] : 1E+30 ) ) ? gap_[0] : gap_[1];
1801                     if ( gap > 0 )
1802                     {
1803                         REAL minDistanceGap = gap * sg_rubberCycleMinDistanceGap;
1804                         if ( stopDistance > minDistanceGap )
1805                             stopDistance = minDistanceGap;
1806                     }
1807                 }
1808             }
1809             sg_ArchiveReal( stopDistance, 9 );
1810 
1811 
1812             // revert to almost old rubber logic if old clients are connected. This may cause rips, but we don't care.
1813             if ( sg_rubberCycleLegacy && !sg_nonRippable.Supported() && stopDistance > .001 )
1814                 stopDistance = .001;
1815 
1816             // if there is a rippable peer connected and the wall is the rim wall, add extra distance
1817             //if ( fr.type == gSENSOR_RIM && !sg_nonRippable.Supported() )
1818             //    stopDistance *= sg_rubberCycleMinDistanceLegacy;
1819 
1820             REAL space = fr.hit;
1821             sg_ArchiveReal( space, 9 );
1822 
1823             // see if we just did a turn
1824             REAL distSinceLastTurn = this->GetDistanceSinceLastTurn();
1825 
1826             // we want to get closer to the wall by at least some percentage
1827             REAL maxStop = ( distSinceLastTurn + space ) * ( 1 - sg_rubberCycleMinAdjust );
1828             if ( maxStop < stopDistance )
1829             {
1830                 stopDistance = maxStop;
1831             }
1832 
1833             sg_ArchiveReal( stopDistance, 9 );
1834 
1835             // add safety
1836             REAL safety = this->Position().Norm() * 2 * EPS;
1837 
1838             info.offset = stopDistance + safety;
1839 
1840             sg_ArchiveReal( space, 9 );
1841 
1842             // create new hit info
1843             if ( !maxSpaceHit_ )
1844                 maxSpaceHit_ = tNEW( gMaxSpaceAheadHitInfo );
1845 
1846             // store information
1847             *maxSpaceHit_ = info;
1848         }
1849         else
1850         {
1851             // delete information
1852             delete maxSpaceHit_;
1853             maxSpaceHit_ = NULL;
1854         }
1855     }
1856 
1857     // information up to date? Good, just take the distance to the collision point.
1858     REAL ret = 1E+30;
1859     if ( maxSpaceHit_ )
1860     {
1861         ret = eCoord::F( dirDrive, maxSpaceHit_->pos - pos ) - maxSpaceHit_->offset;
1862     }
1863 
1864     // clamp it and return.
1865     if ( ret > maxReport )
1866         ret = maxReport;
1867     return ret;
1868 }
1869 
1870 // *******************************************************************************************
1871 // *
1872 // *	MaxSpaceAhead
1873 // *
1874 // *******************************************************************************************
1875 //!     determines how much a given cycle is allowed to drive ahead without getting too close to the next wall. Looks at least lookAhead into the future, but never reports more than maxReport as result. The next timestep is assumed to be ts seconds long.
1876 
1877 //!		@param	cycle the cycle to investigate
1878 //!     @param  ts the next timestep
1879 //!     @param  lookAhead minimum distance to look ahead
1880 //!     @param  maxReport maximum return value
1881 //!		@return		distance from the cycle to the next wall
1882 //!
1883 // *******************************************************************************************
1884 
1885 /*
1886 float MaxSpaceAhead( const gCycleMovement* cycle, float ts, float lookAhead, float maxReport )
1887 {
1888     // lookahead should be at least the next expected timestep ( times two for safety )
1889     REAL step=cycle->Speed() * ts * 2;
1890     if ( lookAhead < step )
1891         lookAhead = step;
1892 
1893     // lookahead should be at least maxReport
1894     if ( lookAhead < maxReport )
1895         lookAhead = maxReport;
1896 
1897     REAL space = MaxSpaceAhead( cycle, lookAhead );
1898 
1899     if ( space < maxReport )
1900         return space;
1901     else
1902         return maxReport;
1903 }
1904 */
1905 
1906 // feature indicating that a client sends the time of turn commands
1907 static nVersionFeature sg_CommandTime( 4 );
1908 
1909 // server side feature of lag sliding correction code
1910 static nVersionFeature sg_AntiLag( 5 );
1911 
1912 // flag indicating whether to use the logic to prevent lag sliding only when every connected client can benefit from it
1913 static bool sg_fairAntiLagSliding=true;
1914 static nSettingItem<bool> c_fals("CYCLE_FAIR_ANTILAG",sg_fairAntiLagSliding);
1915 
1916 // check if we should apply anti-lag-sliding code
sg_UseAntiLagSliding(const eNetGameObject * obj)1917 static bool sg_UseAntiLagSliding( const eNetGameObject* obj )
1918 {
1919     tASSERT( obj );
1920 
1921     // cant do anyting for old clients that don't send the time of commands
1922     if ( !sg_CommandTime.Supported( obj->Owner() ) )
1923         return false;
1924 
1925     // likewise if the server does not support anti lag code and this is the client
1926     if ( sn_GetNetState() == nCLIENT && !sg_AntiLag.Supported( obj->Owner() ) )
1927         return false;
1928 
1929     // check whether the command time is sent by everyone or at least the object owner ( depending on fairness )
1930     if ( sg_fairAntiLagSliding )
1931     {
1932         return sg_CommandTime.Supported();
1933     }
1934     else
1935     {
1936         // we already checked whether the command time was sent
1937         return true;
1938     }
1939 }
1940 
1941 // while an object of this class exists, turn delay is ignored
1942 class gTurnDelayOverride
1943 {
1944 public:
gTurnDelayOverride(bool override)1945     explicit gTurnDelayOverride( bool override )
1946     {
1947         delay_ = sg_delayCycle;
1948         if ( override )
1949             sg_delayCycle = 0.0f;
1950     }
1951 
gTurnDelayOverride(REAL factor)1952     explicit gTurnDelayOverride( REAL factor )
1953     {
1954         delay_ = sg_delayCycle;
1955         sg_delayCycle *= factor;
1956     }
1957 
~gTurnDelayOverride()1958     ~gTurnDelayOverride()
1959     {
1960         sg_delayCycle = delay_;
1961     }
1962 private:
1963     REAL delay_;
1964 };
1965 
1966 static nVersionFeature sg_noRedundantBrakeCommands( 13 );
1967 
1968 // *******************************************************************************************
1969 // *
1970 // *	Timestep
1971 // *
1972 // *******************************************************************************************
1973 //!
1974 //!		@param	currentTime	the time to simulate up to
1975 //!		@return	true if the cycle is to be deleted
1976 //!
1977 // *******************************************************************************************
1978 
Timestep(REAL currentTime)1979 bool gCycleMovement::Timestep( REAL currentTime )
1980 {
1981 /*
1982     static int count = 1;
1983     ++count;
1984     if ( count == 0 )
1985     {
1986         int xc;
1987         xc = 0;
1988     }
1989 */
1990 
1991     // request regeneration of maximum space
1992     refreshSpaceAhead_ = true;
1993 
1994     // clear out dangerous info when we're done
1995     gMaxSpaceAheadHitInfoClearer hitInfoClearer( maxSpaceHit_ );
1996 
1997     // clamp stuff to finite values
1998     clamp( rubber, 0, sg_rubberCycle );
1999 
2000     // keep this cycle alive
2001     tJUST_CONTROLLED_PTR< gCycleMovement > keep( this->GetRefcount()>0 ? this : 0 );
2002 
2003     // don't make a fuss about negative timesteps
2004     if ( currentTime < lastTime )
2005         return TimestepCore( currentTime );
2006 
2007     // remove old destinations
2008     //REAL lag = 1;
2009     //if ( player )
2010     //    lag = player->ping;
2011     // REAL lagDistance = lag * Speed() * 10;
2012 
2013     while(destinationList && destinationList->hasBeenUsed && !IsDestinationUsed( destinationList ) )
2014         delete destinationList;
2015 
2016     // calculate timestep
2017     REAL dt = currentTime - lastTime;
2018 
2019     sg_ArchiveReal( dt, 9 );
2020 
2021     // if (currentTime > lastTime)
2022     {
2023         int timeout=10;
2024         bool forceTurn = false; // force turning at the next iteration
2025         bool overrideTurnDelay=false; // override the turn delay system, turn immediately
2026 
2027         // only simulate forward in time
2028         while (pendingTurns.empty() && currentDestination && timeout > 0 )
2029         {
2030             timeout --;
2031 
2032             // the distance to the next destination
2033             REAL dist_to_dest = DistanceToDestination( *currentDestination );
2034             sg_ArchiveReal( dist_to_dest, 9 );
2035 
2036             REAL ts=currentTime - lastTime;
2037             sg_ArchiveReal( ts, 9 );
2038 
2039             sg_ArchiveReal( verletSpeed_, 9 );
2040             sg_ArchiveReal( acceleration, 9 );
2041 
2042             // our speed
2043             REAL avgspeed=verletSpeed_;
2044             CalculateAcceleration();
2045             if (acceleration > 0)
2046                 avgspeed += acceleration * SpeedMultiplier() * ts * .5;
2047 
2048             // will rubber slow the cycle down to a crawl, so that the command time will be
2049             // a better indication when to turn than the position?
2050             bool rubberActive = false;
2051 
2052             // check if the path to the destination is clear for the next timesteps
2053             sg_ArchiveReal( rubberSpeedFactor, 9 );
2054             REAL distToWall=1E+30;
2055             if ( rubberSpeedFactor < .999 )
2056             {
2057                 // take rubber activity into account
2058                 rubberActive = true;
2059                 avgspeed *= rubberSpeedFactor;
2060                 if ( avgspeed < EPS )
2061                     avgspeed = EPS;
2062 
2063                 sg_ArchiveReal( avgspeed, 9 );
2064 
2065                 // don't drive into a wall, turn before getting too close
2066                 REAL lookahead = ts * avgspeed * 2;
2067 
2068                 REAL dist_to_wall = GetMaxSpaceAhead( lookahead );
2069 
2070                 if ( dist_to_dest > dist_to_wall )
2071                     dist_to_dest = dist_to_wall;
2072             }
2073 
2074             // static bool breakp = false;
2075 
2076             // the time left until the turn happened on the client
2077             // REAL timeLeft = currentDestination->GetGameTime() - lastTime;
2078 
2079             // the earliest and latest allowed time to turn
2080             REAL turnTime = currentDestination->GetGameTime();
2081             REAL earliestTurnTime = turnTime - sg_timeTolerance - 100;
2082             REAL latestTurnTime   = turnTime + sg_timeTolerance;
2083 
2084             // if rubber is active and anti lag sliding code should be enabled,
2085             // then allow no too early or late turns
2086             if ( sg_UseAntiLagSliding( this ) )
2087             {
2088                 if ( rubberActive )
2089                 {
2090                     // smoothly make the allowed time interval smaller with increased rubber use
2091                     earliestTurnTime = turnTime - sg_timeTolerance * rubberSpeedFactor;
2092                     latestTurnTime = turnTime + sg_timeTolerance * rubberSpeedFactor;
2093 
2094                     // clamp latest turn time so we don't drive into the wall
2095                     if ( verletSpeed_ > 0 )
2096                     {
2097                         REAL maxRubber, effectiveness;
2098                         sg_RubberValues( Player(), verletSpeed_, maxRubber, effectiveness );
2099 
2100                         // see when we'll die (be a little careful, the .9 is a fudge factor)
2101                         REAL rubberLeft = (maxRubber - rubber)*.9;
2102                         REAL stepLeft   = rubberLeft + distToWall;
2103                         REAL timeLeft   = stepLeft/verletSpeed_;
2104                         REAL deathTime  = lastTime + timeLeft;
2105 
2106                         // can't do a thing if the player wants to die
2107                         if ( deathTime < turnTime )
2108                             deathTime = turnTime;
2109 
2110                         // clamp latest possible time
2111                         if ( latestTurnTime > deathTime )
2112                             latestTurnTime = deathTime;
2113                     }
2114                 }
2115                 else
2116                 {
2117                     // just clamp the latest turn time
2118                     earliestTurnTime = turnTime - sg_timeTolerance;
2119                 }
2120             }
2121 
2122             sg_ArchiveReal( dist_to_dest, 9 );
2123 
2124             REAL simulateAhead = MaxSimulateAhead();
2125 
2126             if ( dist_to_dest > ( ts + simulateAhead ) * avgspeed && currentTime < latestTurnTime )
2127                 break; // no need to worry; we won't reach the next destination
2128 
2129             if ( currentTime < earliestTurnTime && sg_CommandTime.Supported( Owner() ) )
2130                 break; // the turn is too far in the future
2131 
2132             // if ( currentTime < turnTime + EPS )
2133             //    simulateAhead = 0;
2134 
2135             // determine whether to turn left or right (coping with weird axis settings)
2136             int turnTo=0;
2137             // and whether between the last and next destination, there was one missing that
2138             // we didn't receive.
2139             bool missed=false;
2140 
2141             {
2142                 REAL t = currentDestination->direction * dirDrive;
2143                 bool turn = true;
2144 
2145                 missed  = (fabs(t)<.01);
2146                 if (int(braking) != int(currentDestination->braking))
2147                 {
2148                     turn = false;
2149                     missed=!missed;
2150                 }
2151 
2152                 // detect false misses: if the last destination's message ID is just
2153                 // one below the current destination's message ID, it's a fake
2154                 if ( missed && lastDestination && lastDestination->messageID == currentDestination->messageID-1 )
2155                 {
2156                     missed = false;
2157                     if ( ( dirDrive - currentDestination->direction ).NormSquared() < EPS )
2158                     {
2159                         turn = false;
2160                         turnTo = 0;
2161                     }
2162                 }
2163 
2164                 // if the destination is dead ahead, it can't be a fake, either
2165                 if ( missed && sn_GetNetState() == nSERVER && !sg_noRedundantBrakeCommands.Supported( Owner() ) )
2166                 {
2167                     // calculate difference in expected position at the destination's time and the transmitted position
2168                     REAL timeToDest = currentDestination->GetGameTime() - lastTime;
2169                     eCoord posDelta = pos + dirDrive * ( timeToDest * ( verletSpeed_ + .5f * acceleration * timeToDest ) ) - currentDestination->position;
2170                     REAL deltaParallel = eCoord::F( posDelta, dirDrive );
2171                     REAL deltaOrthogonal = posDelta * dirDrive;
2172 
2173                     // if it's small, that's not a miss
2174                     REAL tolerance = verletSpeed_ * GetTurnDelay();
2175                     if ( fabs(deltaParallel) < tolerance && fabs(deltaOrthogonal) < tolerance * .5 )
2176                     {
2177                         missed = false;
2178                         if ( ( dirDrive - currentDestination->direction ).NormSquared() < EPS )
2179                         {
2180                             turn = false;
2181                         }
2182                     }
2183                 }
2184 
2185                 // see if we missed a turn by, say, just counting?
2186                 if ( turns < currentDestination->turns - 1 )
2187                     missed = true;
2188 
2189                 if ( turn )
2190                 {
2191                     // the direction we need to drive in
2192                     // see which direction we drive into after a left or right turn
2193                     int wn = windingNumberWrapped_;
2194                     Grid()->Turn(wn, 1);
2195                     eCoord dirPlus = Grid()->GetDirection(wn);
2196                     wn = windingNumberWrapped_;
2197                     Grid()->Turn(wn, -1);
2198                     eCoord dirMinus = Grid()->GetDirection(wn);
2199 
2200                     if ( missed )
2201                     {
2202                         eCoord dirTurn = (currentDestination->position - pos);
2203 
2204                         // see witch of the alternatives comes closer to the desired direction
2205                         turnTo = ( ( fabs( dirMinus * dirTurn ) - .1 * eCoord::F( dirMinus, dirTurn ) )/dirTurn.NormSquared() < ( fabs( dirPlus * dirTurn ) - .1 * eCoord::F( dirPlus, dirTurn ) )/dirTurn.NormSquared() ) ? -1 : +1;
2206                     }
2207                     else
2208                     {
2209                         // just see which axis gets closer
2210                         eCoord dirTurn = currentDestination->direction;
2211 
2212                         turnTo = ( ( dirMinus - dirTurn ).NormSquared() < ( dirPlus - dirTurn ).NormSquared() ) ? -1 : +1;
2213 
2214                     }
2215                 }
2216             }
2217 
2218             // can we turn already?
2219             bool canTurn = ( turnTo == 0 || CanMakeTurn(turnTo) || overrideTurnDelay );
2220 
2221             if ( lastTime >= earliestTurnTime && canTurn && ( forceTurn || dist_to_dest < 0.01 || timeout <= 0 || lastTime >= latestTurnTime ) ){
2222                 forceTurn = false;
2223 
2224 #ifdef DEBUG
2225                 if ( turnTo != 0 )
2226                 {
2227                     static REAL checkFactor = .9f;
2228                     gTurnDelayOverride check( checkFactor );
2229                     if ( !CanMakeTurn( turnTo ) )
2230                     {
2231                         con << "Early turn!\n";
2232                         // st_Breakpoint();
2233                     }
2234                 }
2235 #endif
2236 
2237 
2238                 // con << timeLeft << ", " << earlyTurnTolerance << ", " << rubberActive << ", " << dist_to_dest << "\n";
2239 
2240                 // the destination is very close or we gave up. Now is the time to turn towards
2241                 // it or turn to the direction it gives us
2242 
2243                 // bring us to the exact location to avoid lag sliding due to
2244                 // disagreement between client and server.
2245                 // but only if we are reasonably close ( don't want cheating )
2246                 // and use a correct move that kills us if we cross a wall.
2247 
2248                 /*
2249                          if ( dist_to_dest < .1 && dist_to_dest > -.1 )
2250                          {
2251                              Move( pos + dirDrive * dist_to_dest, currentTime, currentTime );
2252                          }
2253                 #ifdef DEBUG
2254                          else
2255                          {
2256                              if ( breakp )
2257                              {
2258                                  int x;
2259                                  x = 0;
2260                              }
2261                              con << "gCycle::Timestep: Could not completely reach destination.\n";
2262                              breakp = true;
2263                          }
2264                 #endif
2265                 */
2266 
2267                 // con << "Turn: " << lastTime << ", " << dist_to_dest << ", " << currentDestination->position << ", " << pos << "\n";
2268 
2269                 //eDebugLine::SetTimeout(2);
2270                 //eDebugLine::SetColor( 0,1,1 );
2271                 //eDebugLine::Draw( currentDestination->position, 0, currentDestination->position, 8 );
2272                 //eDebugLine::SetColor( 0,0,1 );
2273                 //eDebugLine::Draw( pos, 0, pos, 8 );
2274 
2275                 bool used = false; // flag indicating whether the current destination has been used
2276 
2277                 if (!missed){ // then we did not miss a destination
2278                     used = true;
2279 
2280                     if (turnTo != 0)
2281                     {
2282 #ifdef DEBUG
2283 #ifdef DEDICATED
2284                         eCoord slide = this->pos - currentDestination->position;
2285                         if ( Player() && slide.NormSquared() > .01 )
2286                             con << "Lag slide for " << Player()->GetUserName() << ": "  << slide << ", rubberSpeedFactor " << rubberSpeedFactor << "\n";
2287 #endif
2288 #endif
2289                         gTurnDelayOverride override( overrideTurnDelay );
2290                         Turn(turnTo);
2291                     }
2292                     else{
2293                         AccelerationDiscontinuity();
2294                         braking = currentDestination->braking;
2295                         if (sn_GetNetState()!=nCLIENT)
2296                             RequestSync();
2297                     }
2298 
2299                     /*
2300                       con << "turning alon " << currentDestination->position << ","
2301                       << currentDestination->direction << ","
2302                       << currentDestination->distance << "\n";
2303                     */
2304                 }
2305                 else
2306                 {
2307 
2308                     // Uh oh. Turn commands are missing. We should wait as long as possible, it must
2309                     // already be on its way.
2310                     if ( lastTime > currentTime - Lag()*sg_packetMissTolerance )
2311                         return !Alive();
2312 
2313                     if ( turns >= currentDestination->turns - 1 )
2314                     {
2315                         // OK, we missed exactly one turn. Don't panic. Just turn
2316                         // towards the destination:
2317                         REAL side = (currentDestination->position - pos) * dirDrive;
2318                         if ( fabs(side)>verletSpeed_ * GetTurnDelay() * .2 )
2319                         {
2320                             gTurnDelayOverride override( overrideTurnDelay );
2321                             Turn(turnTo);
2322                         }
2323                         else
2324                             used = true;
2325                     }
2326                     else
2327                     {
2328                         // missed more than one turn. Drat. Ignore and hope for the best.
2329                         // st_Breakpoint();
2330                         ++turns;
2331                     }
2332                     /*
2333                       con << "turning to   " << currentDestination->position << ","
2334                       << currentDestination->direction << ","
2335                       << currentDestination->distance << "\n";
2336                     */
2337 
2338                 }
2339 
2340                 overrideTurnDelay = false;
2341 
2342                 if ( used )
2343                 {
2344                     // only the server marks destinations as used; the client has to reuse them sometimes.
2345                     currentDestination->hasBeenUsed = (sn_GetNetState()!=nCLIENT);
2346                     lastDestination = currentDestination;
2347 
2348                     // advance
2349                     currentDestination = currentDestination->next;
2350                 }
2351 
2352 
2353                 while (currentDestination && currentDestination->hasBeenUsed)
2354                 {
2355                     // breakp = false;
2356                     currentDestination = currentDestination->next;
2357                 }
2358             }
2359             else
2360             {
2361                 // ok, the dest is right ahead, but not close enough.
2362                 // how long does it take at least
2363                 // (therefore the strange average speed) to get there?
2364                 sg_ArchiveReal( avgspeed, 9 );
2365                 REAL tsTodo = dist_to_dest/avgspeed;
2366                 /*
2367                          if ( tsTodo > timeLeft )
2368                          {
2369                              tsTodo = timeLeft;
2370                          }
2371                 */
2372                 sg_ArchiveReal( tsTodo, 9 );
2373 
2374                 // we can't turn now, simulate until we can
2375                 if ( !canTurn )
2376                 {
2377                     REAL nextTurn = GetNextTurn(turnTo);
2378                     REAL turnStep = nextTurn - lastTime;
2379 
2380                     // clamp timestep values
2381                     if ( turnTime < nextTurn )
2382                         turnTime = nextTurn;
2383                     if ( earliestTurnTime < nextTurn )
2384                         earliestTurnTime = nextTurn;
2385                     if ( latestTurnTime < nextTurn )
2386                         latestTurnTime = nextTurn;
2387 
2388                     if ( currentTime - lastTime > turnStep )
2389                     {
2390                         tsTodo = turnStep;
2391 
2392                         // if we can simulate to the turn in the next step, do so, overriding
2393                         // the turn delay then.
2394                         if ( tsTodo < ts + simulateAhead && tsTodo > 0 )
2395                         {
2396                             overrideTurnDelay = true;
2397                         }
2398                     }
2399                     else
2400                     {
2401                         // not enough time to simulate to turn possibility; skip out of loop
2402                         break;
2403                     }
2404                 }
2405                 else
2406                 {
2407                     sg_ArchiveReal( tsTodo, 9 );
2408                     // don't turn too late
2409                     REAL maxts = latestTurnTime - lastTime;
2410                     sg_ArchiveReal( maxts, 9 );
2411                     if ( tsTodo > maxts )
2412                     {
2413                         // force turn on next iteration, we'll be there
2414                         forceTurn = true;
2415                         tsTodo = maxts;
2416                     }
2417 
2418                     // don't turn too early
2419                     REAL mints = earliestTurnTime - lastTime;
2420                     // sg_ArchiveReal( mints, 9 );
2421                     if ( tsTodo < mints )
2422                     {
2423                         tsTodo = mints;
2424                     }
2425                 }
2426 
2427                 if ( tsTodo < 0 )
2428                 {
2429                     // should never happen
2430                     st_Breakpoint();
2431                     return !Alive();
2432                 }
2433                 if ( tsTodo > ts + simulateAhead )
2434                 {
2435                     tsTodo = ts + simulateAhead ;
2436                     forceTurn = false;
2437 
2438                     // quit from here if there is nothing to do
2439                     if ( tsTodo <= EPS )
2440                         break;
2441                 }
2442 	#ifdef DEBUG
2443                 if ( tsTodo < 0 )
2444                     con << "Negative timestep!\n";
2445 	#endif
2446                 sg_ArchiveReal( tsTodo, 9 );
2447 
2448                 // core simulation
2449                 if ( tsTodo > EPS )
2450                 {
2451                     TimestepCore( lastTime + tsTodo, false );
2452                 }
2453                 else
2454                 {
2455                     // already nothing to do, turn on next iteration
2456                     forceTurn = true;
2457                 }
2458             }
2459         }
2460     }
2461 
2462     // simulate exactly to the time of the next turn if it is in reach
2463     if ( !pendingTurns.empty())
2464     {
2465         REAL nextTurn = GetNextTurn(pendingTurns.front());
2466         if(currentTime>nextTurn) {
2467             if ( nextTurn > lastTime )
2468                 TimestepCore( nextTurn );
2469 
2470             //con << "Executing delayed turn at time " << lastTime << "\n";
2471             Turn(pendingTurns.front());
2472             pendingTurns.pop_front();
2473         }
2474     }
2475 
2476     // do the rest of the timestep
2477     bool ret = false;
2478     if ( currentTime > lastTime )
2479         ret = TimestepCore( currentTime );
2480 
2481     return ret;
2482 }
2483 
2484 // *******************************************************************************************
2485 // *
2486 // *	AddRef
2487 // *
2488 // *******************************************************************************************
2489 //!
2490 //!
2491 // *******************************************************************************************
2492 
AddRef(void)2493 void gCycleMovement::AddRef( void )
2494 {
2495     eNetGameObject::AddRef();
2496     if ( GetRefcount() > sg_cycleMaxRefCount && Alive() )
2497     {
2498         // during the kill, further refcounts will be added, so we need to pump
2499         // up the limit
2500         int backup = sg_cycleMaxRefCount;
2501         sg_cycleMaxRefCount += 100;
2502         Kill();
2503         sg_cycleMaxRefCount = backup;
2504     }
2505 }
2506 
2507 // *******************************************************************************************
2508 // *
2509 // *	gCycleMovement
2510 // *
2511 // *******************************************************************************************
2512 //!
2513 //!		@param	grid			the grid the cycle will live on
2514 //!		@param	pos				start position
2515 //!		@param	dir				start direction
2516 //!		@param	player			player owning this cycle
2517 //!		@param	autodelete		should the cycle get deleted when it gets killed?
2518 //!
2519 // *******************************************************************************************
2520 
gCycleMovement(eGrid * grid,const eCoord & pos,const eCoord & dir,ePlayerNetID * player,bool autodelete)2521 gCycleMovement::gCycleMovement( eGrid * grid, const eCoord & pos, const eCoord & dir, ePlayerNetID * player, bool autodelete )
2522         :eNetGameObject(grid, pos,dir,player,autodelete),
2523         destinationList(NULL),currentDestination(NULL),lastDestination(NULL),
2524         dirDrive(dir),
2525         acceleration(0),
2526         lastTimestep_(0),
2527         verletSpeed_(sg_speedCycleStart * SpeedMultiplier()),
2528         pendingTurns()
2529 {
2530     windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dir);
2531 
2532     MyInitAfterCreation();
2533 }
2534 
2535 // *******************************************************************************************
2536 // *
2537 // *	gCycleMovement
2538 // *
2539 // *******************************************************************************************
2540 //!
2541 //!		@param	message		network message to read everything from
2542 //!
2543 // *******************************************************************************************
2544 
gCycleMovement(nMessage & message)2545 gCycleMovement::gCycleMovement( nMessage & message )
2546         :eNetGameObject(message),
2547         destinationList(NULL),currentDestination(NULL),lastDestination(NULL),
2548         dirDrive(1,0),
2549         acceleration(0),
2550         lastTimestep_(0),
2551         verletSpeed_(5)
2552 {
2553     windingNumberWrapped_ = windingNumber_ = 2;
2554 
2555     // MyInitAfterCreation();
2556 }
2557 
2558 // *******************************************************************************************
2559 // *
2560 // *	~gCycleMovement
2561 // *
2562 // *******************************************************************************************
2563 //!
2564 //!
2565 // *******************************************************************************************
2566 
~gCycleMovement(void)2567 gCycleMovement::~gCycleMovement( void )
2568 {
2569     lastDestination = NULL;
2570     currentDestination = NULL;
2571 
2572     while(destinationList)
2573     {
2574         gDestination* dest = destinationList;
2575         delete dest;
2576     }
2577 
2578     verletSpeed_=distance=0;
2579 
2580     delete maxSpaceHit_;
2581     maxSpaceHit_ = NULL;
2582 }
2583 
RequestSync(bool ack)2584 void gCycleMovement::RequestSync(bool ack)
2585 {
2586     // no more syncs when you're dead
2587     if ( !Alive() )
2588     {
2589        return;
2590     }
2591 
2592     // delegate
2593     eNetGameObject::RequestSync( ack );
2594 }
2595 
RequestSync(int user,bool ack)2596 void gCycleMovement::RequestSync(int user,bool ack)
2597 {
2598     // no more syncs when you're dead
2599     if ( !Alive() )
2600     {
2601         return;
2602     }
2603 
2604     // delegate
2605     eNetGameObject::RequestSync( user, ack );
2606 }
2607 
OnRemoveFromGame()2608 void gCycleMovement::OnRemoveFromGame()
2609 {
2610     delete maxSpaceHit_;
2611     maxSpaceHit_ = NULL;
2612 
2613     eNetGameObject::OnRemoveFromGame();
2614 }
2615 
2616 // *******************************************************************************************
2617 // *
2618 // *	CopyFrom
2619 // *
2620 // *******************************************************************************************
2621 //!
2622 //!		@param	other	the cycle to copy everything from
2623 //!
2624 // *******************************************************************************************
2625 
CopyFrom(const gCycleMovement & other)2626 void gCycleMovement::CopyFrom( const gCycleMovement & other )
2627 {
2628 #ifdef DEBUG_X
2629     // calculate position update
2630     eCoord posUpdate = other.Position() - this->Position();
2631 
2632     // only update direction if the positions are out of sync
2633     REAL lag = 1;
2634     if ( player )
2635         lag = player->ping;
2636 
2637     REAL tol = this->speed * lag;
2638     if ( posUpdate.NormSquared() > tol*tol )// && eCoord::F( dirDrive, other.dirDrive ) < .5 )
2639     {
2640         con << "Out of sync!\n";
2641         //		dir 			= other.Direction();
2642 
2643         // get second opinion
2644         tJUST_CONTROLLED_PTR<gCycleExtrapolator> extrapolator = tNEW( gCycleExtrapolator )(grid, pos, dir );
2645         gCycleExtrapolator& secondOpinion = *extrapolator;
2646         secondOpinion.CopyFrom( sg_usedMessage, *this );
2647         eGameObject::TimestepThis( other.lastTime, &secondOpinion );
2648     }
2649 #endif
2650 
2651     dirDrive 		= other.dirDrive;
2652 
2653     // transfer position and time
2654     currentFace = other.currentFace;
2655     pos = other.Position();
2656     lastTime = other.LastTime();
2657     // Move( other.Position(), LastTime(), other.LastTime() );
2658     // Move( other.Position() + other.Direction() * ( ( lastTime - other.LastTime() ) * other.Speed() ), LastTime(), other.LastTime() );
2659 
2660     // std::cout << "copy: " << brakingReservoir << ":" << braking << "\n";
2661 
2662     // transfer additional data
2663     team            = other.team;
2664     distance        = other.distance;
2665     lastTimestep_   = other.lastTimestep_;
2666     verletSpeed_    = other.verletSpeed_;
2667     acceleration    = other.acceleration;
2668     rubber	        = other.rubber;
2669     rubberMalus     = other.rubberMalus;
2670     brakingReservoir= other.brakingReservoir;
2671     windingNumber_          = other.windingNumber_;
2672     windingNumberWrapped_   = other.windingNumberWrapped_;
2673 
2674     tASSERT(finite(distance));
2675 
2676     // std::cout << "copy: " << brakingReservoir << ":" << braking << "\n";
2677 
2678 #ifdef DEBUG_X
2679     if ( turns != other.turns )
2680     {
2681         con << "Client/Server turn mismatch:" << turns << " != " << other.turns << "\n";
2682     }
2683 #endif
2684 
2685     // update number of turns if the player is not turning wildly
2686     REAL right = GetNextTurn(1);
2687     REAL left  = GetNextTurn(-1);
2688     if ( lastTime > (right > left ? right : left) + 2 * GetTurnDelay() )
2689         turns			= other.turns;
2690 }
2691 
2692 static nVersionFeature sg_sendCorrectLastTurn(8);
2693 
2694 // *******************************************************************************************
2695 // *
2696 // *	CopyFrom
2697 // *
2698 // *******************************************************************************************
2699 //!
2700 //!		@param	sync	   	the network sync data to copy the most important data from
2701 //!		@param	other		the cycle to copy the rest of the information from
2702 //!
2703 // *******************************************************************************************
2704 
CopyFrom(const SyncData & sync,const gCycleMovement & other)2705 void gCycleMovement::CopyFrom( const SyncData & sync, const gCycleMovement & other )
2706 {
2707     // fetch values from sync
2708     dir = dirDrive 	= sync.dir;
2709     lastTimestep_   = 0;
2710     verletSpeed_ 	= sync.speed;
2711     rubber			= sync.rubber;
2712     rubberMalus     = sync.rubberMalus;
2713     braking			= sync.braking;
2714     distance		= sync.distance;
2715     turns			= sync.turns;
2716     brakingReservoir= sync.brakingReservoir;
2717     // std::cout << "fromsync: " << brakingReservoir << ":" << braking << "\n";
2718 
2719     tASSERT(finite(distance));
2720 
2721     // reset winding number and acceleration
2722     this->SetWindingNumberWrapped( Grid()->DirectionWinding(dirDrive) );
2723     acceleration	= 0;
2724 
2725     // fetch values from other
2726     // rubber = other.rubber;
2727     SetPlayer( other.Player() );
2728     currentFace         = other.currentFace;
2729 
2730     {
2731         //this->currentDestination = other.destinationList;
2732         //while ( currentDestination && currentDestination->messageID != sync.messageID )
2733         //    currentDestination = currentDestination->next;
2734 
2735         // deterimine last passed destination by the message ID
2736         this->currentDestination = GetDestinationBefore( sync, other.destinationList );
2737 
2738         bool trustDestination = true;
2739         if ( currentDestination && sn_GetNetState() == nCLIENT && !sg_sendCorrectLastTurn.Supported(0) )
2740         {
2741             // the server may send wrong information in the rare case that
2742             // our last turn command was not promptly executed. Sanity check:
2743             // if the relevant information from the alleged last destination
2744             // differs from the current state, it is already the next destination.
2745             if ( ( currentDestination->braking != (bool)braking ) || fabs( currentDestination->direction * dirDrive ) > .01 )
2746                 trustDestination = false;
2747         }
2748 
2749         // we only need the next one
2750         if ( trustDestination && currentDestination )
2751             currentDestination = currentDestination->next;
2752     }
2753 
2754     // let extrapolator find its face ( and set position and time )
2755     MoveSafely( sync.pos, sync.time, sync.time );
2756 
2757     // set last turn
2758     lastTurnTimeRight_ = lastTurnTimeLeft_ = -100;
2759 }
2760 
2761 // *******************************************************************************************
2762 // *
2763 // *	InitAfterCreation
2764 // *
2765 // *******************************************************************************************
2766 //!
2767 //!
2768 // *******************************************************************************************
2769 
InitAfterCreation(void)2770 void gCycleMovement::InitAfterCreation( void )
2771 {
2772 #ifdef DEBUG
2773     if (!finite(verletSpeed_))
2774         st_Breakpoint();
2775 #endif
2776     eNetGameObject::InitAfterCreation();
2777 #ifdef DEBUG
2778     if (!finite(verletSpeed_))
2779         st_Breakpoint();
2780 #endif
2781     MyInitAfterCreation();
2782 }
2783 
2784 // version feature indicating that proper scaling of the base acceleration with the speed multiplier is used
2785 static nVersionFeature sg_correctAccelerationScaling( 8 );
2786 
2787 // calculate essential rubber values
sg_RubberValues(ePlayerNetID const * player,REAL speed,REAL & max,REAL & effectiveness)2788 void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness )
2789 {
2790     // base values
2791     max=sg_rubberCycle;
2792     effectiveness=1;
2793 
2794     // make rubber more effective for high ping players
2795     if ( player )
2796     {
2797         if ( max > 0 )
2798             // either by increasing the effectiveness...
2799             effectiveness *= ( max + player->ping * sg_rubberCyclePing )/max;
2800         else
2801             // or the reservoir.
2802             max += player->ping * sg_rubberCyclePing;
2803     }
2804 
2805     {
2806         // modify rubber effectiveness by a speed dependant power law
2807         REAL speedFactor = speed/(sg_speedCycle*gCycleMovement::SpeedMultiplier());
2808 
2809         effectiveness *= pow( speedFactor, sg_rubberCycleTimeBased );
2810     }
2811 }
2812 
2813 // *******************************************************************************************
2814 // *
2815 // *	AccelerationDiscontinuity
2816 // *
2817 // *******************************************************************************************
2818 //!
2819 //!
2820 // *******************************************************************************************
2821 
AccelerationDiscontinuity()2822 void gCycleMovement::AccelerationDiscontinuity()
2823 {
2824     // make fake 0 timestep
2825     verletSpeed_ = Speed();
2826     lastTimestep_ = 0;
2827 }
2828 
2829 // *******************************************************************************************
2830 // *
2831 // *	CalculateAcceleration
2832 // *
2833 // *******************************************************************************************
2834 //!
2835 //!
2836 // *******************************************************************************************
2837 
CalculateAcceleration()2838 void gCycleMovement::CalculateAcceleration()
2839 {
2840     tASSERT( good( verletSpeed_ ) );
2841 
2842     // reset usage variables
2843     brakeUsage = 0.0f;
2844     rubberUsage = 0.0f;
2845 
2846     // calculate acceleration
2847     acceleration=0;
2848 
2849     // brake: it's only available since this version...
2850     static nVersionFeature brakeDepletion(2);
2851 
2852     // and servers starting from this version disable it my modifying config items
2853     static nVersionFeature brakeDepletionHandledWithConfig(10);
2854 
2855     // simply use the configured brake always on the server
2856     // and on the client if the server should have disabled it, but does not.
2857 
2858     if ( sn_GetNetState() != nCLIENT || brakeDepletion.Supported() || brakeDepletionHandledWithConfig.Supported(0) )
2859     {
2860         if(braking)
2861         {
2862             if ( brakingReservoir > 0.0 )
2863             {
2864                 brakeUsage = sg_cycleBrakeDeplete;
2865                 acceleration-=sg_brakeCycle * SpeedMultiplier();
2866             }
2867             else
2868                 brakingReservoir = 0.0f;
2869         }
2870         else
2871         {
2872             if ( brakingReservoir < 1.0 )
2873             {
2874                 brakeUsage = -sg_cycleBrakeRefill;
2875             }
2876             else
2877                 brakingReservoir = 1.0f;
2878         }
2879     }
2880     else
2881     {
2882         if(braking)
2883         {
2884             acceleration-=sg_brakeCycle * SpeedMultiplier();
2885         }
2886     }
2887 
2888     sg_ArchiveReal( acceleration, 9 );
2889 
2890     REAL baseSpeed = sg_speedCycle * SpeedMultiplier();
2891     if ( verletSpeed_ <= ( sg_correctAccelerationScaling.Supported() ? baseSpeed : sg_speedCycle ) )
2892         acceleration+=( baseSpeed - verletSpeed_) * sg_speedCycleDecayBelow;
2893     else
2894         acceleration+=( baseSpeed - verletSpeed_) * sg_speedCycleDecayAbove;
2895 
2896     tASSERT( good( acceleration ) );
2897     sg_ArchiveReal( acceleration, 9 );
2898 
2899     // sense near wall behind us, accelerate more
2900     REAL totalWallAcceleration = 0; // total acceleration by walls
2901     REAL tunnelWidth           = 0; // with of the tunnel the cycle is in
2902     REAL sideWidth             = sg_cycleWidthSide * 2; // minimal distance to wall
2903     bool slingshot  = true;         // flag indicating whether the cycle is between two walls
2904     bool oneOwnWall = false;        // flag indicating whether one of the walls is your own
2905     for(int d=1;d>=-1;d-=2){
2906         // the direction to cast the acceleration rays in
2907         eCoord dirCast = dirDrive.Turn(-1,d);
2908         gSensor rear(this,pos,dirCast);
2909         rear.detect(sg_nearCycle);
2910 
2911         enemyInfluence.AddSensor( rear, 0, this );
2912 
2913         if (rear.ehit && rear.hit < sg_cycleWidth + .1f )
2914             blocks(rear, this, -d);
2915 
2916         if ( 0 != rear.ehit )
2917         {
2918             sg_ArchiveReal( rear.hit, 9 );
2919 
2920             // update the minimal wall distance
2921             if ( sideWidth > rear.hit )
2922                 sideWidth = rear.hit;
2923 
2924             // drop walls that are grinded
2925             if ( rear.hit < verletSpeed_ * .01 )
2926                 ::sg_DropTempWall( dirCast, rear );
2927 
2928             // see if the wall is parallel to the driving direction, only then should it add speed
2929             eCoord wallVec = rear.ehit->Vec();
2930             if ( fabs( eCoord::F( wallVec, dirDrive  ) ) > .9 * dirDrive.NormSquared() )
2931             {
2932                 // enemyInfluence.AddSensor( rear, 1 );
2933                 REAL wallAcceleration=SpeedMultiplier() * sg_accelerationCycle * ((1/(rear.hit+sg_accelerationCycleOffs))
2934                                       -(1/(sg_nearCycle+sg_accelerationCycleOffs)));
2935 
2936                 tunnelWidth += rear.hit;
2937 
2938                 // apply modificators
2939                 switch (rear.type)
2940                 {
2941                 case gSENSOR_SELF:
2942                     wallAcceleration *= sg_accelerationCycleSelf;
2943                     oneOwnWall = true;
2944                     break;
2945                 case gSENSOR_TEAMMATE:
2946                     wallAcceleration *= sg_accelerationCycleTeam;
2947                     break;
2948                 case gSENSOR_ENEMY:
2949                     wallAcceleration *= sg_accelerationCycleEnemy;
2950                     break;
2951                 case gSENSOR_RIM:
2952                     wallAcceleration *= sg_accelerationCycleRim;
2953                     break;
2954                 case gSENSOR_NONE:
2955                     wallAcceleration = 0;
2956                     slingshot = false;
2957                     break;
2958 
2959                 }
2960 
2961                 sg_ArchiveReal( wallAcceleration, 9 );
2962                 totalWallAcceleration += wallAcceleration;
2963             }
2964             else
2965             {
2966                 slingshot = false;
2967             }
2968         }
2969         else
2970         {
2971             slingshot = false;
2972         }
2973 
2974         sg_ArchiveReal( totalWallAcceleration, 9 );
2975     }
2976 
2977     // kill cycle if it is inside a too narrow channel
2978     if ( ( slingshot && tunnelWidth < sg_cycleWidth ) || sideWidth < sg_cycleWidthSide )
2979     {
2980         tunnelWidth = 0;
2981         REAL sideWidth = sg_cycleWidthSide * 2;
2982 
2983         // check again with sensors to the front, both sensor pairs need
2984         // to see a narrow tunnel
2985         for(int d=1;d>=-1;d-=2)
2986         {
2987             // the direction to cast the acceleration rays in
2988             eCoord dirCast = dirDrive.Turn(1,d);
2989             gSensor front(this,pos,dirCast);
2990             front.detect(sg_nearCycle);
2991 
2992             if ( front.ehit && front.ehit->Other() )
2993             {
2994                 sg_ArchiveReal( front.hit, 9 );
2995 
2996                 // update the minimal wall distance
2997                 if ( sideWidth > front.hit )
2998                     sideWidth = front.hit;
2999 
3000                 tunnelWidth += front.hit;
3001             }
3002             else
3003             {
3004                 tunnelWidth += sg_cycleWidth;
3005             }
3006         }
3007 
3008         if ( tunnelWidth < sg_cycleWidth || sideWidth < sg_cycleWidthSide )
3009         {
3010             // determine the space available measured in the space allowed
3011             REAL available1 = 1;
3012             REAL available2 = 1;
3013             if ( sg_cycleWidth > 0 )
3014                 available1 = tunnelWidth/sg_cycleWidth;
3015             if ( sg_cycleWidthSide > 0 )
3016                 available2 = sideWidth/sg_cycleWidthSide;
3017             REAL available = available1 < available2 ? available1 : available2;
3018 
3019             // get rubber values
3020             // REAL rubberGranted, rubberEffectiveness;
3021             // sg_RubberValues( player, verletSpeed_, rubberGranted, rubberEffectiveness );
3022 
3023             // calculate rubber usage from squeezing
3024             rubberUsage = sg_cycleWidthRubberMax + ( sg_cycleWidthRubberMin - sg_cycleWidthRubberMax ) * available;
3025         }
3026     }
3027 
3028     // apply slingshot/tunnel multiplier
3029     if ( slingshot )
3030     {
3031         if ( oneOwnWall )
3032             totalWallAcceleration *= sg_accelerationCycleSlingshot;
3033         else
3034             totalWallAcceleration *= sg_accelerationCycleTunnel;
3035     }
3036 
3037     // apply wall acceleration
3038     acceleration += totalWallAcceleration;
3039 
3040     tASSERT( good( acceleration ) );
3041     sg_ArchiveReal( acceleration, 9 );
3042 
3043     tASSERT( good( verletSpeed_ ) );
3044 }
3045 
3046 // *******************************************************************************************
3047 // *
3048 // *	ApplyAcceleration
3049 // *
3050 // *******************************************************************************************
3051 //!
3052 //!		@param	dt		length of timestep
3053 //!
3054 // *******************************************************************************************
3055 
ApplyAcceleration(REAL dt)3056 void gCycleMovement::ApplyAcceleration( REAL dt )
3057 {
3058     sg_ArchiveReal( verletSpeed_, 9 );
3059     sg_ArchiveReal( dt, 9 );
3060     sg_ArchiveReal( acceleration, 9 );
3061 
3062     // the speed needs to be simulated for this half frame and half of the last frame
3063     REAL verletTimestep = sg_verletIntegration.Supported() ? .5 * ( dt + lastTimestep_ ) : dt;
3064     lastTimestep_ = dt;
3065 
3066     sg_ArchiveReal( verletTimestep, 9 );
3067 
3068     // don't use euler timesteps for large cycle speed decays
3069     bool properDecay = false;
3070     REAL maxTimestep = verletTimestep > dt ? verletTimestep : dt;
3071     if ( sg_speedCycleDecayBelow * maxTimestep > .1 || sg_speedCycleDecayAbove * maxTimestep > .1 )
3072     {
3073         REAL speedDecay = 0;
3074         REAL baseSpeed = sg_speedCycle * SpeedMultiplier();
3075         if ( verletSpeed_ < ( sg_correctAccelerationScaling.Supported() ? baseSpeed : sg_speedCycle ) )
3076             speedDecay = sg_speedCycleDecayBelow;
3077         else
3078             speedDecay = sg_speedCycleDecayAbove;
3079 
3080         if ( speedDecay * maxTimestep > .1 && dt > EPS )
3081         {
3082             // ok, really, a  better simulation is needed
3083             properDecay = true;
3084 
3085             // that's what CalculateAcceleration extrapolates
3086             REAL decayAcceleration = ( baseSpeed - verletSpeed_) * speedDecay;
3087             // throw it away
3088             acceleration -= decayAcceleration;
3089 
3090             tASSERT( good( acceleration ) );
3091 
3092             // adapt base speed as the limit speed with the current decay and acceleration
3093             baseSpeed += acceleration/speedDecay;
3094 
3095             // do a proper decay
3096             verletSpeed_ = baseSpeed + ( verletSpeed_ - baseSpeed ) * exp( -speedDecay * verletTimestep );
3097 
3098             // calculate new acceleration based purely on the decay, the external acceleration
3099             // is factored into baseSpeed now. Add extra decay factor so that
3100             // Speed() returns the most accurate current speed available.
3101             acceleration = ( baseSpeed - verletSpeed_) * ( 1 - exp( -speedDecay * dt * .5f ) ) / ( .5f * dt );
3102 
3103             tASSERT( good( acceleration ) );
3104         }
3105     }
3106 
3107     // if decay wasn't handled properly (because it didn't need to), use euler/verlet
3108     tASSERT( good( acceleration ) );
3109     if ( !properDecay )
3110         verletSpeed_+=acceleration*verletTimestep;
3111     tASSERT( good( verletSpeed_ ) );
3112 
3113     // clamp speed
3114     REAL minSpeed = sg_speedCycle*SpeedMultiplier()*sg_speedCycleMin;
3115     REAL maxSpeed = ( 100 + sg_speedCycle*SpeedMultiplier() )* 100000;
3116     if ( sg_speedCycleMax > 0 )
3117     {
3118         maxSpeed = sg_speedCycle*SpeedMultiplier()*sg_speedCycleMax;
3119     }
3120 
3121     sg_ArchiveReal( minSpeed, 9 );
3122     sg_ArchiveReal( maxSpeed, 9 );
3123     sg_ArchiveReal( acceleration, 9 );
3124 
3125     if ( clamp( verletSpeed_, minSpeed, maxSpeed ) )
3126         acceleration = 0;
3127 
3128     sg_ArchiveReal( acceleration, 9 );
3129 
3130     sg_ArchiveReal( verletSpeed_, 9 );
3131 }
3132 
3133 // *******************************************************************************************
3134 // *
3135 // *	DoTurn
3136 // *
3137 // *******************************************************************************************
3138 //!
3139 //!		@param	dir +1 for right turns, -1 for left turns
3140 //!		@return		true of the turn could be executed right now, false if it was queued
3141 //!
3142 // *******************************************************************************************
3143 
DoTurn(int dir)3144 bool gCycleMovement::DoTurn( int dir )
3145 {
3146     if ( turns == 0 )
3147         turns = 1;
3148 
3149     if (dir >  1) dir =  1;
3150     if (dir < -1) dir = -1;
3151 
3152     if ( CanMakeTurn( lastTime, dir ) )
3153     {
3154         // request regeneration of maximum space
3155         refreshSpaceAhead_ = true;
3156 
3157         // notify that no rubber is currently used (may be a lie, but a timestep correcting
3158         // it will surely follow)
3159         rubberSpeedFactor = 1;
3160 
3161         // store last postion
3162         lastTurnPos_ = pos;
3163 
3164         turns++;
3165         /*
3166         if( sn_GetNetState() != nSERVER )
3167         {
3168             // simulate lost turns
3169             turns+=2;
3170         }
3171         */
3172 
3173         AccelerationDiscontinuity();
3174         verletSpeed_ *= sg_cycleTurnSpeedFactor;
3175         rubberMalus  += sg_rubberCycleMalusTurn;
3176 
3177         gap_[0] = gap_[1] = 1E+30;
3178         keepLookingForGap_[0] = keepLookingForGap_[1] = true;
3179 
3180         // turn winding numbers
3181         int wn = windingNumberWrapped_;
3182         Grid()->Turn(wn, dir);
3183         this->SetWindingNumberWrapped( wn );
3184 
3185         eCoord nextDirDrive = Grid()->GetDirection(windingNumberWrapped_);
3186 
3187         // send out a sensor a bit backwards and forwards into the turn direction to
3188         // copy all temporary walls into the grid
3189         {
3190             REAL range = .1 * Speed();
3191             eCoord dirCast = nextDirDrive;
3192             gSensor gridder1( this, Position(), dirCast );
3193             gridder1.detect( range );
3194             if ( gridder1.ehit )
3195                 ::sg_DropTempWall( nextDirDrive, gridder1 );
3196 
3197             gSensor gridder3( this, Position() - dirCast * (range*.5), dirCast );
3198             gridder3.detect( range );
3199             if ( gridder3.ehit )
3200                 ::sg_DropTempWall( nextDirDrive, gridder3 );
3201 
3202             // the ray backwards should detect walls that affected the acceleration;
3203             // they can also give a boost. Increase the range.
3204             if ( range < sg_nearCycle )
3205                 range = sg_nearCycle;
3206 
3207             gSensor gridder2( this, Position(), -dirCast );
3208             gridder2.detect( range );
3209             if ( gridder2.ehit )
3210             {
3211                 ::sg_DropTempWall( nextDirDrive, gridder2 );
3212 
3213                 // apply the boost. Calculate wall distance
3214                 REAL dist = gridder2.hit;
3215 
3216                 // calculate the factor acceleration would be multiplied with
3217                 REAL accellerationFactorOffset = 1/(sg_nearCycle+sg_accelerationCycleOffs);
3218                 REAL accelerationFactor = (1/(dist+sg_accelerationCycleOffs)) - accellerationFactorOffset;
3219                 // this would be the maximal acceleration factor
3220                 REAL accelerationFactorMax = (1/sg_accelerationCycleOffs) - accellerationFactorOffset;
3221 
3222                 if( accelerationFactorMax > 0 && dist < sg_nearCycle )
3223                 {
3224                     // select boost settings according to wall type
3225                     // apply modificators
3226                     REAL boost = 0, boostFactor = 1;
3227                     switch (gridder2.type)
3228                     {
3229                     case gSENSOR_SELF:
3230                         boost = sg_boostCycleSelf;
3231                         boostFactor = sg_boostFactorCycleSelf;
3232                         break;
3233                     case gSENSOR_TEAMMATE:
3234                         boost = sg_boostCycleTeam;
3235                         boostFactor = sg_boostFactorCycleTeam;
3236                         break;
3237                     case gSENSOR_ENEMY:
3238                         boost = sg_boostCycleEnemy;
3239                         boostFactor = sg_boostFactorCycleEnemy;
3240                         break;
3241                     case gSENSOR_RIM:
3242                         boost = sg_boostCycleRim;
3243                         boostFactor = sg_boostFactorCycleRim;
3244                         break;
3245                     case gSENSOR_NONE:
3246                         break;
3247                     }
3248 
3249                     // apply acceleration factor to boost
3250                     boostFactor = 1 + ( boostFactor - 1 ) * accelerationFactor / accelerationFactorMax;
3251                     boost *= SpeedMultiplier() * accelerationFactor / accelerationFactorMax;
3252 
3253                     // apply boost to speed
3254                     verletSpeed_ = verletSpeed_ * boostFactor + boost;
3255                     tASSERT( good( verletSpeed_ ) );
3256                 }
3257             }
3258 
3259             // if edges have been inserted into the grid, find a new current face.
3260             FindCurrentFace();
3261         }
3262 
3263         // update driving directions
3264         lastDirDrive = dirDrive;
3265 
3266         if(dir == 1)
3267             lastTurnTimeRight_ = lastTime;
3268         else
3269             lastTurnTimeLeft_ = lastTime;
3270 
3271         dirDrive = nextDirDrive;
3272 
3273 #ifdef DEBUGOUTPUT
3274         if ( sg_cycleDebugPrintLevel > 0 )
3275             con << Player()->GetName() << " turned " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
3276 #endif
3277 
3278         return true;
3279     }
3280     else {
3281         int maxPendingTurns=sg_cycleTurnMemory;
3282         int size = pendingTurns.size();
3283         // std::cerr << "size of " << &pendingTurns << ": " << size << std::endl;
3284         if (size <= maxPendingTurns)
3285             pendingTurns.push_back(dir);
3286         else {
3287             if(pendingTurns.empty()) return false; //just to be sure
3288             if(pendingTurns.back() != dir) {
3289                 pendingTurns.pop_back(); //opposite turns cancel so the cycle still moves into the expected direction
3290             }
3291             else {
3292                 pendingTurns.push_back(dir); //add it anyways...
3293             }
3294         }
3295     }
3296 
3297     return false;
3298 }
3299 
3300 // *******************************************************************************************
3301 // *
3302 // *	RightBeforeDeath
3303 // *
3304 // *******************************************************************************************
3305 //!
3306 //!		@param	numTries	number of times this function will be called approximately before the cycle will be killed
3307 //!
3308 // *******************************************************************************************
3309 
RightBeforeDeath(int numTries)3310 void gCycleMovement::RightBeforeDeath( int numTries )
3311 {
3312 }
3313 
3314 // *******************************************************************************************
3315 // *
3316 // *	Die
3317 // *
3318 // *******************************************************************************************
3319 //!
3320 //!		@param	time	the time of death
3321 //!
3322 // *******************************************************************************************
3323 
Die(REAL time)3324 void gCycleMovement::Die( REAL time )
3325 {
3326     // only do something if you are alive
3327     if ( alive_ == 1 )
3328     {
3329         alive_ = -1;
3330         deathTime = time;
3331     }
3332 
3333     // or complete death if you died only recently
3334     if ( alive_ == -1 )
3335     {
3336         alive_ = 0;
3337     }
3338 }
3339 
3340 class gRecursionGuard
3341 {
3342 public:
gRecursionGuard(bool & guard)3343     gRecursionGuard( bool & guard )
3344             : guard_( guard )
3345     {
3346         guard_ = false;
3347     }
~gRecursionGuard()3348     ~gRecursionGuard()
3349     {
3350         guard_ = true;
3351     }
3352 private:
3353     bool & guard_;
3354 };
3355 
3356 // *******************************************************************************************
3357 // *
3358 // *	TimestepCore
3359 // *
3360 // *******************************************************************************************
3361 //!
3362 //!		@param	currentTime		time to simulate up to
3363 //!		@return					true if the cycle is to be killed
3364 //!
3365 // *******************************************************************************************
3366 
TimestepCore(REAL currentTime,bool calculateAcceleration)3367 bool gCycleMovement::TimestepCore( REAL currentTime, bool calculateAcceleration )
3368 {
3369     // eCoord oldpos=pos;
3370     REAL lastSpeed=verletSpeed_;
3371 
3372     REAL ts=(currentTime-lastTime);
3373 
3374     // calculate acceleration
3375     if ( calculateAcceleration )
3376         this->CalculateAcceleration();
3377 
3378     // ApplyAcceleration modifies the acceleration, so we need to back it up
3379     REAL lastAcceleration=acceleration;
3380 
3381     // calculate when the braking reservoir will run dry and simulate to that point
3382     {
3383         static bool recurse = true;
3384         if (recurse && brakingReservoir > 0 && brakeUsage > 0 && brakingReservoir - ts * brakeUsage < 0 )
3385         {
3386             gRecursionGuard guard( recurse );
3387 
3388             // calculate the time the brake will run out
3389             REAL brakeTime = lastTime + brakingReservoir/brakeUsage;
3390             if ( TimestepCore( brakeTime, false ) )
3391                 return true;
3392             AccelerationDiscontinuity();
3393             brakingReservoir = -EPS;
3394             return TimestepCore( currentTime );
3395         }
3396     }
3397 
3398     // apply acceleration
3399     if ( sg_verletIntegration.Supported() )
3400         this->ApplyAcceleration( ts );
3401 
3402     //eDebugLine::SetTimeout( 2 );
3403     //eDebugLine::SetColor(1,1,0);
3404     //eDebugLine::Draw(pos, 4, pos, 4 + 20 * ts);
3405 
3406     sg_ArchiveCoord( pos, 9 );
3407     sg_ArchiveReal( ts, 9 );
3408     sg_ArchiveReal( verletSpeed_, 9 );
3409 
3410 #ifdef DEBUG
3411     if ( ts > 2.0f )
3412     {
3413         int x;
3414         x = 0;
3415     }
3416 
3417     if ( verletSpeed_ > 30.0f )
3418     {
3419         int x;
3420         x = 0;
3421     }
3422 
3423     if ( acceleration > 100.0f )
3424     {
3425         int x;
3426         x = 0;
3427     }
3428 #endif
3429 
3430     clamp(ts, -10, 10);
3431 
3432     REAL step=verletSpeed_*ts;
3433     tASSERT(finite(step));
3434 
3435     int numTries = 0;
3436     bool emergency = false;
3437 
3438     rubberSpeedFactor = 1;
3439 
3440     // be a little nice and don't drive into the wall
3441     REAL rubber_granted, rubberEffectiveness;
3442 
3443     // get rubber values
3444     sg_RubberValues( player, verletSpeed_, rubber_granted, rubberEffectiveness );
3445 
3446     // rubber effectiveness right now
3447     rubberEffectiveness /= (1 + rubberMalus );
3448 
3449     // reduce it further if cycle turned recently
3450     {
3451         REAL delayTime = (lastTurnTimeRight_ > lastTurnTimeLeft_ ? lastTurnTimeRight_ : lastTurnTimeLeft_) + GetTurnDelay() * sg_rubberCycleDelay;
3452         if ( lastTime < delayTime )
3453         {
3454             rubberEffectiveness *= sg_rubberCycleDelayBonus;
3455 
3456             // if the target time is after the rubber delay ends...
3457             if( currentTime > delayTime )
3458             {
3459                 static bool recurse = true;
3460                 if (recurse)
3461                 {
3462                     gRecursionGuard guard( recurse );
3463 
3464                     verletSpeed_=lastSpeed;
3465                     acceleration=lastAcceleration;
3466                     // do two small timesteps
3467                     return TimestepCore( delayTime, false ) || TimestepCore( currentTime );
3468                 }
3469             }
3470         }
3471     }
3472 
3473     sg_ArchiveReal( rubberEffectiveness, 9 );
3474 
3475     tASSERT( rubber >= 0 );
3476 
3477     // TODO: solve smooth position correction trouble with rubber
3478     if ( player && ( rubber_granted > rubber || sn_GetNetState() == nCLIENT || !Vulnerable() ) && sg_rubberCycleSpeed > 0 && step > -EPS && ( sn_GetNetState() == nCLIENT || rubberEffectiveness > 0 ) )
3479     {
3480         // ignore zero effectiveness, this happens only on the client
3481         if ( rubberEffectiveness <= 0 )
3482             rubberEffectiveness = 1E+20;
3483 
3484         // formerly: rubberFactor = .5
3485         REAL beta = ts * sg_rubberCycleSpeed;
3486         REAL neededSpace = 0;
3487         REAL rubberFactor;
3488         if ( beta > .001 )
3489         {
3490             rubberFactor = 1 - exp( -beta );
3491             neededSpace = step/rubberFactor;
3492         }
3493         else
3494         {
3495             rubberFactor = beta;        // better accuracy than the full formula
3496 
3497             // a lot of factors can be cut out of this one (avoiding a division by zero for ts=0)
3498             neededSpace = verletSpeed_/sg_rubberCycleSpeed;
3499         }
3500 
3501         // rubberFactor must not be too close to 1, otherwise we get precision trouble
3502         if ( rubberFactor > .999 )
3503             rubberFactor = .999;
3504 
3505         // revert to old rubber logic if old clients are connected
3506         if ( sg_rubberCycleLegacy && !sg_nonRippable.Supported() && rubberFactor < .5f )
3507             rubberFactor = .5f;
3508 
3509         // space we need to look ahead
3510         if ( neededSpace < step*3 || ts < -EPS )
3511             neededSpace = step*3;
3512 
3513         // determine how long we can drive on
3514         // REAL space = GetMaxSpaceAhead( this, neededSpace, ts * step * rubberFactor / rubberEffectiveness, &hitInfo );
3515         REAL space = GetMaxSpaceAhead( neededSpace );
3516 
3517 #ifdef DEBUG_RUBBER
3518         if ( Player() && space < 1E+15)
3519         {
3520             std::ofstream f( Player()->GetUserName() + "_rubber", std::ios::app );
3521             f << lastTime << " " << space << "\n";
3522         }
3523 #endif
3524 
3525         // if the available space in front is less than the space needed to slow down via
3526         // the rubber brake, activate rubber and slow down
3527         if ( space < neededSpace )
3528         {
3529             // the minimal space rubber gets active at
3530             REAL rubberStartSpace = verletSpeed_/sg_rubberCycleSpeed;
3531             static bool recurse = true;
3532             if ( space > rubberStartSpace && recurse )
3533             {
3534                 // rubber will not be active immediately, simulate to the time it will
3535                 gRecursionGuard guard( recurse );
3536 
3537                 // calculate the time rubber will get active at
3538                 REAL ratio = ( space - rubberStartSpace )/step;
3539                 if ( ratio > EPS && ratio < 1 - EPS )
3540                 {
3541                     REAL rubberGetsActiveTime = lastTime + ( currentTime - lastTime ) * ratio;
3542 
3543                     verletSpeed_=lastSpeed;
3544                     acceleration=lastAcceleration;
3545                     return TimestepCore( rubberGetsActiveTime, false ) || TimestepCore( currentTime );
3546                 }
3547             }
3548 #ifdef DEDICATED
3549             else
3550             {
3551                 // see if the wall we're about to hit comes from its cycle's future. If so,
3552                 // it is a prediction wall and we shouldn't actually use rubber before we
3553                 // have to.
3554                 if ( maxSpaceHit_ && maxSpaceHit_->playerWall )
3555                 {
3556                     gPlayerWall * wall = maxSpaceHit_->playerWall;
3557 
3558                     // get the position of the hit
3559                     REAL alpha = maxSpaceHit_->wallAlpha;
3560 
3561                     // get the distance of the wall
3562                     REAL wallDist = wall->Pos( alpha );
3563                     // get the distance the cycle is simulated up to
3564                     REAL cycleDist = wall->CycleMovement()->distance;
3565                     // comparing these two gives an accurate criterion whether the wall is extrapolated
3566 
3567                     REAL minLag = se_GameTime() - lastTime - LagThreshold();
3568                     if ( cycleDist < wallDist && ( minLag < Lag() || minLag < wall->CycleMovement()->Lag() ) )
3569                     {
3570                         // it is an extrapolation wall and we are allowed to delay simulation a bit.
3571                         // so let's abort here.
3572                         verletSpeed_=lastSpeed;
3573                         acceleration=lastAcceleration;
3574 
3575                         return false;
3576                     }
3577                 }
3578             }
3579 #endif
3580 
3581             // see if the obstacle will go away during this timestep.
3582             // if it does, simulate in two steps to make the simulation more accurate.
3583             {
3584                 // get the wall
3585                 if ( maxSpaceHit_ && maxSpaceHit_->playerWall )
3586                 {
3587                     gPlayerWall * wall = maxSpaceHit_->playerWall;
3588 
3589                     // get the position of the hit
3590                     REAL alpha = maxSpaceHit_->wallAlpha;
3591 
3592                     // use binary search to find the time the wall goes away. Not
3593                     // the fastest way, but it doesn't depend on wall internals, and
3594                     // it shouldn't be called often anyway.
3595                     REAL tolerance = 0.001;
3596                     if ( !wall->IsDangerous( alpha, currentTime ) && currentTime > lastTime + tolerance )
3597                     {
3598                         // take movement speed into account, we won't hit the wall for
3599                         // another distanceOffset seconds
3600                         REAL distanceOffset = 0;
3601                         {
3602                             REAL speed = Speed();
3603                             if ( speed > 0 )
3604                                 distanceOffset = space/speed;
3605                         }
3606 
3607                         REAL minTime = lastTime + distanceOffset;
3608                         REAL maxTime = currentTime + distanceOffset;
3609                         while ( minTime + tolerance < maxTime )
3610                         {
3611                             REAL midTime = .5 * ( minTime + maxTime );
3612                             if ( wall->IsDangerous( alpha, midTime ) )
3613                                 minTime = midTime;
3614                             else
3615                                 maxTime = midTime;
3616                         }
3617 
3618                         maxTime -= distanceOffset;
3619                         // minTime -= distanceOffset;
3620 
3621                         // split simulation into two parts, one up to the point the wall turns harmless
3622                         {
3623                             static bool recurse = true;
3624                             if (recurse)
3625                             {
3626                                 gRecursionGuard guard( recurse );
3627 
3628                                 verletSpeed_=lastSpeed;
3629                                 acceleration=lastAcceleration;
3630                                 return TimestepCore( maxTime, false ) || TimestepCore( currentTime );
3631                             }
3632                         }
3633                     }
3634                 }
3635             }
3636 
3637             /*
3638             // debug output for sensitive space/time diagrams
3639             static REAL lastTimePrinted = 0;
3640             if ( currentTime > lastTimePrinted && Player() )
3641             {
3642                 lastTimePrinted = currentTime;
3643                 std::ofstream f( Player()->GetUserName() + "_space", std::ios::app );
3644                 f << lastTime << " " << log(space) << "\n";
3645             }
3646             */
3647 
3648             // notify AIs of it
3649             emergency = true;
3650 
3651             // calculate the step the rubber code should do based on the decay factor
3652             // calculated earler
3653             REAL rubberStep = space * rubberFactor;
3654             if ( rubberStep > step )
3655                 rubberStep = step;
3656 
3657             // clamp the step
3658             if (step<0)
3659                 step=0;
3660 
3661             // calculate the amount of rubber needed for the desired brake effect
3662             REAL rubberneeded = step - rubberStep;
3663             if (rubberneeded < 0)
3664                 rubberneeded = 0;
3665 
3666             // clamp rubberneeded to the amout of rubber available
3667             REAL rubberAvailable = ( rubber_granted - rubber ) * rubberEffectiveness;
3668             if ( sn_GetNetState() != nCLIENT && rubberneeded > rubberAvailable && Vulnerable() )
3669             {
3670                 // rubber will run out this frame.
3671                 // split simulation into two parts, one up to the point rubber runs out
3672                 {
3673                     REAL ratio = rubberAvailable/rubberneeded;
3674 
3675                     if ( ratio > .01 && ratio < .99 && currentTime - lastTime > .001 )
3676                     {
3677                         REAL runOutTime = lastTime + ( currentTime - lastTime ) * ratio;
3678                         static bool recurse = true;
3679                         if (recurse)
3680                         {
3681                             gRecursionGuard guard( recurse );
3682                             // need many attempts
3683                             verletSpeed_=lastSpeed;
3684                             acceleration=lastAcceleration;
3685                             return TimestepCore( runOutTime, false ) || TimestepCore( currentTime );
3686                         }
3687                     }
3688                 }
3689 
3690                 rubberneeded = rubberAvailable;
3691             }
3692 
3693             // update rubber usage
3694             rubber += rubberneeded / rubberEffectiveness;
3695 
3696             numTries = int((sg_rubberCycleTime * ( rubber_granted - rubber ) - 1 )/(sg_rubberCycleTime * step*1.5 + 1));
3697             int numTriesSpace = int(space*10/verletSpeed_);
3698             if ( numTriesSpace < numTries )
3699                 numTriesSpace = 0;
3700 
3701             if ( step > 0 )
3702                 rubberSpeedFactor = 1 - rubberneeded/step;
3703             else
3704                 // better algorithm for zero steps
3705                 rubberSpeedFactor = space / neededSpace;
3706 
3707             // clamp
3708             if ( rubberSpeedFactor < 0 )
3709                 rubberSpeedFactor = 0;
3710 
3711             // correct the step to take, don't go backwards.
3712             step -= rubberneeded;
3713             if (step<0)
3714                 step=0;
3715 
3716             //{
3717             //    rubber+=step;
3718             //    step=0;
3719             //}
3720         }
3721     }
3722 
3723     tASSERT( rubber >= 0 );
3724 
3725     sg_ArchiveReal( step, 9 );
3726 
3727     // move forward
3728     eCoord nextpos;
3729     if ( verletSpeed_ >0 )
3730         nextpos=pos+dirDrive*step;
3731     else
3732         nextpos=pos;
3733 
3734     eCoord lastPos = pos;
3735     tJUST_CONTROLLED_PTR< eFace > lastFace = currentFace;
3736     try
3737     {
3738 #ifdef DEBUG
3739         static int run = 0;
3740         run++;
3741         if ( run == -1 )
3742         {
3743             st_Breakpoint();
3744         }
3745 #endif
3746         Move(nextpos,lastTime,currentTime);
3747 #ifdef DEBUG
3748         {
3749             if ( step > 0 && ( nextpos - pos ).NormSquared() > 1 )
3750             {
3751                 con << "Wrong move! run = " << run << ", nextpos = " << nextpos << ", pos = " << pos << "\n";
3752             }
3753         }
3754 #endif
3755 
3756         tASSERT(finite(distance));
3757         tASSERT(finite(step));
3758         distance += step;
3759         lastTimeAlive_ = currentTime;
3760     }
3761     catch ( gCycleStop const & )
3762     {
3763         // undo simulation done so far and stop
3764         pos = lastPos;
3765         verletSpeed_ = lastSpeed;
3766         acceleration = lastAcceleration;
3767         currentFace = lastFace;
3768         numTries = 0;
3769 
3770         // don't simulate further
3771         return false;
3772     }
3773     catch ( gCycleDeath const & )
3774     {
3775         rubberSpeedFactor = 0;
3776 
3777         // the cycle should die in this movement. Prevent it if there is rubber left.
3778         // if RUBBER_MINDISTANCE is negative and the player is not an AI, the cycle dies anyway.
3779         if ( rubberEffectiveness <= 0 || step >= (rubber_granted-rubber)*rubberEffectiveness || ( sg_rubberCycleMinDistance < 0 && Player() && Player()->IsHuman() ) )
3780         {
3781             // last survival chance: packet loss protection. Determine whether it should be in effect..
3782             bool toleratePacketLoss = false;
3783             if (!currentDestination)
3784             {
3785                 // calculate time tolerance to capture packet loss...
3786                 REAL tolerance = Lag() * sg_packetLossTolerance;
3787 
3788                 // add lag credit on top of that
3789                 if ( Owner() > 0 )
3790                     tolerance += eLag::Credit( Owner() );
3791 
3792                 // add lag fluctuation to the mix
3793                 if ( sn_GetNetState() == nSERVER && player && player->Owner() != 0 )
3794                 {
3795                     REAL varianceTolerance = 2 * sqrtf( sn_Connections[ player->Owner() ].ping.GetSnailAverager().GetDataVariance() );
3796                     // clamp it, high fluctuations are the player's own problem
3797                     if ( varianceTolerance > tolerance )
3798                         varianceTolerance = tolerance;
3799                     tolerance += varianceTolerance;
3800                 }
3801 
3802                 // if time has not progressed beyond tolerance, protection may be in effect
3803                 toleratePacketLoss = ( se_GameTime() - Lag() - lastTimeAlive_ < tolerance );
3804             }
3805 
3806             // ... and apply it.
3807             if ( toleratePacketLoss )
3808             {
3809                 pos = lastPos;
3810                 verletSpeed_ = lastSpeed;
3811                 acceleration = lastAcceleration;
3812                 currentFace = lastFace;
3813                 numTries = 0;
3814                 emergency = true;
3815 
3816                 // don't simulate further
3817                 return false;
3818             }
3819             else
3820             {
3821                 // no, no straw left. Rethrow and get killed.
3822                 rubber = rubber_granted;
3823 
3824                 // update distance to include the really covered space
3825                 tASSERT(finite(distance));
3826                 distance += eCoord::F( dirDrive, pos - lastPos )/dirDrive.NormSquared();
3827                 tASSERT(finite(distance));
3828 
3829                 throw;
3830             }
3831         }
3832         else
3833         {
3834             pos = lastPos;
3835             currentFace = lastFace;
3836             rubber += step/rubberEffectiveness;
3837             if ( rubber < 0 )
3838                 rubber = 0;
3839 
3840             numTries = 0;
3841             emergency = true;
3842         }
3843     }
3844 
3845     tASSERT( rubber >= 0 );
3846 
3847     // use up rubber from tunneling (calculated by CalculateAcceleration
3848     if ( rubberEffectiveness > 0 )
3849     {
3850         rubber += rubberUsage * ts * verletSpeed_ / rubberEffectiveness;            }
3851     else if ( rubberUsage > 0 )
3852     {
3853         rubber = rubber_granted + 10;
3854     }
3855     rubberUsage = 0;
3856 
3857     // decide over kill
3858     bool rubberUsedUp = false;
3859     if ( rubber > rubber_granted || ( sg_cycleWidthRubberMax == 0 && sg_cycleWidthRubberMin == 0 ) )
3860     {
3861         if ( sn_GetNetState() != nCLIENT )
3862         {
3863             throw gCycleDeath( pos );
3864         }
3865         else
3866         {
3867             rubber = rubber_granted;
3868             rubberUsedUp = true;
3869         }
3870     }
3871 
3872     // use up brake
3873     brakingReservoir -= brakeUsage * ts;
3874     clamp( brakingReservoir, 0, 1 );
3875 
3876     // let rubber decay
3877     if ( sg_rubberCycleTime > 0 )
3878         rubber /= (1+ts/sg_rubberCycleTime);
3879     else
3880         rubber = 0;
3881 
3882     // let rubber decay
3883     if ( sg_rubberCycleMalusTime > 0 )
3884         rubberMalus /= (1+ts/sg_rubberCycleMalusTime);
3885     else
3886         rubberMalus = 0;
3887 
3888 
3889     // clamp rubber ( mostly for client side HUD display )
3890     if ( rubber >= rubber_granted )
3891     {
3892         rubber = rubber_granted;
3893         rubberUsedUp = true;
3894     }
3895 
3896     // record time rubber went out
3897     if( rubberUsedUp )
3898     {
3899         if ( rubberDepleteTime_ <= 0 )
3900         {
3901             rubberDepleteTime_ = lastTime;
3902         }
3903     }
3904     else
3905     {
3906         rubberDepleteTime_ = 0;
3907     }
3908 
3909     lastTime=currentTime;
3910 
3911     // give the AI a chance to evade just in time
3912     if (emergency)
3913     {
3914         RightBeforeDeath(numTries);
3915     }
3916 
3917 #ifdef DEBUGOUTPUT
3918     if ( sg_cycleDebugPrintLevel > 1 )
3919         con << Player()->GetName() << " moved " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
3920 #endif
3921 
3922     /*
3923     // debug output for sensitive rubber/time diagrams
3924     static REAL lastTimePrinted = 0;
3925     if ( currentTime > lastTimePrinted && Player() )
3926     {
3927         lastTimePrinted = currentTime;
3928         std::ofstream f( Player()->GetUserName() + "_rubber", std::ios::app );
3929         f << currentTime << " " << rubber << "\n";
3930     }
3931     */
3932 
3933     // apply acceleration
3934     if ( !sg_verletIntegration.Supported() )
3935         this->ApplyAcceleration( ts );
3936 
3937     tASSERT(finite(distance));
3938 
3939     tASSERT( rubber >= 0 );
3940 
3941     // call base timestep
3942     return eNetGameObject::Timestep(currentTime);
3943 }
3944 
3945 // *******************************************************************************************
3946 // *
3947 // *	MyInitAfterCreation
3948 // *
3949 // *******************************************************************************************
3950 //!
3951 //!
3952 // *******************************************************************************************
3953 
MyInitAfterCreation(void)3954 void gCycleMovement::MyInitAfterCreation( void )
3955 {
3956 #ifdef DEBUG
3957     // con << "creating cycle.\n";
3958 #endif
3959     brakingReservoir = 1.0f;
3960     rubberDepleteTime_ = 0.0f;
3961 
3962     braking = false;
3963 
3964     acceleration = 0;
3965 
3966     refreshSpaceAhead_ = true;
3967     maxSpaceMaxCast_ = 0.0;
3968     maxSpaceHit_ = NULL;
3969 
3970     dir=dirDrive;
3971     lastDirDrive=dirDrive;
3972     lastTurnPos_=pos;
3973 
3974     distance=0;
3975     // wallContDistance = 5;
3976     rubber=0.0f;
3977     rubberMalus=0.0f;
3978     rubberSpeedFactor=1.0f;
3979 
3980     gap_[0] = gap_[1] = 1E+30;
3981     keepLookingForGap_[0] = keepLookingForGap_[1] = true;
3982 
3983     alive_ = 1;
3984 
3985     z=.75;
3986 
3987     turns=1;
3988     // pendingTurns.resize(0); //clear it
3989     lastTurnTimeRight_ = lastTurnTimeLeft_=lastTime-10;
3990 
3991     lastTimeAlive_ = lastTime;
3992 
3993     if (!finite(verletSpeed_)){
3994         st_Breakpoint();
3995         verletSpeed_ = 1;
3996     }
3997 
3998     if (verletSpeed_ < .1)
3999         verletSpeed_=.1;
4000 
4001 #ifdef DEBUGOUTPUT
4002     if ( sg_cycleDebugPrintLevel > 0 )
4003         con << Player()->GetName() << " created " << pos << "," << dirDrive << " " << tSysTimeFloat() << "\n";
4004 #endif
4005 }
4006 
4007 // *******************************************************************************************
4008 // *
4009 // *	Init_gCycleCore
4010 // *
4011 // *******************************************************************************************
4012 //!
4013 //!
4014 // *******************************************************************************************
4015 
4016 //void gCycleMovement::Init_gCycleCore( void )
4017 //{
4018 //    assert(0); // implement me
4019 //}
4020 
4021 // *******************************************************************************************
4022 // *
4023 // *	Finit_gCycleCore
4024 // *
4025 // *******************************************************************************************
4026 //!
4027 //!
4028 // *******************************************************************************************
4029 
4030 //void gCycleMovement::Finit_gCycleCore( void )
4031 //{
4032 //    assert(0); // implement me
4033 //}
4034 
4035 // *******************************************************************************************
4036 // *
4037 // *	gCycleMovement
4038 // *
4039 // *******************************************************************************************
4040 //!
4041 //!
4042 // *******************************************************************************************
4043 
4044 //gCycleMovement::gCycleMovement( void )
4045 //{
4046 //    assert(0); // implement me
4047 //}
4048 
4049 // *******************************************************************************************
4050 // *
4051 // *	gCycleMovement
4052 // *
4053 // *******************************************************************************************
4054 //!
4055 //!		@param	other
4056 //!
4057 // *******************************************************************************************
4058 
4059 //gCycleMovement::gCycleMovement( gCycleMovement const & other )
4060 //{
4061 //    assert(0); // implement me
4062 //}
4063 
4064 // *******************************************************************************************
4065 // *
4066 // *	operator =
4067 // *
4068 // *******************************************************************************************
4069 //!
4070 //!		@param	other
4071 //!		@return
4072 //!
4073 // *******************************************************************************************
4074 
4075 //gCycleMovement & gCycleMovement::operator =( gCycleMovement const & other )
4076 //{
4077 //    assert(0); // implement me
4078 //    return gCycleMovement();
4079 //}
4080 
4081 // *******************************************************************************************
4082 // *
4083 // *	CopyFrom
4084 // *
4085 // *******************************************************************************************
4086 //!
4087 //!		@param	other
4088 //!
4089 // *******************************************************************************************
4090 
4091 //void gCycleMovement::CopyFrom( const gCycleMovement & other )
4092 //{
4093 //   assert(0); // implement me
4094 //}
4095 
4096 // *******************************************************************************************
4097 // *
4098 // *	DoGetDistanceSinceLastTurn
4099 // *
4100 // *******************************************************************************************
4101 //!
4102 //!		@return		the distance driven since the last turn
4103 //!
4104 // *******************************************************************************************
4105 
DoGetDistanceSinceLastTurn(void) const4106 REAL gCycleMovement::DoGetDistanceSinceLastTurn( void ) const
4107 {
4108     return eCoord::F( dirDrive, pos - lastTurnPos_ )/dirDrive.NormSquared();
4109 }
4110 
4111 // *******************************************************************************************
4112 // *
4113 // *	RubberMalusActive
4114 // *
4115 // *******************************************************************************************
4116 //!
4117 //!		@return
4118 //!
4119 // *******************************************************************************************
4120 
RubberMalusActive(void)4121 bool gCycleMovement::RubberMalusActive( void )
4122 {
4123     return sg_rubberCycleMalusTurn > 0;
4124 }
4125 
4126 // *******************************************************************************************
4127 // *
4128 // *	MoveSafely
4129 // *
4130 // *******************************************************************************************
4131 //!
4132 //!		@param	dest	   the destination position
4133 //!		@param	startTime  the start time of the movement
4134 //!		@param	endTime	   the end time of the movement
4135 //!
4136 // *******************************************************************************************
4137 
MoveSafely(const eCoord & dest,REAL startTime,REAL endTime)4138 void gCycleMovement::MoveSafely( const eCoord & dest, REAL startTime, REAL endTime )
4139 {
4140     static bool recursing = false;
4141     if ( !recursing )
4142     {
4143         recursing = true;
4144         try
4145         {
4146             // try a regular move
4147             Move( dest, startTime, endTime );
4148         }
4149         catch( eDeath & death )
4150         {
4151             // and play dead if that doesn't work right
4152             short lastAlive = alive_;
4153             alive_ = 0;
4154             Move( dest, startTime, endTime );
4155             alive_ = lastAlive;
4156         }
4157     }
4158     else
4159     {
4160         // play dead if another safe move is already in process. Sometimes,
4161         // crossing a wall of a live cycle causes that cycle to be moved with
4162         // this function, and "killing" it temporarily avoids an endless
4163         // recursion in that case.
4164         short lastAlive = alive_;
4165         alive_ = 0;
4166         Move( dest, startTime, endTime );
4167         alive_ = lastAlive;
4168     }
4169 }
4170 
GetTurnSpeedFactor(void)4171 REAL GetTurnSpeedFactor(void) {
4172     return sg_cycleTurnSpeedFactor;
4173 }
4174 
4175 // *******************************************************************************
4176 // *
4177 // *	NextInterestingTime
4178 // *
4179 // *******************************************************************************
4180 //!
4181 //!		@return
4182 //!
4183 // *******************************************************************************
4184 
NextInterestingTime(void) const4185 REAL gCycleMovement::NextInterestingTime( void ) const
4186 {
4187     // default to the last time
4188     REAL ret = LastTime();
4189 
4190     // look for a later destination
4191     gDestination * run = currentDestination;
4192     while ( run )
4193     {
4194         REAL time = run->GetGameTime();
4195         if ( time > ret )
4196             ret = time;
4197         run = run->next;
4198     }
4199 
4200     return ret;
4201 }
4202 
4203 
4204