~swag/armagetronad/0.2.9-sty+ct+ap-fork

« back to all changes in this revision

Viewing changes to src/tron/gCycle.cpp

  • Committer: luke-jr
  • Date: 2006-05-29 01:55:42 UTC
  • Revision ID: svn-v3-list-QlpoOTFBWSZTWZvbKhsAAAdRgAAQABK6798QIABURMgAAaeoNT1TxT1DQbKaeobXKiyAmlWT7Y5MkdJOtXDtB7w7DOGFBHiOBxaUIu7HQyyQSvxdyRThQkJvbKhs:7d95bf1e-0414-0410-9756-b78462a59f44:armagetronad%2Fbranches%2F0.2.8%2Farmagetronad:4612
Unify tags/branches of modules released together

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
*************************************************************************
 
4
 
 
5
ArmageTron -- Just another Tron Lightcycle Game in 3D.
 
6
Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
 
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 "gCycle.h"
 
29
#include "nConfig.h"
 
30
#include "rModel.h"
 
31
//#include "eTess.h"
 
32
#include "eGrid.h"
 
33
#include "rTexture.h"
 
34
#include "eTimer.h"
 
35
#include "tInitExit.h"
 
36
#include "tRecorder.h"
 
37
#include "rScreen.h"
 
38
#include "rFont.h"
 
39
#include "gSensor.h"
 
40
#include "ePlayer.h"
 
41
#include "eSound.h"
 
42
#include "eGrid.h"
 
43
#include "eFloor.h"
 
44
#include "gSparks.h"
 
45
#include "gExplosion.h"
 
46
#include "gWall.h"
 
47
#include "nKrawall.h"
 
48
#include "gAIBase.h"
 
49
#include "eDebugLine.h"
 
50
#include "gArena.h"
 
51
 
 
52
#include "tMath.h"
 
53
#include <stdlib.h>
 
54
#include <fstream>
 
55
 
 
56
#ifndef DEDICATED
 
57
#define DONTDOIT
 
58
#include "rRender.h"
 
59
#endif
 
60
 
 
61
// TODO: get rid of this
 
62
#include "tDirectories.h"
 
63
 
 
64
bool sg_gnuplotDebug = false;
 
65
 
 
66
#ifdef DEBUG
 
67
static tSettingItem<bool> sg_("DEBUG_GNUPLOT",sg_gnuplotDebug);
 
68
#endif
 
69
 
 
70
//  *****************************************************************
 
71
 
 
72
static nNOInitialisator<gCycle> cycle_init(320,"cycle");
 
73
 
 
74
//  *****************************************************************
 
75
 
 
76
// static nVersionFeature sg_DoubleSpeed( 1 );
 
77
 
 
78
//tCONTROLLED_PTR(ePlayerNetID)   lastEnemyInfluence;   // the last enemy wall we encountered
 
79
//REAL                                                  lastTime;                               // the time it was drawn at
 
80
bool headlights=1;
 
81
bool cycleprograminited=0;
 
82
 
 
83
// enemy influence settings
 
84
static REAL sg_enemyChatbotTimePenalty = 30.0f;   //!< penalty for victim in chatbot mode
 
85
static REAL sg_enemyFriendTimePenalty = 2500.0f;  //!< penalty for teammate influence
 
86
// REAL sg_enemySelfTimePenalty = 5000.0f;    //!< penalty for self influence
 
87
static REAL sg_enemyDeadTimePenalty = 0.0f;       //!< penalty for influence from dead players
 
88
static REAL sg_suicideTimeout = 10000.0f;         //!< influences older than this don't count as kill cause
 
89
static REAL sg_enemyCurrentTimeInfluence = 0.0f;  //!< blends in the current time into the relevant time
 
90
 
 
91
static tSettingItem<REAL> sg_enemyChatbotTimePenaltyConf( "ENEMY_CHATBOT_PENALTY", sg_enemyChatbotTimePenalty );
 
92
static tSettingItem<REAL> sg_enemyFriendTimePenaltyConf( "ENEMY_TEAMMATE_PENALTY", sg_enemyFriendTimePenalty );
 
93
static tSettingItem<REAL> sg_enemyDeadTimePenaltyConf( "ENEMY_DEAD_PENALTY", sg_enemyDeadTimePenalty );
 
94
static tSettingItem<REAL> sg_suicideTimeoutConf( "ENEMY_SUICIDE_TIMEOUT", sg_suicideTimeout );
 
95
static tSettingItem<REAL> sg_enemyCurrentTimeInfluenceConf( "ENEMY_CURRENTTIME_INFLUENCE", sg_enemyCurrentTimeInfluence );
 
96
 
 
97
// the last enemy possibly responsible for our death
 
98
const ePlayerNetID* gEnemyInfluence::GetEnemy() const
 
99
{
 
100
    return lastEnemyInfluence.GetPointer();
 
101
}
 
102
 
 
103
REAL gEnemyInfluence::GetTime() const
 
104
{
 
105
    return lastTime;
 
106
}
 
107
 
 
108
gEnemyInfluence::gEnemyInfluence()
 
109
{
 
110
    lastTime = -sg_suicideTimeout;
 
111
}
 
112
 
 
113
// add the result of the sensor scan to our data
 
114
void gEnemyInfluence::AddSensor( const gSensor& sensor, REAL timePenalty, gCycle * thisCycle )
 
115
{
 
116
    // the client has no need for this, it does not execute AI code
 
117
    if ( sn_GetNetState() == nCLIENT )
 
118
        return;
 
119
 
 
120
    // check if the sensor hit an enemy wall
 
121
    // if ( sensor.type != gSENSOR_ENEMY )
 
122
    //    return;
 
123
 
 
124
    // get the wall
 
125
    if ( !sensor.ehit )
 
126
        return;
 
127
 
 
128
    eWall* wall = sensor.ehit->GetWall();
 
129
    if ( !wall )
 
130
        return;
 
131
 
 
132
    AddWall( wall, sensor.before_hit, timePenalty, thisCycle );
 
133
}
 
134
 
 
135
// add the interaction with a wall to our data
 
136
void gEnemyInfluence::AddWall( const eWall * wall, eCoord const & pos, REAL timePenalty, gCycle * thisCycle )
 
137
{
 
138
    // the client has no need for this, it does not execute AI code
 
139
    if ( sn_GetNetState() == nCLIENT )
 
140
        return;
 
141
 
 
142
    // see if it is a player wall
 
143
    gPlayerWall const * playerWall = dynamic_cast<gPlayerWall const *>( wall );
 
144
    if ( !playerWall )
 
145
        return;
 
146
 
 
147
    // get the approximate time the wall was drawn
 
148
    REAL alpha = .5f;
 
149
    // try to get a more accurate value
 
150
    if ( playerWall->Edge() )
 
151
    {
 
152
        // get the position of the collision point
 
153
        alpha = playerWall->Edge()->Ratio( pos );
 
154
    }
 
155
    REAL timeBuilt = playerWall->Time( 0.5f );
 
156
 
 
157
    AddWall( playerWall, timeBuilt - timePenalty, thisCycle );
 
158
}
 
159
 
 
160
// add the interaction with a wall to our data
 
161
void gEnemyInfluence::AddWall( const gPlayerWall * wall, REAL timeBuilt, gCycle * thisCycle )
 
162
{
 
163
    // the client has no need for this, it does not execute AI code
 
164
    if ( sn_GetNetState() == nCLIENT )
 
165
        return;
 
166
 
 
167
    if ( !wall )
 
168
        return;
 
169
 
 
170
    // get the cycle
 
171
    gCycle *cycle = wall->Cycle();
 
172
    if ( !cycle )
 
173
        return;
 
174
 
 
175
    // don't count self influence
 
176
    if ( thisCycle == cycle )
 
177
        return;
 
178
 
 
179
    REAL time = timeBuilt;
 
180
    if ( thisCycle )
 
181
    {
 
182
        REAL currentTime = thisCycle->LastTime();
 
183
        time += ( currentTime - time ) * sg_enemyCurrentTimeInfluence;
 
184
    }
 
185
 
 
186
    // get the player
 
187
    ePlayerNetID* player = cycle->Player();
 
188
    if ( !player )
 
189
        return;
 
190
 
 
191
    // don't accept milkers.
 
192
    if ( thisCycle && !ePlayerNetID::Enemies( thisCycle->Player(), player ) )
 
193
    {
 
194
        return;
 
195
    }
 
196
 
 
197
    // if the player is not our enemy, add extra time penalty
 
198
    if ( thisCycle->Player() && player->CurrentTeam() == thisCycle->Player()->CurrentTeam() )
 
199
    {
 
200
        // the time shall be at most the time of the last turn, it should count as suicide if
 
201
        // I drive into your three mile long wall
 
202
        if ( time > cycle->GetLastTurnTime() )
 
203
            time = cycle->GetLastTurnTime();
 
204
        time -= sg_enemyFriendTimePenalty;
 
205
    }
 
206
    const ePlayerNetID* pInfluence = this->lastEnemyInfluence.GetPointer();
 
207
 
 
208
    // calculate effective last time. Add malus if the player is dead.
 
209
    REAL lastEffectiveTime = lastTime;
 
210
    if ( !pInfluence  || !pInfluence->Object() ||  !pInfluence->Object()->Alive() )
 
211
    {
 
212
        lastEffectiveTime -= sg_enemyDeadTimePenalty;
 
213
    }
 
214
 
 
215
    // same for the current influence
 
216
    REAL effectiveTime = time;
 
217
    if ( !cycle->Alive() )
 
218
    {
 
219
        effectiveTime -= sg_enemyDeadTimePenalty;
 
220
    }
 
221
 
 
222
    // if the new influence is newer, take it.
 
223
    if ( effectiveTime > lastEffectiveTime || !bool(lastEnemyInfluence) )
 
224
    {
 
225
        lastEnemyInfluence = player;
 
226
        lastTime                   = time;
 
227
    }
 
228
}
 
229
 
 
230
static float sg_cycleSyncSmoothTime = .1f;
 
231
static tSettingItem<float> conf_smoothTime ("CYCLE_SMOOTH_TIME", sg_cycleSyncSmoothTime);
 
232
 
 
233
static float sg_cycleSyncSmoothMinSpeed = .2f;
 
234
static tSettingItem<float> conf_smoothMinSpeed ("CYCLE_SMOOTH_MIN_SPEED", sg_cycleSyncSmoothMinSpeed);
 
235
 
 
236
static float sg_cycleSyncSmoothThreshold = .2f;
 
237
static tSettingItem<float> conf_smoothThreshold ("CYCLE_SMOOTH_THRESHOLD", sg_cycleSyncSmoothThreshold);
 
238
 
 
239
static inline void clamp(REAL &c, REAL min, REAL max){
 
240
    tASSERT(min < max);
 
241
 
 
242
    if (!finite(c))
 
243
        c = 0;
 
244
 
 
245
    if (c<min)
 
246
        c = min;
 
247
 
 
248
    if (c>max)
 
249
        c = max;
 
250
}
 
251
 
 
252
 
 
253
REAL            gCycle::wallsStayUpDelay=8.0f;  // the time the cycle walls stay up ( negative values: they stay up forever )
 
254
 
 
255
REAL            gCycle::wallsLength=-1.0f;              // the maximum total length of the walls
 
256
REAL            gCycle::explosionRadius=4.0f;   // the radius of the holes blewn in by an explosion
 
257
 
 
258
static          nSettingItem<REAL> *c_pwsud = NULL, *c_pwl = NULL, *c_per = NULL;
 
259
 
 
260
void gCycle::PrivateSettings()
 
261
{
 
262
    static nSettingItem<REAL> c_wsud("CYCLE_WALLS_STAY_UP_DELAY",wallsStayUpDelay);
 
263
    static nSettingItem<REAL> c_wl("CYCLE_WALLS_LENGTH",wallsLength);
 
264
    static nSettingItem<REAL> c_er("CYCLE_EXPLOSION_RADIUS",explosionRadius);
 
265
 
 
266
    c_pwsud=&c_wsud;
 
267
    c_pwl  =&c_wl;
 
268
    c_per  =&c_er;
 
269
}
 
270
 
 
271
// sound speed divisor
 
272
static REAL sg_speedCycleSound=15;
 
273
static nSettingItem<REAL> c_ss("CYCLE_SOUND_SPEED",
 
274
                               sg_speedCycleSound);
 
275
 
 
276
// time after spawning it takes the cycle to start building a wall
 
277
static REAL sg_cycleWallTime=0.0;
 
278
static nSettingItemWatched<REAL>
 
279
sg_cycleWallTimeConf("CYCLE_WALL_TIME",
 
280
                     sg_cycleWallTime,
 
281
                     nConfItemVersionWatcher::Group_Bumpy,
 
282
                     12);
 
283
 
 
284
// time after spawning during which a cycle can't be killed
 
285
static REAL sg_cycleInvulnerableTime=0.0;
 
286
static nSettingItemWatched<REAL>
 
287
sg_cycleInvulnerableTimeConf("CYCLE_INVULNERABLE_TIME",
 
288
                             sg_cycleInvulnerableTime,
 
289
                             nConfItemVersionWatcher::Group_Bumpy,
 
290
                             12);
 
291
 
 
292
// time after spawning during which a cycle can't be killed
 
293
static bool sg_cycleFirstSpawnProtection=false;
 
294
static nSettingItemWatched<bool>
 
295
sg_cycleFirstSpawnProtectionConf("CYCLE_FIRST_SPAWN_PROTECTION",
 
296
                                 sg_cycleFirstSpawnProtection,
 
297
                                 nConfItemVersionWatcher::Group_Bumpy,
 
298
                                 12);
 
299
 
 
300
// time intevals between server-client syncs
 
301
static REAL sg_syncIntervalEnemy=1;
 
302
static tSettingItem<REAL> c_sie( "CYCLE_SYNC_INTERVAL_ENEMY",
 
303
                                 sg_syncIntervalEnemy );
 
304
 
 
305
static REAL sg_syncIntervalSelf=.1;
 
306
static tSettingItem<REAL> c_sis( "CYCLE_SYNC_INTERVAL_SELF",
 
307
                                 sg_syncIntervalSelf );
 
308
 
 
309
// flag controlling case-by-case-niceness for older clients
 
310
static bool sg_avoidBadOldClientSync=true;
 
311
static tSettingItem<bool> c_sbs( "CYCLE_AVOID_OLDCLIENT_BAD_SYNC",
 
312
                                 sg_avoidBadOldClientSync );
 
313
 
 
314
// fast forward factor for extrapolating sync
 
315
static REAL sg_syncFF=10;
 
316
static tSettingItem<REAL> c_sff( "CYCLE_SYNC_FF",
 
317
                                 sg_syncFF );
 
318
 
 
319
static int sg_syncFFSteps=1;
 
320
static tSettingItem<int> c_sffs( "CYCLE_SYNC_FF_STEPS",
 
321
                                 sg_syncFFSteps );
 
322
 
 
323
// client side bugfix: local tunneling is avoided on syncs
 
324
static nVersionFeature sg_NoLocalTunnelOnSync( 4 );
 
325
 
 
326
static REAL sg_GetSyncIntervalSelf( gCycle* cycle )
 
327
{
 
328
    if ( sg_NoLocalTunnelOnSync.Supported( cycle->Owner() ) )
 
329
        return sg_syncIntervalSelf;
 
330
    else
 
331
        return sg_syncIntervalEnemy * 10;
 
332
}
 
333
 
 
334
// moviepack hack
 
335
//static bool moviepack_hack=false;       // do we use it?
 
336
//static tSettingItem<bool> ump("MOVIEPACK_HACK",moviepack_hack);
 
337
 
 
338
 
 
339
 
 
340
static int score_die=-2;
 
341
static tSettingItem<int> s_d("SCORE_DIE",score_die);
 
342
 
 
343
static int score_kill=3;
 
344
static tSettingItem<int> s_k("SCORE_KILL",score_kill);
 
345
 
 
346
static int score_suicide=-4;
 
347
static tSettingItem<int> s_s("SCORE_SUICIDE",score_suicide);
 
348
 
 
349
// input control
 
350
 
 
351
uActionPlayer gCycle::s_brake("CYCLE_BRAKE", -5);
 
352
static uActionPlayer s_brakeToggle("CYCLE_BRAKE_TOGGLE", -5);
 
353
 
 
354
static eWavData cycle_run("moviesounds/engine.wav","sound/cyclrun.wav");
 
355
static eWavData turn_wav("moviesounds/cycturn.wav","sound/expl.wav");
 
356
static eWavData scrap("sound/expl.wav");
 
357
 
 
358
// a class of textures where the transparent part of the
 
359
// image is replaced by the player color
 
360
class gTextureCycle: public rSurfaceTexture
 
361
{
 
362
    gRealColor color_; // player color
 
363
    bool wheel; // wheel or body
 
364
public:
 
365
    gTextureCycle(rSurface const & surface, const gRealColor& color,bool repx=0,bool repy=0,bool wheel=false);
 
366
 
 
367
    virtual void ProcessImage(SDL_Surface *im);
 
368
 
 
369
    virtual void OnSelect(bool enforce);
 
370
};
 
371
 
 
372
gTextureCycle::gTextureCycle(rSurface const & surface, const gRealColor& color,bool repx,bool repy,bool w)
 
373
        :rSurfaceTexture(rTextureGroups::TEX_OBJ,surface,repx,repy),
 
374
        color_(color),wheel(w)
 
375
{
 
376
    Select();
 
377
}
 
378
 
 
379
void gTextureCycle::ProcessImage(SDL_Surface *im)
 
380
{
 
381
#ifndef DEDICATED
 
382
    // blend transparent texture parts with cycle color
 
383
    tVERIFY(im->format->BytesPerPixel == 4);
 
384
    GLubyte R=int(color_.r*255);
 
385
    GLubyte G=int(color_.g*255);
 
386
    GLubyte B=int(color_.b*255);
 
387
 
 
388
    GLubyte *pixels =reinterpret_cast<GLubyte *>(im->pixels);
 
389
 
 
390
    for(int i=im->w*im->h-1;i>=0;i--){
 
391
        GLubyte alpha=pixels[4*i+3];
 
392
        pixels[4*i  ] = (alpha * pixels[4*i  ] + (255-alpha)*R) >> 8;
 
393
        pixels[4*i+1] = (alpha * pixels[4*i+1] + (255-alpha)*G) >> 8;
 
394
        pixels[4*i+2] = (alpha * pixels[4*i+2] + (255-alpha)*B) >> 8;
 
395
        pixels[4*i+3] = 255;
 
396
    }
 
397
#endif
 
398
}
 
399
 
 
400
void gTextureCycle::OnSelect(bool enforce){
 
401
#ifndef DEDICATED
 
402
    rISurfaceTexture::OnSelect(enforce);
 
403
 
 
404
    if(rTextureGroups::TextureMode[rTextureGroups::TEX_OBJ]<0){
 
405
        REAL R=color_.r,G=color_.g,B=color_.b;
 
406
        if(wheel){
 
407
            R*=.7;
 
408
            G*=.7;
 
409
            B*=.7;
 
410
        }
 
411
        glColor3f(R,G,B);
 
412
        GLfloat color[4]={R,G,B,1};
 
413
 
 
414
        glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,color);
 
415
        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color);
 
416
    }
 
417
#endif
 
418
}
 
419
 
 
420
 
 
421
//  *****************************************************************
 
422
 
 
423
static void sg_ArchiveCoord( eCoord & coord, int level )
 
424
{
 
425
    static char const * section = "_COORD";
 
426
    tRecorderSync< eCoord >::Archive( section, level, coord );
 
427
}
 
428
 
 
429
static void sg_ArchiveReal( REAL & real, int level )
 
430
{
 
431
    static char const * section = "_REAL";
 
432
    tRecorderSync< REAL >::Archive( section, level, real );
 
433
}
 
434
 
 
435
//  *****************************************************************
 
436
 
 
437
 
 
438
 
 
439
//  *****************************************************************
 
440
 
 
441
 
 
442
// take pos,dir and time from a cycle
 
443
gDestination::gDestination(const gCycleMovement &c)
 
444
        :chatting(false)
 
445
        ,turns(0)
 
446
        ,hasBeenUsed(false)
 
447
        ,messageID(1)
 
448
        ,missable(true)
 
449
        ,next(NULL)
 
450
        ,list(NULL)
 
451
{
 
452
    CopyFrom( c );
 
453
}
 
454
 
 
455
gDestination::gDestination(const gCycle &c)
 
456
        :chatting(false)
 
457
        ,turns(0)
 
458
        ,hasBeenUsed(false)
 
459
        ,messageID(1)
 
460
        ,missable(true)
 
461
        ,next(NULL)
 
462
        ,list(NULL)
 
463
{
 
464
    CopyFrom( c );
 
465
}
 
466
 
 
467
// or from a message
 
468
gDestination::gDestination(nMessage &m)
 
469
        :gameTime(0),distance(0),speed(0),
 
470
        hasBeenUsed(false),
 
471
        messageID(1),
 
472
        missable(true),
 
473
next(NULL),list(NULL){
 
474
    m >> position;
 
475
    m >> direction;
 
476
    m >> distance;
 
477
 
 
478
    unsigned short flags;
 
479
    m >> flags;
 
480
    braking  = flags & 0x01;
 
481
    chatting = flags & 0x02;
 
482
 
 
483
    messageID = m.MessageID();
 
484
 
 
485
    turns = 0;
 
486
}
 
487
 
 
488
void gDestination::CopyFrom(const gCycleMovement &other)
 
489
{
 
490
    position    = other.Position();
 
491
    direction   = other.Direction();
 
492
    gameTime    = other.LastTime();
 
493
    distance    = other.GetDistance();
 
494
    speed               = other.Speed();
 
495
    braking     = other.GetBraking();
 
496
    turns               = other.GetTurns();
 
497
 
 
498
#ifdef DEBUG
 
499
    if (!finite(gameTime) || !finite(speed) || !finite(distance))
 
500
        st_Breakpoint();
 
501
#endif
 
502
    if ( other.Owner() && other.Player() )
 
503
        chatting = other.Player()->IsChatting();
 
504
}
 
505
 
 
506
void gDestination::CopyFrom(const gCycle &other)
 
507
{
 
508
    CopyFrom( static_cast<const gCycleMovement&>(other) );
 
509
 
 
510
    // correct distance
 
511
    distance    += other.correctDistanceSmooth;
 
512
}
 
513
 
 
514
// *******************************************************************************************
 
515
// *
 
516
// *    CompareWith
 
517
// *
 
518
// *******************************************************************************************
 
519
//!
 
520
//!             @param  other   the destination to compare with
 
521
//!             @return                 -1 if this destination came earler, +1 if other was earler, 0 if no difference can be found
 
522
//!
 
523
// *******************************************************************************************
 
524
 
 
525
int gDestination::CompareWith( const gDestination & other ) const
 
526
{
 
527
    // compare distances, but use the network message ID ( they are always increasing, the distance may stagnate or in extreme cases run backwards ) as main sort criterion
 
528
 
 
529
    // compare message IDs with overflow ( if both are at good values )
 
530
    if ( messageID > 1 && other.messageID > 1 )
 
531
    {
 
532
        short messageIDDifference = messageID - other.messageID;
 
533
        if ( messageIDDifference < 0 )
 
534
            return -1;
 
535
        if ( messageIDDifference > 0 )
 
536
            return 1;
 
537
    }
 
538
 
 
539
    // compare travelled distance
 
540
    if ( distance < other.distance )
 
541
        return -1;
 
542
    else if ( distance > other.distance )
 
543
        return 1;
 
544
 
 
545
    // no relevant difference found
 
546
    return 0;
 
547
}
 
548
 
 
549
class gFloatCompressor
 
550
{
 
551
public:
 
552
    gFloatCompressor( REAL min, REAL max )
 
553
            : min_( min ), max_( max ){}
 
554
 
 
555
    // write compressed float to message
 
556
    void Write( nMessage& m, REAL value ) const
 
557
    {
 
558
        clamp( value, min_, max_ );
 
559
        unsigned short compressed = static_cast< unsigned short > ( maxShort_ * ( value - min_ )/( max_ - min_ ) );
 
560
        m.Write( compressed );
 
561
    }
 
562
 
 
563
    REAL Read( nMessage& m ) const
 
564
    {
 
565
        unsigned short compressed;
 
566
        m.Read( compressed );
 
567
 
 
568
        return  min_ + compressed * ( max_ - min_ )/maxShort_;
 
569
    }
 
570
private:
 
571
    REAL min_, max_;  // minimal and maximal expected value
 
572
 
 
573
    static const unsigned short maxShort_;
 
574
 
 
575
    gFloatCompressor();
 
576
};
 
577
 
 
578
const unsigned short gFloatCompressor::maxShort_ = 0xFFFF;
 
579
 
 
580
static gFloatCompressor compressZeroOne( 0, 1 );
 
581
 
 
582
// write all the data into a nMessage
 
583
void gDestination::WriteCreate(nMessage &m){
 
584
    m << position;
 
585
    m << direction;
 
586
    m << distance;
 
587
 
 
588
    unsigned short flags = 0;
 
589
    if ( braking )
 
590
        flags |= 0x01;
 
591
    if ( chatting )
 
592
        flags |= 0x02;
 
593
    m << flags;
 
594
 
 
595
    // store message ID for later reference
 
596
    messageID = m.MessageID();
 
597
}
 
598
 
 
599
gDestination *gDestination::RightBefore(gDestination *list, REAL dist){
 
600
    if (!list || list->distance > dist)
 
601
        return NULL;
 
602
 
 
603
    gDestination *ret=list;
 
604
    while (ret && ret->next && ret->next->distance < dist)
 
605
        ret=ret->next;
 
606
 
 
607
    return ret;
 
608
}
 
609
 
 
610
gDestination *gDestination::RightAfter(gDestination *list, REAL dist){
 
611
    if (!list)
 
612
        return NULL;
 
613
 
 
614
    gDestination *ret=list;
 
615
    while (ret && ret->distance < dist)
 
616
        ret=ret->next;
 
617
 
 
618
    return ret;
 
619
}
 
620
 
 
621
// insert yourself into a list ordered by gameTime
 
622
void gDestination::InsertIntoList(gDestination **l){
 
623
    if (!l)
 
624
        return;
 
625
 
 
626
    RemoveFromList();
 
627
 
 
628
    // let message find its place
 
629
    while (l && *l && CompareWith( **l ) > 0 )
 
630
        l = &((*l)->next);
 
631
 
 
632
    list=l;
 
633
 
 
634
    tASSERT(l);
 
635
 
 
636
    next=*l;
 
637
    *l=this;
 
638
}
 
639
 
 
640
 
 
641
 
 
642
void gDestination::RemoveFromList(){
 
643
    /*
 
644
       if (!list)
 
645
           return;
 
646
 
 
647
       while (list && *list && *list != this)
 
648
           list = &((*list)->next);
 
649
 
 
650
       tASSERT(list);
 
651
       tASSERT(*list);
 
652
 
 
653
       (*list) = next;
 
654
       next=NULL;
 
655
       list=NULL;
 
656
       
 
657
      */
 
658
 
 
659
    // z-man: HUH? I don't understand the code above any more and it seems to be leaky.
 
660
    // This simple alternative sounds better:
 
661
 
 
662
    if(list)
 
663
        *list = next;
 
664
    if(next)
 
665
        next->list = list;
 
666
 
 
667
    next = 0;
 
668
    list = 0;
 
669
}
 
670
 
 
671
// *******************************************************************************************
 
672
// *
 
673
// *    GetGameTime
 
674
// *
 
675
// *******************************************************************************************
 
676
//!
 
677
//!             @return         game time of the command
 
678
//!
 
679
// *******************************************************************************************
 
680
 
 
681
REAL gDestination::GetGameTime( void ) const
 
682
{
 
683
    return this->gameTime;
 
684
}
 
685
 
 
686
// *******************************************************************************************
 
687
// *
 
688
// *    GetGameTime
 
689
// *
 
690
// *******************************************************************************************
 
691
//!
 
692
//!             @param  gameTime        game time of the command to fill
 
693
//!             @return         A reference to this to allow chaining
 
694
//!
 
695
// *******************************************************************************************
 
696
 
 
697
gDestination const & gDestination::GetGameTime( REAL & gameTime ) const
 
698
{
 
699
    gameTime = this->gameTime;
 
700
    return *this;
 
701
}
 
702
 
 
703
// *******************************************************************************************
 
704
// *
 
705
// *    SetGameTime
 
706
// *
 
707
// *******************************************************************************************
 
708
//!
 
709
//!             @param  gameTime        game time of the command to set
 
710
//!             @return         A reference to this to allow chaining
 
711
//!
 
712
// *******************************************************************************************
 
713
 
 
714
gDestination & gDestination::SetGameTime( REAL gameTime )
 
715
{
 
716
    this->gameTime = gameTime;
 
717
    return *this;
 
718
}
 
719
 
 
720
// ********************************************************
 
721
 
 
722
static void new_destination_handler(nMessage &m)
 
723
{
 
724
    // read the destination
 
725
    gDestination *dest=new gDestination(m);
 
726
 
 
727
    // and the ID of the cycle the destination is added to
 
728
    unsigned short cycle_id;
 
729
    m.Read(cycle_id);
 
730
    nNetObject *o=nNetObject::ObjectDangerous(cycle_id);
 
731
    if (o && &o->CreatorDescriptor() == &cycle_init){
 
732
        if ((sn_GetNetState() == nSERVER) && (m.SenderID() != o->Owner()))
 
733
        {
 
734
            Cheater(m.SenderID());
 
735
        }
 
736
        else
 
737
            if(o->Owner() != ::sn_myNetID)
 
738
            {
 
739
                gCycle* c = dynamic_cast<gCycle *>(o);
 
740
                if (c)
 
741
                {
 
742
                    c->AddDestination(dest);
 
743
                    if ( c->Player() && !dest->Chatting() )
 
744
                        c->Player()->Activity();
 
745
 
 
746
                    // read game time from message
 
747
                    REAL gameTime = se_GameTime()+c->Lag()*3;
 
748
                    if ( !m.End() )
 
749
                        m >> gameTime;
 
750
 
 
751
                    // uncomment to test sync error compensation code on client :)
 
752
                    // gameTime += .2;
 
753
 
 
754
                    dest->SetGameTime( gameTime );
 
755
                    dest = 0;
 
756
                }
 
757
            }
 
758
    }
 
759
 
 
760
    // delete the destination if it has not been used
 
761
    // TODO: exception safety!
 
762
    delete dest;
 
763
}
 
764
 
 
765
static nDescriptor destination_descriptor(321,&new_destination_handler,"destinaton");
 
766
 
 
767
static void BroadCastNewDestination(gCycleMovement *c, gDestination *dest){
 
768
    nMessage *m=new nMessage(destination_descriptor);
 
769
    dest->WriteCreate(*m);
 
770
    m->Write(c->ID());
 
771
    *m << dest->GetGameTime();
 
772
    m->BroadCast();
 
773
}
 
774
 
 
775
void gCycle::OnNotifyNewDestination( gDestination* dest )
 
776
{
 
777
#ifdef DEBUG
 
778
    if ( sg_gnuplotDebug && Player() )
 
779
    {
 
780
        std::ofstream f( Player()->GetUserName() + "_sync", std::ios::app );
 
781
        f << dest->position.x << " " << dest->position.y << "\n";
 
782
    }
 
783
#endif
 
784
 
 
785
    gCycleMovement::OnNotifyNewDestination( dest );
 
786
 
 
787
    //  if (sn_GetNetState()==nSERVER || ::sn_myNetID == owner)
 
788
    if (sn_GetNetState()==nCLIENT && ::sn_myNetID == Owner())
 
789
        BroadCastNewDestination(this,dest);
 
790
 
 
791
    if ( extrapolator_ )
 
792
    {
 
793
        // add destination and try to squeeze it into the schedule.
 
794
        extrapolator_->NotifyNewDestination( dest );
 
795
    }
 
796
 
 
797
    // start a new simulation in any case. The server may simulate the movement a bit differently at the turn.
 
798
    // resimulate_ = ( sn_GetNetState() == nCLIENT );
 
799
}
 
800
 
 
801
 
 
802
// *******************************************************************************************
 
803
// *
 
804
// *    OnDropTempWall
 
805
// *
 
806
// *******************************************************************************************
 
807
//!
 
808
//!             @param  wall       the wall the other cycle is grinding
 
809
//!
 
810
// *******************************************************************************************
 
811
 
 
812
void gCycle::OnDropTempWall( gPlayerWall * wall )
 
813
{
 
814
    tASSERT( wall );
 
815
 
 
816
    // drop the current wall if eiter this or the last wall is grinded
 
817
    gNetPlayerWall * nw = wall->NetWall();
 
818
    if ( nw == currentWall || ( nw == lastWall && currentWall && currentWall->Vec().NormSquared() > EPS ) )
 
819
    {
 
820
        // just request the drop, Timestep() will execute it later
 
821
        dropWallRequested_ = true;
 
822
    }
 
823
}
 
824
 
 
825
// ************************************************************
 
826
 
 
827
 
 
828
 
 
829
 
 
830
// ************************************************************
 
831
 
 
832
 
 
833
// the time the cycle walls stay up ( negative values: they stay up forever )
 
834
void    gCycle::SetWallsStayUpDelay     ( REAL delay )
 
835
{
 
836
    c_pwsud->Set( delay );
 
837
}
 
838
 
 
839
// how much rubber usage shortens the walls
 
840
static REAL sg_cycleRubberWallShrink = 0;
 
841
static nSettingItemWatched<REAL>
 
842
sg_cycleRubberWallShrinkConf("CYCLE_RUBBER_WALL_SHRINK",
 
843
                             sg_cycleRubberWallShrink,
 
844
                             nConfItemVersionWatcher::Group_Bumpy,
 
845
                             12);
 
846
 
 
847
// make walls grow with distance traveled
 
848
static REAL sg_cycleDistWallShrink = 0;
 
849
static nSettingItemWatched<REAL>
 
850
sg_cycleDistWallShrinkConf("CYCLE_DIST_WALL_SHRINK",
 
851
                           sg_cycleDistWallShrink,
 
852
                           nConfItemVersionWatcher::Group_Bumpy,
 
853
                           12);
 
854
 
 
855
static REAL sg_cycleDistWallShrinkOffset = 0;
 
856
static nSettingItemWatched<REAL>
 
857
sg_cycleDistWallShrinkOffsetConf("CYCLE_DIST_WALL_SHRINK_OFFSET",
 
858
                                 sg_cycleDistWallShrinkOffset,
 
859
                                 nConfItemVersionWatcher::Group_Bumpy,
 
860
                                 12);
 
861
 
 
862
// calculates the effect of driving distance to wall length
 
863
static REAL sg_CycleWallLengthFromDist( REAL distance )
 
864
{
 
865
    REAL len = gCycle::WallsLength();
 
866
 
 
867
    // make base length longer or shorter, depending on the sign of sg_cycleDistWallShrink
 
868
    REAL d = sg_cycleDistWallShrinkOffset - distance;
 
869
    if ( d > 0 )
 
870
        len -= sg_cycleDistWallShrink * d;
 
871
 
 
872
    return len;
 
873
}
 
874
 
 
875
// the current length of the walls of this cycle
 
876
REAL gCycle::ThisWallsLength() const
 
877
{
 
878
    // get distance influence
 
879
    REAL len = sg_CycleWallLengthFromDist( distance );
 
880
 
 
881
    // apply rubber shortening
 
882
    return len - GetRubber() * sg_cycleRubberWallShrink;
 
883
}
 
884
 
 
885
// the maximum total length of the walls
 
886
REAL gCycle::MaxWallsLength() const
 
887
{
 
888
    REAL len = sg_CycleWallLengthFromDist( distance );
 
889
 
 
890
    // exception: if the wall grows faster than it receeds, take the maximum, because the wall will
 
891
    // grow backwards
 
892
    if ( sg_cycleDistWallShrink > 1 )
 
893
    {
 
894
        len = wallsLength;
 
895
    }
 
896
 
 
897
    // if the wall grows from rubber use, add the maximal growth
 
898
    if ( sg_cycleRubberWallShrink >= 0 || sg_rubberCycle < 0 )
 
899
        return len;
 
900
    else
 
901
        return len - sg_cycleRubberWallShrink * sg_rubberCycle;
 
902
}
 
903
 
 
904
// the maximum total length of the walls
 
905
void    gCycle::SetWallsLength                  ( REAL length)
 
906
{
 
907
    c_pwl->Set( length );
 
908
}
 
909
 
 
910
// the radius of the holes blewn in by an explosion
 
911
void    gCycle::SetExplosionRadius              ( REAL radius)
 
912
{
 
913
    c_per->Set( radius );
 
914
}
 
915
 
 
916
//  *****************************************************************
 
917
 
 
918
// copies relevant info from other cylce
 
919
void gCycleExtrapolator::CopyFrom( const gCycleMovement& other )
 
920
{
 
921
    // delegate
 
922
    gCycleMovement::CopyFrom( other );
 
923
 
 
924
    // set parent
 
925
    parent_ = &other;
 
926
 
 
927
    // copy distance
 
928
    trueDistance_ = GetDistance();
 
929
}
 
930
 
 
931
// copies relevant info from sync data and everything else from other cycle
 
932
void gCycleExtrapolator::CopyFrom( const SyncData& sync, const gCycle& other )
 
933
{
 
934
    // delegate
 
935
    gCycleMovement::CopyFrom( sync, other );
 
936
 
 
937
    //eFace* face1 = currentFace;
 
938
    MoveSafely( other.lastGoodPosition_, sync.time, sync.time );
 
939
    //eFace* face2 = currentFace;
 
940
    MoveSafely( sync.pos, sync.time, sync.time );
 
941
 
 
942
#ifdef DEBUG_X
 
943
    if ( face1 != face2 || face1 != currentFace )
 
944
    {
 
945
        currentFace = face1;
 
946
        MoveSafely( sync.lastTurn *.01 + sync.pos * .99, sync.time, sync.time );
 
947
        MoveSafely( sync.pos, sync.time, sync.time );
 
948
    }
 
949
#endif
 
950
 
 
951
    // set parent
 
952
    parent_ = &other;
 
953
 
 
954
    // set last turn
 
955
    lastTurnPos_ = sync.lastTurn;
 
956
 
 
957
    // copy distance
 
958
    trueDistance_ = GetDistance();
 
959
}
 
960
 
 
961
gCycleExtrapolator::gCycleExtrapolator(eGrid *grid, const eCoord &pos,const eCoord &dir,ePlayerNetID *p,bool autodelete)
 
962
        :gCycleMovement( grid, pos, dir, p, autodelete )
 
963
        ,trueDistance_( 0 )
 
964
        ,parent_( 0 )
 
965
{
 
966
    // an extrapolator should not be visible as a gameobject from the outside
 
967
    RemoveFromList();
 
968
}
 
969
 
 
970
// gCycleExtrapolator::gCycleExtrapolator(nMessage &m);
 
971
gCycleExtrapolator::~gCycleExtrapolator()
 
972
{
 
973
}
 
974
 
 
975
/*
 
976
// returns the current destination
 
977
gDestination* gCycleExtrapolator::GetCurrentDestination() const
 
978
{
 
979
    return currentDestination;
 
980
 
 
981
    // the code below does not appear to be such a good idea after all...
 
982
    if ( currentDestination )
 
983
    {
 
984
        return currentDestination;
 
985
    }
 
986
    else
 
987
    {
 
988
        tASSERT( parent_ );
 
989
 
 
990
        // return a destination with the current parent position
 
991
        static gDestination parentPos( *parent_ );
 
992
        parentPos.missable = false;
 
993
        parentPos.CopyFrom( *parent_ );
 
994
        return &parentPos;
 
995
    }
 
996
}
 
997
*/
 
998
 
 
999
bool gCycleExtrapolator::EdgeIsDangerous(const eWall *ww, REAL time, REAL alpha ) const
 
1000
{
 
1001
    // ignore temporary walls, they may not be real
 
1002
    const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(ww);
 
1003
    if (w)
 
1004
    {
 
1005
        gNetPlayerWall* nw = w->NetWall();
 
1006
        if ( nw && nw->Preliminary() && w->Cycle() == parent_ )
 
1007
            return false;
 
1008
 
 
1009
        // get time the wall was built
 
1010
        REAL builtTime = w->Time(alpha);
 
1011
 
 
1012
        // is the wall built in the future ( happens during extrapolation )
 
1013
        if ( builtTime > time )
 
1014
            return false;
 
1015
 
 
1016
        // ignore recent walls of parent cycle
 
1017
        gCycle *otherPlayer=w->Cycle();
 
1018
        if (otherPlayer==parent_ &&
 
1019
                ( time < builtTime + 4 * GetTurnDelay() || GetDistance() < w->Pos( alpha ) + .01 * sg_delayCycle*Speed()/SpeedMultiplier()  )
 
1020
           )
 
1021
            return false;
 
1022
    }
 
1023
 
 
1024
    // delegate
 
1025
    return parent_->EdgeIsDangerous( ww, time, alpha ) && gCycleMovement::EdgeIsDangerous( ww, time, alpha );
 
1026
}
 
1027
 
 
1028
bool gCycleExtrapolator::TimestepCore(REAL currentTime)
 
1029
{
 
1030
    // determine a suitable next destination
 
1031
    gDestination destDefault( *parent_ ), *dest=&destDefault;
 
1032
    if ( GetCurrentDestination() )
 
1033
    {
 
1034
        dest = GetCurrentDestination();
 
1035
    }
 
1036
 
 
1037
    // correct distance
 
1038
    distance = dest->distance - DistanceToDestination( *dest );
 
1039
    REAL distanceBefore = GetDistance();
 
1040
 
 
1041
    // delegate
 
1042
    bool ret = gCycleMovement::TimestepCore( currentTime );
 
1043
 
 
1044
    // update true distance
 
1045
    trueDistance_ += GetDistance() - distanceBefore;
 
1046
 
 
1047
    return ret;
 
1048
}
 
1049
 
 
1050
/*
 
1051
bool gCycleExtrapolator::DoTurn( int dir )
 
1052
{
 
1053
    // delegate
 
1054
    return gCycleMovement::DoTurn( dir );
 
1055
}
 
1056
*/
 
1057
 
 
1058
nDescriptor &gCycleExtrapolator::CreatorDescriptor() const{
 
1059
    // should never be called
 
1060
    tASSERT( 0 );
 
1061
    return cycle_init;
 
1062
}
 
1063
 
 
1064
//  *****************************************************************
 
1065
 
 
1066
const eTempEdge* gCycle::Edge(){
 
1067
    if (currentWall)
 
1068
        return currentWall->Edge();
 
1069
    else
 
1070
        return NULL;
 
1071
}
 
1072
 
 
1073
const gPlayerWall* gCycle::CurrentWall(){
 
1074
    if (currentWall)
 
1075
        return currentWall->Wall();
 
1076
    else
 
1077
        return NULL;
 
1078
}
 
1079
 
 
1080
/*
 
1081
const gPlayerWall* gCycle::LastWall(){
 
1082
    if (lastWall)
 
1083
        return lastWall->Wall();
 
1084
    else
 
1085
        return NULL;
 
1086
}
 
1087
*/
 
1088
 
 
1089
/*
 
1090
static tString lala_bodyTexture("Anonymous/original/textures/cycle_body.png");
 
1091
static nSettingItem<tString> gg_tex("TEXTURE_CYCLE_BODY", lala_bodyTexture);
 
1092
//static tConfItemLine g_tex("CYCLE_BODY", sg_bodyTexture);
 
1093
//static tSettingItem<tString> gg_tex("TEXTURE_CYCLE_BODY", sg_bodyTexture);
 
1094
static tString lala_wheelTexture("Anonymous/original/textures/cycle_wheel.png");
 
1095
static nSettingItem<tString> gg_wheelTexture("TEXTURE_CYCLE_WHEEL", lala_wheelTexture);
 
1096
 
 
1097
static tString lala_bikeTexture("Anonymous/original/moviepack/bike.png");
 
1098
static nSettingItem<tString> lalala_bikeTexture("TEXTURE_MP_BIKE", lala_bikeTexture);
 
1099
*/
 
1100
 
 
1101
// HACK! Flexible model loader that eats anything a moviepack currently can throw at it.
 
1102
#ifndef DEDICATED
 
1103
struct gCycleVisuals
 
1104
{
 
1105
    rModel *customModel, *bodyModel, *frontModel, *rearModel; // cycle models
 
1106
    gTextureCycle *customTexture, *bodyTexture, *wheelTexture; // cycle textures
 
1107
    gRealColor color; // cycle color
 
1108
    bool mpType;      // true if moviepack type model/texture can be used
 
1109
    int  mpPreference; // the user preference for the texture/model search path
 
1110
 
 
1111
    gCycleVisuals( gRealColor const & a_color )
 
1112
    {
 
1113
        customModel = bodyModel = frontModel = rearModel = 0;
 
1114
        customTexture = bodyTexture = wheelTexture = 0;
 
1115
 
 
1116
        color = a_color;
 
1117
 
 
1118
        mpType = false;
 
1119
        mpPreference = 0;
 
1120
    }
 
1121
 
 
1122
    ~gCycleVisuals()
 
1123
    {
 
1124
        delete customModel;
 
1125
        delete bodyModel;
 
1126
        delete frontModel;
 
1127
        delete rearModel;
 
1128
        delete customTexture;
 
1129
        delete bodyTexture;
 
1130
        delete wheelTexture;
 
1131
    }
 
1132
 
 
1133
    enum Slot{ SLOT_CUSTOM=0, SLOT_BODY=1, SLOT_WHEEL=2, SLOT_MAX=3 };
 
1134
 
 
1135
    // loads a specific texture from a specific folder
 
1136
    static rSurface * LoadTextureSafe2( Slot slot, int mp )
 
1137
    {
 
1138
        static std::auto_ptr<rSurface> cache[SLOT_MAX][2];
 
1139
        std::auto_ptr<rSurface> & surface = cache[slot][mp];
 
1140
        if ( surface.get() == NULL )
 
1141
        {
 
1142
            static char const * names[SLOT_MAX]={"bike.png","cycle_body.png", "cycle_wheel.png"};
 
1143
            char const * name = names[slot];
 
1144
 
 
1145
            char const * folder = mp ? "moviepack" : "textures";
 
1146
            tString file = tString(folder) + "/" + name;
 
1147
 
 
1148
            surface = std::auto_ptr<rSurface> ( tNEW( rSurface( file ) ) );
 
1149
        }
 
1150
 
 
1151
        if ( surface->GetSurface() )
 
1152
            return surface.get();
 
1153
        else
 
1154
            return NULL;
 
1155
    }
 
1156
 
 
1157
    // load one texture from the prefered folder (or the other one as fallback)
 
1158
    gTextureCycle * LoadTextureSafe( Slot slot, bool wheel )
 
1159
    {
 
1160
        rSurface * surface = LoadTextureSafe2( slot, mpPreference );
 
1161
        if ( !surface )
 
1162
            surface = LoadTextureSafe2( slot, 1-mpPreference );
 
1163
 
 
1164
        if ( surface )
 
1165
            return tNEW( gTextureCycle )( *surface, color, 0, 0, wheel );
 
1166
 
 
1167
        return NULL;
 
1168
    }
 
1169
 
 
1170
    // load textures from the specified folder (or the other one) and the format the model has just been read for
 
1171
    bool LoadTextures()
 
1172
    {
 
1173
        if ( mpType )
 
1174
        {
 
1175
            if ( !customTexture )
 
1176
                customTexture = LoadTextureSafe( SLOT_CUSTOM, false );
 
1177
 
 
1178
            return customTexture;
 
1179
        }
 
1180
        else
 
1181
        {
 
1182
            if ( !bodyTexture )
 
1183
                bodyTexture = LoadTextureSafe( SLOT_BODY, false );
 
1184
            if ( !wheelTexture )
 
1185
                wheelTexture = LoadTextureSafe( SLOT_WHEEL, true );
 
1186
 
 
1187
            return bodyTexture && wheelTexture;
 
1188
        }
 
1189
    }
 
1190
 
 
1191
    // loads a model, checking before if the file exists
 
1192
    static rModel * LoadModelSafe( char const * filename )
 
1193
    {
 
1194
        std::ifstream in;
 
1195
        if ( tDirectories::Data().Open( in, filename ) )
 
1196
        {
 
1197
            return tNEW(rModel( filename ));
 
1198
        }
 
1199
        return 0;
 
1200
    }
 
1201
 
 
1202
    // load a model of specified type from a specified directory
 
1203
    bool LoadModel( bool a_mpType, bool mpFolder )
 
1204
    {
 
1205
        mpType = a_mpType;
 
1206
        char const * folder = mpFolder ? "moviepack" : "models";
 
1207
 
 
1208
        if ( mpType )
 
1209
        {
 
1210
            tString base = tString(folder);
 
1211
            base << "/cycle";
 
1212
            if (!customModel) customModel = LoadModelSafe( base + ".ASE" );
 
1213
            if (!customModel) customModel = LoadModelSafe( base + ".ase" );
 
1214
 
 
1215
            return customModel && LoadTextures();
 
1216
        }
 
1217
        else
 
1218
        {
 
1219
            tString base = tString(folder) + "/cycle_";
 
1220
 
 
1221
            if (!bodyModel) bodyModel = LoadModelSafe( base + "body.mod" );
 
1222
            if (!frontModel) frontModel = LoadModelSafe( base + "front.mod" );
 
1223
            if (!rearModel) rearModel = LoadModelSafe( base + "rear.mod" );
 
1224
 
 
1225
            return bodyModel && frontModel && rearModel && LoadTextures();
 
1226
        }
 
1227
    }
 
1228
 
 
1229
    // tries to load everything from the given data folder
 
1230
    bool LoadModel2( bool mp )
 
1231
    {
 
1232
        // first, try the right type, then the 'unnatural' choice
 
1233
        return LoadModel( mp, mp ) || LoadModel( !mp, mp );
 
1234
    }
 
1235
 
 
1236
    // top level load function: tries to load all variations, starting with passed moviepack folder flag
 
1237
    bool LoadModel( bool mp )
 
1238
    {
 
1239
        mpPreference = mp ? 1 : 0;
 
1240
 
 
1241
        // delegate to try loading both formats from both directories
 
1242
        return LoadModel2( mp ) || LoadModel2( !mp );
 
1243
    }
 
1244
};
 
1245
#endif
 
1246
 
 
1247
void gCycle::MyInitAfterCreation(){
 
1248
    dropWallRequested_ = false;
 
1249
    lastGoodPosition_ = pos;
 
1250
 
 
1251
#ifdef DEBUG
 
1252
    // con << "creating cycle.\n";
 
1253
#endif
 
1254
    engine  = tNEW(eSoundPlayer)(cycle_run,true);
 
1255
    turning = tNEW(eSoundPlayer)(turn_wav);
 
1256
    spark   = tNEW(eSoundPlayer)(scrap);
 
1257
 
 
1258
    //correctDistSmooth=correctTimeSmooth=correctSpeedSmooth=0;
 
1259
    correctDistanceSmooth = 0;
 
1260
 
 
1261
    resimulate_ = false;
 
1262
 
 
1263
    deathTime=0;
 
1264
 
 
1265
    mp=sg_MoviePack();
 
1266
 
 
1267
    lastTimeAnim = 0;
 
1268
    timeCameIntoView = 0;
 
1269
 
 
1270
    customModel = NULL;
 
1271
    body = NULL;
 
1272
    front = NULL;
 
1273
    rear = NULL;
 
1274
    wheelTex = NULL;
 
1275
    bodyTex = NULL;
 
1276
    customTexture = NULL;
 
1277
 
 
1278
    correctPosSmooth=eCoord(0,0);
 
1279
 
 
1280
    rotationFrontWheel=rotationRearWheel=eCoord(1,0);
 
1281
 
 
1282
    skew=skewDot=0;
 
1283
 
 
1284
    // dir=dirDrive;
 
1285
 
 
1286
    if (sn_GetNetState()!=nCLIENT){
 
1287
        if(!player)
 
1288
        { // distribute AI colors
 
1289
            // con << current_ai << ':' << take_ai << ':' << maxmindist <<  "\n\n\n";
 
1290
            color_.r=1;
 
1291
            color_.g=1;
 
1292
            color_.b=1;
 
1293
 
 
1294
            trailColor_.r=1;
 
1295
            trailColor_.g=1;
 
1296
            trailColor_.b=1;
 
1297
        }
 
1298
        else
 
1299
        {
 
1300
            player->Color(color_.r,color_.g,color_.b);
 
1301
            player->TrailColor(trailColor_.r,trailColor_.g,trailColor_.b);
 
1302
        }
 
1303
 
 
1304
        se_MakeColorValid( color_.r, color_.g, color_.b, 1.0f );
 
1305
        se_MakeColorValid( trailColor_.r, trailColor_.g, trailColor_.b, .5f );
 
1306
    }
 
1307
 
 
1308
    // load model and texture
 
1309
#ifndef DEDICATED
 
1310
    gCycleVisuals visuals( color_ );
 
1311
    if ( !visuals.LoadModel( mp ) )
 
1312
    {
 
1313
        tERR_ERROR( "Neither classic style nor moviepack style model and textures found. "
 
1314
                    "The folders \"textures\" and \"moviepack\" need to contain either "
 
1315
                    "cycle.ase and bike.png or body.mod, front.mod, rear.mod, body.png and wheel.png." );
 
1316
    }
 
1317
 
 
1318
    mp = visuals.mpType;
 
1319
 
 
1320
    // transfer models and textures
 
1321
    if ( mp )
 
1322
    {
 
1323
        // use moviepack style body and texture
 
1324
        customModel = visuals.customModel;
 
1325
        visuals.customModel = 0;
 
1326
        customTexture = visuals.customTexture;
 
1327
        visuals.customTexture = 0;
 
1328
    }
 
1329
    else
 
1330
    {
 
1331
        // use classic style body and texture
 
1332
        body = visuals.bodyModel;
 
1333
        visuals.bodyModel = 0;
 
1334
        front = visuals.frontModel;
 
1335
        visuals.frontModel = 0;
 
1336
        rear = visuals.rearModel;
 
1337
        visuals.rearModel = 0;
 
1338
        bodyTex = visuals.bodyTexture;
 
1339
        visuals.bodyTexture = 0;
 
1340
        wheelTex = visuals.wheelTexture;
 
1341
        visuals.wheelTexture = 0;
 
1342
 
 
1343
        tASSERT ( body && front && rear && bodyTex && wheelTex );
 
1344
 
 
1345
        mp = false;
 
1346
    }
 
1347
#endif // DEDICATED
 
1348
 
 
1349
    /*
 
1350
      the old, less flexible, loading code
 
1351
 
 
1352
    if (mp)
 
1353
    {
 
1354
        customModel=new rModel("moviepack/cycle.ASE","moviepack/cycle.ase");
 
1355
    }
 
1356
    else{
 
1357
        body=new rModel("models/cycle_body.mod");
 
1358
        front=new rModel("models/cycle_front.mod");
 
1359
        rear=new rModel("models/cycle_rear.mod");
 
1360
    }
 
1361
 
 
1362
    if (mp)
 
1363
    {
 
1364
        // static rSurface bike(lala_bikeTexture);
 
1365
        static rSurface bike("moviepack/bike.png");
 
1366
        customTexture=new gTextureCycle(bike,color_,0,0);
 
1367
    }
 
1368
    else{
 
1369
        static rSurface body("textures/cycle_body.png");
 
1370
        static rSurface wheel("textures/cycle_wheel.png");
 
1371
        //static rSurface body((const char*) lala_bodyTexture);
 
1372
        //static rSurface wheel((const char*) lala_wheelTexture);
 
1373
        wheelTex=new gTextureCycle(wheel,color_,0,0,true);
 
1374
        bodyTex=new gTextureCycle(body,color_,0,0);
 
1375
    }
 
1376
    */
 
1377
 
 
1378
#ifdef DEBUG
 
1379
    //con << "Created cycle.\n";
 
1380
#endif
 
1381
    nextSyncOwner=nextSync=tSysTimeFloat()-1;
 
1382
 
 
1383
    if (sn_GetNetState()!=nCLIENT)
 
1384
        RequestSync();
 
1385
 
 
1386
    grid->AddGameObjectInteresting(this);
 
1387
 
 
1388
    spawnTime_=lastTimeAnim=lastTime;
 
1389
    // set spawn time to infinite past if this is the first spawn
 
1390
    if ( !sg_cycleFirstSpawnProtection && spawnTime_ <= 1.0 )
 
1391
    {
 
1392
        spawnTime_ = -1E+20;
 
1393
    }
 
1394
 
 
1395
 
 
1396
    if ( engine )
 
1397
        engine->Reset(10000);
 
1398
 
 
1399
    if ( turning )
 
1400
        turning->End();
 
1401
 
 
1402
    nextChatAI=lastTime;
 
1403
 
 
1404
    // add to game grid
 
1405
    this->AddToList();
 
1406
 
 
1407
#ifdef DEBUG
 
1408
    if ( sg_gnuplotDebug && Player() )
 
1409
    {
 
1410
        std::ofstream f( Player()->GetUserName() + "_step" );
 
1411
        f << pos.x << " " << pos.y << "\n";
 
1412
        std::ofstream g( Player()->GetUserName() + "_sync" );
 
1413
        g << pos.x << " " << pos.y << "\n";
 
1414
        std::ofstream h( Player()->GetUserName() + "_turn" );
 
1415
        h << pos.x << " " << pos.y << "\n";
 
1416
    }
 
1417
#endif
 
1418
}
 
1419
 
 
1420
void gCycle::InitAfterCreation(){
 
1421
#ifdef DEBUG
 
1422
    if (!finite(Speed()))
 
1423
        st_Breakpoint();
 
1424
#endif
 
1425
    gCycleMovement::InitAfterCreation();
 
1426
#ifdef DEBUG
 
1427
    if (!finite(Speed()))
 
1428
        st_Breakpoint();
 
1429
#endif
 
1430
    MyInitAfterCreation();
 
1431
}
 
1432
 
 
1433
gCycle::gCycle(eGrid *grid, const eCoord &pos,const eCoord &d,ePlayerNetID *p,bool autodelete)
 
1434
        :gCycleMovement(grid, pos,d,p,autodelete),
 
1435
        engine(NULL),
 
1436
        turning(NULL),
 
1437
        skew(0),skewDot(0),
 
1438
        rotationFrontWheel(1,0),rotationRearWheel(1,0),heightFrontWheel(0),heightRearWheel(0),
 
1439
        currentWall(NULL),
 
1440
        lastWall(NULL)
 
1441
{
 
1442
    windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dirDrive);
 
1443
    dirDrive = Grid()->GetDirection(windingNumberWrapped_);
 
1444
    dir = dirDrive;
 
1445
 
 
1446
    lastNetWall=lastWall=currentWall=NULL;
 
1447
 
 
1448
    MyInitAfterCreation();
 
1449
 
 
1450
    sg_ArchiveCoord( this->dirDrive, 1 );
 
1451
    sg_ArchiveCoord( this->dir, 1 );
 
1452
    sg_ArchiveCoord( this->pos, 1 );
 
1453
    sg_ArchiveReal( this->verletSpeed_, 1 );
 
1454
}
 
1455
 
 
1456
gCycle::~gCycle(){
 
1457
#ifdef DEBUG
 
1458
    //  con << "deleting cylce...\n";
 
1459
#endif
 
1460
    // clear the destination list
 
1461
 
 
1462
    tDESTROY(engine);
 
1463
    tDESTROY(turning);
 
1464
    tDESTROY(spark);
 
1465
 
 
1466
    this->RemoveFromGame();
 
1467
 
 
1468
    if (mp){
 
1469
        delete customModel;
 
1470
        delete customTexture;
 
1471
    }
 
1472
    else{
 
1473
        delete body;
 
1474
        delete front;
 
1475
        delete rear;
 
1476
        delete wheelTex;
 
1477
        delete bodyTex;
 
1478
    }
 
1479
#ifdef DEBUG
 
1480
    //con << "Deleted cycle.\n";
 
1481
#endif
 
1482
    /*
 
1483
      delete currentPos;
 
1484
      delete last_turn;
 
1485
    */
 
1486
}
 
1487
 
 
1488
void gCycle::RemoveFromGame()
 
1489
{
 
1490
    // keep this cycle alive
 
1491
    tJUST_CONTROLLED_PTR< gCycle > keep;
 
1492
 
 
1493
    if ( this->GetRefcount() > 0 )
 
1494
    {
 
1495
        keep = this;
 
1496
 
 
1497
        this->Turn(0);
 
1498
        this->Kill();
 
1499
 
 
1500
        // really kill the cycle even on the client
 
1501
        if ( this->Alive() )
 
1502
        {
 
1503
            Die( lastTime );
 
1504
            tNEW(gExplosion)(grid, pos, lastTime, color_);
 
1505
        }
 
1506
    }
 
1507
 
 
1508
    if (currentWall)
 
1509
        currentWall->CopyIntoGrid( grid );
 
1510
    currentWall=NULL;
 
1511
    lastWall=NULL;
 
1512
 
 
1513
    gCycleMovement::RemoveFromGame();
 
1514
}
 
1515
 
 
1516
static inline void rotate(eCoord &r,REAL angle){
 
1517
    REAL x=r.x;
 
1518
    r.x+=r.y*angle;
 
1519
    r.y-=x*angle;
 
1520
    r=r*(1/sqrt(r.NormSquared()));
 
1521
}
 
1522
 
 
1523
#ifdef MACOSX
 
1524
// Sparks have a large performance problem on Macs. See http://guru3.sytes.net/viewtopic.php?t=2167
 
1525
bool crash_sparks=false;
 
1526
#else
 
1527
bool crash_sparks=true;
 
1528
#endif
 
1529
 
 
1530
//static bool forceTime=false;
 
1531
 
 
1532
// from nNetwork.C
 
1533
extern REAL planned_rate_control[MAXCLIENTS+2];
 
1534
 
 
1535
static REAL sg_minDropInterval=0.05;
 
1536
static tSettingItem< REAL > sg_minDropIntervalConf( "CYCLE_MIN_WALLDROP_INTERVAL", sg_minDropInterval );
 
1537
 
 
1538
bool gCycle::Timestep(REAL currentTime){
 
1539
    // drop current wall if it was requested
 
1540
    if ( dropWallRequested_ )
 
1541
    {
 
1542
        // but don't do so too often globally (performance)
 
1543
        static double nextDrop = 0;
 
1544
        double time = tSysTimeFloat();
 
1545
        if ( time >= nextDrop )
 
1546
        {
 
1547
            nextDrop = time + sg_minDropInterval;
 
1548
            this->DropWall();
 
1549
        }
 
1550
    }
 
1551
 
 
1552
#ifdef DEBUG
 
1553
    if ( sg_gnuplotDebug && Player() )
 
1554
    {
 
1555
        std::ofstream f( Player()->GetUserName() + "_step", std::ios::app );
 
1556
        f << pos.x << " " << pos.y << "\n";
 
1557
    }
 
1558
#endif
 
1559
    // timewarp test debug code
 
1560
    //if ( Player() && Player()->IsHuman() )
 
1561
    //    currentTime -= .1;
 
1562
 
 
1563
    // don't timestep when you're dead
 
1564
    if ( !Alive() )
 
1565
    {
 
1566
        if ( sn_GetNetState() == nSERVER )
 
1567
            RequestSync();
 
1568
 
 
1569
        // die completely
 
1570
        Die( lastTime );
 
1571
 
 
1572
        // and let yourself be removed from the lists so we don't have to go
 
1573
        // through this again.
 
1574
        return true;
 
1575
    }
 
1576
 
 
1577
    // Debug archive position and speed
 
1578
    sg_ArchiveCoord( pos, 7 );
 
1579
    sg_ArchiveReal( verletSpeed_, 7 );
 
1580
 
 
1581
    if ( !destinationList && sn_GetNetState() == nCLIENT )
 
1582
    {
 
1583
        // insert emergency first destination without broadcasting it
 
1584
        gDestination* dest = tNEW(gDestination)(*this);
 
1585
        dest->messageID = 0;
 
1586
        dest->distance = -.001;
 
1587
        dest->InsertIntoList(&destinationList);
 
1588
    }
 
1589
 
 
1590
    // start new extrapolation
 
1591
    if ( !extrapolator_ && resimulate_ )
 
1592
        ResetExtrapolator();
 
1593
 
 
1594
    // extrapolate state from server and copy state when finished
 
1595
    if ( extrapolator_ )
 
1596
    {
 
1597
        REAL dt = ( currentTime - lastTime ) * sg_syncFF / sg_syncFFSteps;
 
1598
#ifdef DEBUG
 
1599
        // dt *= 10.101;
 
1600
        //if ( !resimulate_ )
 
1601
        //      dt *= .1;
 
1602
#endif
 
1603
        for ( int i = sg_syncFFSteps - 1; i>= 0; --i )
 
1604
        {
 
1605
            if ( Extrapolate( dt ) )
 
1606
            {
 
1607
                SyncFromExtrapolator();
 
1608
                break;
 
1609
            }
 
1610
        }
 
1611
    }
 
1612
 
 
1613
    // nothing special if simulating backwards
 
1614
    if (currentTime < lastTime)
 
1615
        return gCycleMovement::Timestep(currentTime);
 
1616
 
 
1617
    // no targets are given: activate chat AI
 
1618
    if (!currentDestination && pendingTurns.empty()){
 
1619
        if (currentTime>nextChatAI && bool(player) &&
 
1620
                ((player->IsChatting()    && player->Owner() == sn_myNetID) ||
 
1621
                 (!player->IsActive() && sn_GetNetState() == nSERVER) )
 
1622
           ){
 
1623
            nextChatAI=currentTime+1;
 
1624
 
 
1625
            gSensor front(this,pos,dir);
 
1626
            gSensor left(this,pos,dir.Turn(eCoord(0,1)));
 
1627
            gSensor right(this,pos,dir.Turn(eCoord(0,-1)));
 
1628
 
 
1629
            REAL range=verletSpeed_*4;
 
1630
 
 
1631
            front.detect(range);
 
1632
            left.detect(range);
 
1633
            right.detect(range);
 
1634
 
 
1635
            enemyInfluence.AddSensor( front, sg_enemyChatbotTimePenalty, this );
 
1636
            enemyInfluence.AddSensor( right, sg_enemyChatbotTimePenalty, this );
 
1637
            enemyInfluence.AddSensor( left, sg_enemyChatbotTimePenalty, this );
 
1638
 
 
1639
#ifdef DEBUG_X
 
1640
            if ( rand() > RAND_MAX / 4 )
 
1641
                left.hit *= .5f;
 
1642
            if ( rand() > RAND_MAX / 4 )
 
1643
                right.hit *= .5f;
 
1644
            if ( rand() > RAND_MAX / 4 )
 
1645
                front.hit *= .5f;
 
1646
#endif
 
1647
 
 
1648
            if (front.hit<left.hit*.9 || front.hit<right.hit*.9){
 
1649
                REAL lr=front.lr;
 
1650
                if (front.type==gSENSOR_SELF) // NEVER close yourself in.
 
1651
                    lr*=-100;
 
1652
                lr-=left.hit-right.hit;
 
1653
                if (lr>0)
 
1654
                    Act(&se_turnRight,1);
 
1655
                else
 
1656
                    Act(&se_turnLeft,1);
 
1657
            }
 
1658
        }
 
1659
 
 
1660
 
 
1661
        bool simulate=Alive();
 
1662
 
 
1663
        if ( !pendingTurns.empty() || currentDestination )
 
1664
            simulate=true;
 
1665
 
 
1666
        if (simulate)
 
1667
        {
 
1668
            try
 
1669
            {
 
1670
                return gCycleMovement::Timestep(currentTime);
 
1671
            }
 
1672
            catch ( gCycleDeath const & death )
 
1673
            {
 
1674
                KillAt( death.pos_ );
 
1675
                return false;
 
1676
            }
 
1677
        }
 
1678
        else
 
1679
            return !Alive();
 
1680
    }
 
1681
    else
 
1682
    {
 
1683
        // just basic movement to do: let base class handle that.
 
1684
        gCycleMovement::Timestep( currentTime );
 
1685
    }
 
1686
 
 
1687
    // do the rest of the timestep
 
1688
    return gCycleMovement::Timestep( currentTime );
 
1689
}
 
1690
 
 
1691
static void blocks(const gSensor &s, const gCycle *c, int lr)
 
1692
{
 
1693
    if ( nCLIENT == sn_GetNetState() )
 
1694
        return;
 
1695
 
 
1696
    if (s.type == gSENSOR_RIM)
 
1697
        gAIPlayer::CycleBlocksRim(c, lr);
 
1698
    else if (s.type == gSENSOR_TEAMMATE || s.type == gSENSOR_ENEMY && s.ehit)
 
1699
    {
 
1700
        gPlayerWall *w = dynamic_cast<gPlayerWall*>(s.ehit->GetWall());
 
1701
        if (w)
 
1702
        {
 
1703
            // int turn     = c->Grid()->WindingNumber();
 
1704
            //    int halfTurn = turn >> 1;
 
1705
 
 
1706
            // calculate the winding number.
 
1707
            int windingBefore = c->WindingNumber();  // we start driving in c's direction
 
1708
            // we need to make a sharp turn in the lr-direction
 
1709
            //    windingBefore   += lr * halfTurn;
 
1710
 
 
1711
            // after the transfer, we need to drive in the direction of the other
 
1712
            // wall:
 
1713
            int windingAfter = w->WindingNumber();
 
1714
 
 
1715
            // if the other wall drives in the opposite direction, we
 
1716
            // need to turn around again:
 
1717
            //    if (s.lr == lr)
 
1718
            // windingAfter -= lr * halfTurn;
 
1719
 
 
1720
            // make the winding difference a multiple of the winding number
 
1721
            /*
 
1722
              int compensation = ((windingAfter - windingBefore - halfTurn) % turn)
 
1723
              + halfTurn;
 
1724
              while (compensation < -halfTurn)
 
1725
              compensation += turn;
 
1726
            */
 
1727
 
 
1728
            // only if the two walls are parallel/antiparallel, there is true blocking.
 
1729
            if (((windingBefore - windingAfter) & 1) == 0)
 
1730
                gAIPlayer::CycleBlocksWay(c, w->Cycle(),
 
1731
                                          lr, s.lr,
 
1732
                                          w->Pos(s.ehit->Ratio(s.before_hit)),
 
1733
                                          - windingAfter + windingBefore);
 
1734
        }
 
1735
    }
 
1736
}
 
1737
 
 
1738
// lets a value decay smoothly
 
1739
static void DecaySmooth( REAL& smooth, REAL relSpeed, REAL minSpeed, REAL clamp )
 
1740
{
 
1741
    if ( fabs(smooth) > .01 )
 
1742
    {
 
1743
        int x;
 
1744
        x = 1;
 
1745
    }
 
1746
 
 
1747
    // increase correction speed if the value is out of bounds
 
1748
    if ( clamp > 0 )
 
1749
        relSpeed *= ( 1 + smooth * smooth / ( clamp * clamp ) );
 
1750
 
 
1751
    // nothing to do
 
1752
    if ( smooth == 0 )
 
1753
    {
 
1754
        return;
 
1755
    }
 
1756
 
 
1757
    // calculate speed from relative speed
 
1758
    REAL speed = smooth * relSpeed;
 
1759
 
 
1760
    // apply minimal correction
 
1761
    if ( fabs( speed ) < minSpeed )
 
1762
        speed = copysign ( minSpeed , smooth );
 
1763
 
 
1764
    // don't overshoot
 
1765
    if ( fabs( speed ) > fabs( smooth ) )
 
1766
        smooth = 0;
 
1767
    else
 
1768
        smooth -= speed;
 
1769
}
 
1770
 
 
1771
// clamps a cycle displacement to non-confusing values
 
1772
static REAL ClampDisplacement( gCycle* cycle, eCoord& displacement, const eCoord& lookout, const eCoord& pos )
 
1773
{
 
1774
    gSensor sensor( cycle, pos, lookout );
 
1775
    sensor.detect(1);
 
1776
    if ( sensor.ehit && sensor.hit >= 0 && sensor.hit < 1 )
 
1777
    {
 
1778
#ifdef DEBUG_X
 
1779
        // repeat sensor
 
1780
        gSensor sensor( cycle, pos, lookout );
 
1781
        sensor.detect(1);
 
1782
#endif
 
1783
        displacement = displacement * sensor.hit;
 
1784
    }
 
1785
    return sensor.hit;
 
1786
}
 
1787
 
 
1788
bool gCycle::TimestepCore(REAL currentTime){
 
1789
    if (!finite(skew))
 
1790
        skew=0;
 
1791
    if (!finite(skewDot))
 
1792
        skewDot=0;
 
1793
 
 
1794
    eCoord oldpos=pos;
 
1795
 
 
1796
    REAL ts=(currentTime-lastTime);
 
1797
 
 
1798
    clamp(ts, -10, 10);
 
1799
    //clamp(correctTimeSmooth, -100, 100);
 
1800
    clamp(correctDistanceSmooth, -100, 100);
 
1801
    //clamp(correctSpeedSmooth, -100, 100);
 
1802
 
 
1803
    // scale factor for smoothing. It is always used as
 
1804
    // value += smooth * smooth_correction;
 
1805
    // smooth_correction -= smooth * smooth_correction;
 
1806
 
 
1807
    REAL smooth = 0;
 
1808
 
 
1809
    if ( ts > 0 )
 
1810
    {
 
1811
        // go a bit of the way
 
1812
        smooth = 1 - 1/( 1 + ts / sg_cycleSyncSmoothTime );
 
1813
    }
 
1814
 
 
1815
    if ( smooth > 0)
 
1816
    {
 
1817
        REAL scd = correctDistanceSmooth * smooth;
 
1818
        distance += scd;
 
1819
        correctDistanceSmooth -= scd;
 
1820
    }
 
1821
 
 
1822
    // apply smooth position correction
 
1823
    // smooth = .5f;
 
1824
    //correctPosSmooth = eCoord(0,0);
 
1825
 
 
1826
    // correctPosSmooth = correctPosSmooth * ( 1 - smooth );
 
1827
 
 
1828
    //if ( 0 )
 
1829
 
 
1830
    REAL animts=currentTime-lastTimeAnim;
 
1831
    if (animts<0 || !finite(animts))
 
1832
        animts=0;
 
1833
    else
 
1834
        lastTimeAnim=currentTime;
 
1835
 
 
1836
    // handle decaying of smooth position correction
 
1837
    {
 
1838
        // let components of smooth position correction decay
 
1839
        REAL minSpeed = sg_cycleSyncSmoothMinSpeed * Speed() * animts;
 
1840
        REAL clamp    = sg_cycleSyncSmoothThreshold * Speed();
 
1841
 
 
1842
        DecaySmooth( correctPosSmooth.x, smooth, minSpeed, clamp );
 
1843
        DecaySmooth( correctPosSmooth.y, smooth, minSpeed, clamp );
 
1844
 
 
1845
        // do sanity checks
 
1846
        if ( correctPosSmooth.NormSquared() > EPS )
 
1847
        {
 
1848
            // cast ray to make sure corrected position lies on the right side of walls
 
1849
            ClampDisplacement( this, correctPosSmooth, correctPosSmooth * 2, pos );
 
1850
 
 
1851
            // same for negative direction, players should see it when they are close to a wall
 
1852
            ClampDisplacement( this, correctPosSmooth, -correctPosSmooth, pos );
 
1853
 
 
1854
            // cast another some rays into the future
 
1855
            eCoord lookahead = dirDrive * ( 10 * correctPosSmooth.Norm() );
 
1856
            ClampDisplacement( this, correctPosSmooth, lookahead + correctPosSmooth, pos );
 
1857
            ClampDisplacement( this, correctPosSmooth, lookahead - correctPosSmooth, pos );
 
1858
            lookahead = dirDrive * Speed();
 
1859
            ClampDisplacement( this, correctPosSmooth, correctPosSmooth, pos + lookahead );
 
1860
            ClampDisplacement( this, correctPosSmooth, - correctPosSmooth, pos + lookahead );
 
1861
        }
 
1862
    }
 
1863
 
 
1864
    if (animts>.2)
 
1865
        animts=.2;
 
1866
 
 
1867
    rotate(rotationFrontWheel,2*verletSpeed_*animts/.43);
 
1868
    rotate(rotationRearWheel,2*verletSpeed_*animts/.73);
 
1869
 
 
1870
    const REAL extension=.25;
 
1871
 
 
1872
    //    REAL step=speed*ts; // +.5*acceleration*ts*ts;
 
1873
 
 
1874
    // animate cycle direction
 
1875
    {
 
1876
        // move it a bit closer to dirDrive
 
1877
        dir=dir+dirDrive*animts*verletSpeed_*3;
 
1878
 
 
1879
        // if it's too far away from dirDrive, clamp it
 
1880
        eCoord dirDistance = dir - dirDrive;
 
1881
        REAL dist = dirDistance.NormSquared();
 
1882
        const REAL maxDist = .8;
 
1883
        if (dirDistance.NormSquared() > maxDist)
 
1884
            dir = dirDrive + dirDistance* (1/sqrt(dist/maxDist));
 
1885
 
 
1886
        // normalize
 
1887
        dir=dir*(1/sqrt(dir.NormSquared()));
 
1888
    }
 
1889
 
 
1890
    {
 
1891
        eCoord oldPos = pos;
 
1892
 
 
1893
        if ( Alive() ){
 
1894
            // delegate core work to base class
 
1895
            try
 
1896
            {
 
1897
                // start building wall
 
1898
                REAL startBuildWallAt = spawnTime_ + sg_cycleWallTime;
 
1899
                if ( !currentWall && currentTime > startBuildWallAt  )
 
1900
                {
 
1901
                    // simulate right to the spot where the wall should begin
 
1902
                    if ( currentTime < startBuildWallAt )
 
1903
                        if ( gCycleMovement::TimestepCore( startBuildWallAt ) )
 
1904
                            return true;
 
1905
 
 
1906
                    // build the wall, modifying the spawn time to make sure it happens
 
1907
                    REAL lastSpawn = spawnTime_;
 
1908
                    spawnTime_ += -1E+20;
 
1909
                    DropWall();
 
1910
                    spawnTime_ = lastSpawn;
 
1911
                }
 
1912
 
 
1913
                // simulate rest of frame
 
1914
                if ( gCycleMovement::TimestepCore( currentTime ) )
 
1915
                    return true;
 
1916
            }
 
1917
            catch ( gCycleDeath const & death )
 
1918
            {
 
1919
                KillAt( death.pos_ );
 
1920
 
 
1921
                // death exceptions are precise; we can safely take over the position from it
 
1922
                oldPos = death.pos_;
 
1923
            }
 
1924
        }
 
1925
 
 
1926
        // die where you started
 
1927
        if ( !Alive() )
 
1928
        {
 
1929
            MoveSafely(oldPos,currentTime,currentTime);
 
1930
            Die( currentTime );
 
1931
        }
 
1932
    }
 
1933
 
 
1934
    // Debug archive position and speed
 
1935
    sg_ArchiveCoord( pos, 7 );
 
1936
    sg_ArchiveReal( verletSpeed_, 7 );
 
1937
 
 
1938
    if (Alive()){
 
1939
        if (currentWall)
 
1940
        {
 
1941
            eCoord wallEndPos = pos;
 
1942
 
 
1943
            // z-man: the next two lines are a very bad idea. This lets walls stick out on the other side while you're using up your rubber.
 
1944
            //if ( sn_GetNetState() != nSERVER )
 
1945
            //    wallEndPos = pos + dirDrive * ( verletSpeed_ * se_PredictTime() );
 
1946
            currentWall->Update(currentTime, wallEndPos );
 
1947
        }
 
1948
 
 
1949
        // animate skew
 
1950
        gSensor fl(this,pos,dirDrive.Turn(1,1));
 
1951
        gSensor fr(this,pos,dirDrive.Turn(1,-1));
 
1952
 
 
1953
        fl.detect(extension*4);
 
1954
        fr.detect(extension*4);
 
1955
 
 
1956
        enemyInfluence.AddSensor( fr, 0, this );
 
1957
        enemyInfluence.AddSensor( fl, 0, this );
 
1958
 
 
1959
        if (fl.ehit)
 
1960
            blocks(fl, this, -1);
 
1961
 
 
1962
        if (fr.ehit)
 
1963
            blocks(fr, this,  1);
 
1964
 
 
1965
#ifndef DEDICATED
 
1966
        if (fl.hit > extension)
 
1967
            fl.hit = extension;
 
1968
 
 
1969
        if (fr.hit > extension)
 
1970
            fr.hit = extension;
 
1971
 
 
1972
        REAL lr=(fl.hit-fr.hit)/extension;
 
1973
 
 
1974
        // ODE for skew
 
1975
        const REAL skewrelax=24;
 
1976
        skewDot-=128*(skew+lr/2)*animts;
 
1977
        skewDot/=1+skewrelax*animts;
 
1978
        if (skew*skewDot>4) skewDot=4/skew;
 
1979
        skew+=skewDot*animts;
 
1980
 
 
1981
        REAL fac = 0.5f;
 
1982
        if ( skew > fr.hit * fac )
 
1983
        {
 
1984
            skew = fr.hit * fac;
 
1985
        }
 
1986
        if ( skew < -fl.hit * fac )
 
1987
        {
 
1988
            skew = -fl.hit * fac;
 
1989
        }
 
1990
 
 
1991
        // generate sparks
 
1992
        eCoord sparkpos,sparkdir;
 
1993
 
 
1994
        if (fl.ehit && fl.hit<extension){
 
1995
            sparkpos=pos+dirDrive.Turn(1,1)*fl.hit;
 
1996
            sparkdir=dirDrive.Turn(0,-1);
 
1997
        }
 
1998
        if (fr.ehit && fr.hit<extension){
 
1999
            //      blocks(fr, this, -1);
 
2000
            sparkpos=pos+dirDrive.Turn(1,-1)*fr.hit;
 
2001
            sparkdir=dirDrive.Turn(0,1);
 
2002
        }
 
2003
 
 
2004
        /*
 
2005
          if (crash_sparks && animts>0)
 
2006
          new gSpark(pos,dirDrive,currentTime);
 
2007
        */
 
2008
        if (fabs(skew)<fabs(lr*.8) ){
 
2009
            skewDot-=lr*1000*animts;
 
2010
            if (crash_sparks && animts>0)
 
2011
            {
 
2012
                gPlayerWall *tmpplayerWall=0;
 
2013
 
 
2014
                if(fl.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fl.ehit->GetWall());
 
2015
                if(fr.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fr.ehit->GetWall());
 
2016
 
 
2017
                if(tmpplayerWall) {
 
2018
                    gCycle *tmpcycle = tmpplayerWall->Cycle();
 
2019
 
 
2020
                    if( tmpcycle )
 
2021
                        new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,tmpcycle->color_.r,tmpcycle->color_.g,tmpcycle->color_.b);
 
2022
                }
 
2023
                else
 
2024
                    new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,1,1,1);
 
2025
 
 
2026
                if ( spark )
 
2027
                    spark->Reset();
 
2028
            }
 
2029
        }
 
2030
 
 
2031
        if (fabs(skew)<fabs(lr*.9) ){
 
2032
            skewDot-=lr*100*animts;
 
2033
            if (crash_sparks && animts>0)
 
2034
            {
 
2035
                gPlayerWall *tmpplayerWall=0;
 
2036
 
 
2037
                if(fl.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fl.ehit->GetWall());
 
2038
                if(fr.ehit) tmpplayerWall=dynamic_cast<gPlayerWall*>(fr.ehit->GetWall());
 
2039
 
 
2040
                if(tmpplayerWall) {
 
2041
                    gCycle *tmpcycle = tmpplayerWall->Cycle();
 
2042
 
 
2043
                    if( tmpcycle )
 
2044
                        new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,tmpcycle->color_.r,tmpcycle->color_.g,tmpcycle->color_.b);
 
2045
                }
 
2046
                else
 
2047
                    new gSpark(grid, sparkpos-dirDrive*.1,sparkdir,currentTime,color_.r,color_.g,color_.b,1,1,1);
 
2048
            }
 
2049
        }
 
2050
#endif
 
2051
        /*
 
2052
          if (fl.hit+fr.hit<extension*.4)
 
2053
          Kill();
 
2054
        */
 
2055
    }
 
2056
 
 
2057
    // clamp skew
 
2058
    if (skew>.5){
 
2059
        skew=.5;
 
2060
        skewDot=0;
 
2061
    }
 
2062
 
 
2063
    if (skew<-.5){
 
2064
        skew=-.5;
 
2065
        skewDot=0;
 
2066
    }
 
2067
 
 
2068
    if ( sn_GetNetState()==nSERVER )
 
2069
    {
 
2070
        if (nextSync < tSysTimeFloat() )
 
2071
        {
 
2072
            // delay syncs for old clients when there is a wall ahead; they would tunnel locally
 
2073
            REAL lookahead = Speed() * sg_syncIntervalEnemy*.5;
 
2074
            if ( !sg_avoidBadOldClientSync || sg_NoLocalTunnelOnSync.Supported( Owner() ) || MaxSpaceAhead( this, ts, lookahead, lookahead ) >= lookahead )
 
2075
            {
 
2076
                RequestSync(false);
 
2077
            }
 
2078
 
 
2079
            nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
 
2080
            nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
 
2081
        }
 
2082
        else if ( nextSyncOwner < tSysTimeFloat() &&
 
2083
                  Owner() != 0 &&
 
2084
                  sn_Connections[Owner()].bandwidthControl_.Control( nBandwidthControl::Usage_Planning ) > 200 )
 
2085
        {
 
2086
            // sync only to the owner (provided there is enough bandwidth available)
 
2087
            RequestSync(Owner(), false);
 
2088
            nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
 
2089
        }
 
2090
    }
 
2091
 
 
2092
    return (!Alive());
 
2093
}
 
2094
 
 
2095
 
 
2096
void gCycle::InteractWith(eGameObject *target,REAL,int){
 
2097
    /*
 
2098
      if (alive && target->type()==ArmageTron_CYCLE){
 
2099
         gCycle *c=(gCycle *)target;
 
2100
         if (c->alive){
 
2101
      const eEdge *current_eEdge=Edge();
 
2102
      const eEdge *other_eEdge=c->Edge();
 
2103
      if(current_eEdge && other_eEdge){
 
2104
      ePoint *meet=current_eEdge->IntersectWith(other_eEdge);
 
2105
      if (meet){ // somebody is going to die!
 
2106
      REAL ratio=current_eEdge->Ratio(*meet);
 
2107
      REAL time=CurrentWall()->Time(ratio);
 
2108
      PassEdge(other_eEdge,time,other_eEdge->Ratio(*meet),0);
 
2109
      }
 
2110
      }
 
2111
         }
 
2112
      }
 
2113
    */
 
2114
}
 
2115
 
 
2116
void gCycle::KillAt( const eCoord& pos){
 
2117
    // find the killer from the enemy influence storage
 
2118
    ePlayerNetID const * constHunter = enemyInfluence.GetEnemy();
 
2119
    ePlayerNetID * hunter = Player();
 
2120
 
 
2121
    // cast away const the safe way
 
2122
    if ( constHunter && constHunter->Object() )
 
2123
        hunter = constHunter->Object()->Player();
 
2124
 
 
2125
    // only take it if it is not too old
 
2126
    if ( LastTime() - enemyInfluence.GetTime() > sg_suicideTimeout )
 
2127
        hunter = NULL;
 
2128
 
 
2129
    // suicide?
 
2130
    if ( !hunter )
 
2131
        hunter = Player();
 
2132
 
 
2133
 
 
2134
    if (!Alive() || sn_GetNetState()==nCLIENT)
 
2135
        return;
 
2136
 
 
2137
#ifdef KRAWALL_SERVER
 
2138
    if (    hunter           && Player()          &&
 
2139
            !dynamic_cast<gAIPlayer*>(hunter)     &&
 
2140
            !dynamic_cast<gAIPlayer*>(Player())             &&
 
2141
            hunter->IsAuth() && Player()->IsAuth())
 
2142
        nKrawall::ServerFrag(hunter->GetUserName(), Player()->GetUserName());
 
2143
#endif
 
2144
 
 
2145
    if (hunter==Player())
 
2146
    {
 
2147
        if (hunter)
 
2148
        {
 
2149
            tString ladderLog;
 
2150
            ladderLog << "DEATH_SUICIDE " << hunter->GetUserName() << "\n";
 
2151
            se_SaveToLadderLog( ladderLog );
 
2152
 
 
2153
            if ( score_suicide )
 
2154
                hunter->AddScore(score_suicide, tOutput(), "$player_lose_suicide" );
 
2155
            else
 
2156
            {
 
2157
                tColoredString hunterName;
 
2158
                hunterName << *hunter << tColoredString::ColorString(1,1,1);
 
2159
                sn_ConsoleOut( tOutput( "$player_free_suicide", hunterName ) );
 
2160
            }
 
2161
        }
 
2162
    }
 
2163
    else{
 
2164
        if (hunter)
 
2165
        {
 
2166
            tOutput lose;
 
2167
            tOutput win;
 
2168
            if (Player())
 
2169
            {
 
2170
                tColoredString preyName;
 
2171
                preyName << *Player();
 
2172
                preyName << tColoredString::ColorString(1,1,1);
 
2173
                if (Player()->CurrentTeam() != hunter->CurrentTeam()) {
 
2174
                    tString ladderLog;
 
2175
                    ladderLog << "DEATH_FRAG " << Player()->GetUserName() << " " << hunter->GetUserName()  << "\n";
 
2176
                    se_SaveToLadderLog( ladderLog );
 
2177
 
 
2178
                    win.SetTemplateParameter(3, preyName);
 
2179
                    win << "$player_win_frag";
 
2180
                    if ( score_kill != 0 )
 
2181
                        hunter->AddScore(score_kill, win, lose );
 
2182
                    else
 
2183
                    {
 
2184
                        tColoredString hunterName;
 
2185
                        hunterName << *hunter << tColoredString::ColorString(1,1,1);
 
2186
                        sn_ConsoleOut( tOutput( "$player_free_frag", hunterName, preyName ) );
 
2187
                    }
 
2188
                }
 
2189
                else {
 
2190
                    tString ladderLog;
 
2191
                    ladderLog << "DEATH_TEAMKILL " << Player()->GetUserName() << " " << hunter->GetUserName()  << "\n";
 
2192
                    se_SaveToLadderLog( ladderLog );
 
2193
 
 
2194
                    tColoredString hunterName;
 
2195
                    hunterName << *hunter << tColoredString::ColorString(1,1,1);
 
2196
                    sn_ConsoleOut( tOutput( "$player_teamkill", hunterName, preyName ) );
 
2197
                }
 
2198
            }
 
2199
            else
 
2200
            {
 
2201
                win << "$player_win_frag_ai";
 
2202
                hunter->AddScore(score_kill, win, lose);
 
2203
            }
 
2204
        }
 
2205
        //      if (prey->player && (prey->player->CurrentTeam() != hunter->player->CurrentTeam()))
 
2206
        if (Player())
 
2207
            Player()->AddScore(score_die,tOutput(),"$player_lose_frag");
 
2208
    }
 
2209
 
 
2210
    Kill();
 
2211
}
 
2212
 
 
2213
class gJustChecking
 
2214
{
 
2215
public:
 
2216
    static bool justChecking;
 
2217
 
 
2218
    gJustChecking(){ justChecking = false; }
 
2219
    ~gJustChecking(){ justChecking = true; }
 
2220
};
 
2221
 
 
2222
bool gJustChecking::justChecking = true;
 
2223
 
 
2224
bool gCycle::EdgeIsDangerous(const eWall* ww, REAL time, REAL a) const{
 
2225
    gPlayerWall const * w = dynamic_cast< gPlayerWall const * >( ww );
 
2226
    if ( w )
 
2227
    {
 
2228
        if ( !Vulnerable() )
 
2229
            return false;
 
2230
 
 
2231
        gNetPlayerWall *nw = w->NetWall();
 
2232
        if( nw == currentWall || nw == lastWall || nw == lastNetWall )
 
2233
            return false;
 
2234
 
 
2235
        // see if the wall is from another player and from the future.
 
2236
        // if it is, it's not dangerous, this cycle was here first.
 
2237
        // on passing the wall later, the other cycle will be pushed back
 
2238
        // to the collision point or killed if that is no longer possible.
 
2239
        // z-man notes: I've got the vaque feeling something like this was
 
2240
        // here before, but got thrown out again for making problems.
 
2241
        if ( gJustChecking::justChecking && w->CycleMovement() != static_cast< const gCycleMovement * >( this ) && w->Time(a) > time )
 
2242
            return false;
 
2243
    }
 
2244
 
 
2245
    return gCycleMovement::EdgeIsDangerous( ww, time, a );
 
2246
}
 
2247
 
 
2248
void gCycle::PassEdge(const eWall *ww,REAL time,REAL a,int){
 
2249
    {
 
2250
        // deactivate time check
 
2251
        gJustChecking thisIsSerious;
 
2252
 
 
2253
        if (!EdgeIsDangerous(ww,time,a) || !Alive() )
 
2254
            return;
 
2255
 
 
2256
#ifdef DEBUG
 
2257
        if (!EdgeIsDangerous(ww,time,a) || !Alive() )
 
2258
            return;
 
2259
#endif
 
2260
    }
 
2261
 
 
2262
#ifdef DEBUG_X
 
2263
    // keep other cycle around
 
2264
    tJUST_CONTROLLED_PTR<gCycleExtrapolator> keepOther( extrapolator_ );
 
2265
 
 
2266
    ResetExtrapolator();
 
2267
 
 
2268
    // extrapolate state from server and copy state when finished
 
2269
    REAL dt = 1;
 
2270
    for ( int i = 9; i>= 0; --i )
 
2271
    {
 
2272
        Extrapolate( dt );
 
2273
    }
 
2274
 
 
2275
    extrapolator_ = keepOther;
 
2276
#endif
 
2277
 
 
2278
    eCoord collPos = ww->Point( a );
 
2279
 
 
2280
    const gPlayerWall *w = dynamic_cast<const gPlayerWall*>(ww);
 
2281
 
 
2282
    enemyInfluence.AddWall( ww, collPos, 0, this );
 
2283
 
 
2284
    if (w)
 
2285
    {
 
2286
        gCycle *otherPlayer=w->Cycle();
 
2287
 
 
2288
        REAL otherTime = w->Time(a);
 
2289
        if(time < otherTime*(1-EPS))
 
2290
        {
 
2291
            // we were first!
 
2292
            static bool tryToSaveFutureWallOwner = false;
 
2293
            if ( tryToSaveFutureWallOwner && sn_GetNetState() != nCLIENT && w->NetWall() == otherPlayer->currentWall && otherPlayer->LastTime() < time + .5f )
 
2294
            {
 
2295
                // teleport the other cycle back to the point before the collision; its next timestep
 
2296
                // will simulate the collision again from the right viewpoint
 
2297
                // determine the distance of the pushback
 
2298
                REAL d = otherPlayer->GetDistanceSinceLastTurn() * .001;
 
2299
                if ( d < .01 )
 
2300
                    d = .01;
 
2301
                REAL maxd = eCoord::F( otherPlayer->dirDrive, collPos - otherPlayer->GetLastTurnPos() ) * .5;
 
2302
                if ( d > maxd )
 
2303
                    d = maxd;
 
2304
                if ( d < 0 )
 
2305
                {
 
2306
                    // err, trouble. Better kill the other player.
 
2307
                    if ( currentWall )
 
2308
                        otherPlayer->enemyInfluence.AddWall( currentWall->Wall(), lastTime, otherPlayer );
 
2309
                    otherPlayer->KillAt( collPos );
 
2310
                }
 
2311
 
 
2312
                // do the move
 
2313
                otherPlayer->MoveSafely( collPos-otherPlayer->dirDrive*d, otherPlayer->LastTime(), otherTime - d/otherPlayer->Speed() );
 
2314
 
 
2315
                // drop our wall so collisions are more accurate
 
2316
                dropWallRequested_ = true;
 
2317
            }
 
2318
            else
 
2319
            {
 
2320
                // the unfortunate other player made a turn in the meantime or too much time has passed. We cannot unroll its movement,
 
2321
                // so we have to destroy it.
 
2322
                if ( currentWall )
 
2323
                    otherPlayer->enemyInfluence.AddWall( currentWall->Wall(), lastTime, otherPlayer );
 
2324
 
 
2325
                otherPlayer->KillAt( collPos );
 
2326
            }
 
2327
        }
 
2328
        else if (time > w->Time(a)+EPS) // sad but true
 
2329
        {
 
2330
            // this cycle has to die here unless it has rubber left
 
2331
            throw gCycleDeath( collPos );
 
2332
 
 
2333
            //                  REAL dist = w->Pos( a );
 
2334
            //                  const_cast<gPlayerWall*>(w)->BlowHole( dist - explosionRadius, dist + explosionRadius );
 
2335
        }
 
2336
    }
 
2337
    else
 
2338
    {
 
2339
        if (bool(player) && sn_GetNetState()!=nCLIENT && Alive() )
 
2340
        {
 
2341
            throw gCycleDeath( collPos );
 
2342
        }
 
2343
 
 
2344
    }
 
2345
}
 
2346
 
 
2347
REAL gCycle::PathfindingModifier( const eWall *w ) const
 
2348
{
 
2349
    if (!w)
 
2350
        return 1;
 
2351
    if (dynamic_cast<const gPlayerWall*>(w))
 
2352
        return .9f;
 
2353
    else
 
2354
        return 1;
 
2355
}
 
2356
 
 
2357
 
 
2358
bool gCycle::Act(uActionPlayer *Act, REAL x){
 
2359
    // don't accept premature input
 
2360
    if (se_mainGameTimer && ( se_mainGameTimer->speed <= 0 || se_mainGameTimer->Time() < -1 ) )
 
2361
        return false;
 
2362
 
 
2363
    if (!Alive() && sn_GetNetState()==nSERVER)
 
2364
        RequestSync(false);
 
2365
 
 
2366
    if(se_turnLeft==*Act && x>.5){
 
2367
        //SendControl(lastTime,&se_turnLeft,1);
 
2368
        Turn(-1);
 
2369
        return true;
 
2370
    }
 
2371
    else if(se_turnRight==*Act && x>.5){
 
2372
        //SendControl(lastTime,&se_turnRight,1);
 
2373
        Turn(1);
 
2374
        return true;
 
2375
    }
 
2376
    else if(s_brake==*Act){
 
2377
        //SendControl(lastTime,&brake,x);
 
2378
        unsigned short newBraking=(x>0);
 
2379
        if ( braking != newBraking )
 
2380
        {
 
2381
            braking = newBraking;
 
2382
            AddDestination();
 
2383
        }
 
2384
        return true;
 
2385
    }
 
2386
    else if(s_brakeToggle==*Act){
 
2387
        if ( x > 0 )
 
2388
        {
 
2389
            braking = !braking;
 
2390
            AddDestination();
 
2391
        }
 
2392
        return true;
 
2393
    }
 
2394
    return false;
 
2395
}
 
2396
 
 
2397
// client side bugfix: network sync messages get actually used
 
2398
static nVersionFeature sg_SyncsAreUsed( 5 );
 
2399
 
 
2400
// temporarily override driving directions on wall drops
 
2401
static eCoord const * sg_fakeDirDrive = NULL;
 
2402
class gFakeDirDriveSetter
 
2403
{
 
2404
public:
 
2405
    gFakeDirDriveSetter( eCoord const & dir )
 
2406
            : lastFakeDir_( sg_fakeDirDrive )
 
2407
    {
 
2408
        sg_fakeDirDrive = &dir;
 
2409
    }
 
2410
 
 
2411
    ~gFakeDirDriveSetter()
 
2412
    {
 
2413
        sg_fakeDirDrive = lastFakeDir_;
 
2414
    }
 
2415
private:
 
2416
    eCoord const * lastFakeDir_;
 
2417
};
 
2418
 
 
2419
bool gCycle::DoTurn(int d)
 
2420
{
 
2421
#ifdef DEBUG
 
2422
    if ( sg_gnuplotDebug && Player() )
 
2423
    {
 
2424
        std::ofstream f( Player()->GetUserName() + "_turn", std::ios::app );
 
2425
        f << pos.x << " " << pos.y << "\n";
 
2426
    }
 
2427
#endif
 
2428
 
 
2429
    if (d >  1) d =  1;
 
2430
    if (d < -1) d = -1;
 
2431
 
 
2432
    if (Alive()){
 
2433
        if ( turning )
 
2434
            turning->Reset();
 
2435
 
 
2436
        clientside_action();
 
2437
 
 
2438
        if ( gCycleMovement::DoTurn( d ) )
 
2439
        {
 
2440
            sg_ArchiveCoord( pos, 1 );
 
2441
 
 
2442
            skewDot+=4*d;
 
2443
 
 
2444
            if (sn_GetNetState() == nCLIENT && Owner() == ::sn_myNetID)
 
2445
                AddDestination();
 
2446
 
 
2447
            if (sn_GetNetState()!=nCLIENT)
 
2448
            {
 
2449
                RequestSync();
 
2450
            }
 
2451
 
 
2452
            // hack: while dropping the wall, turn around dirDrive.
 
2453
            // this makes FindCurrentFace work better.
 
2454
            {
 
2455
                FindCurrentFace();
 
2456
                REAL factor = -16;
 
2457
                eCoord dirDriveFake = dirDrive * factor;
 
2458
                eCoord lastDirDriveBack = lastDirDrive;
 
2459
                lastDirDrive = lastDirDrive * factor;
 
2460
                gFakeDirDriveSetter fakeSetter( dirDriveFake );
 
2461
                DropWall();
 
2462
                lastDirDrive = lastDirDriveBack;
 
2463
            }
 
2464
 
 
2465
            return true;
 
2466
        }
 
2467
    }
 
2468
 
 
2469
    return false;
 
2470
}
 
2471
 
 
2472
void gCycle::DropWall( bool buildNew )
 
2473
{
 
2474
    // keep this cycle alive
 
2475
    tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
 
2476
 
 
2477
    // drop last net wall if it is outdated
 
2478
    if ( lastWall && lastNetWall && lastWall->Time(.5) > lastNetWall->Time(0) )
 
2479
        lastNetWall = 0;
 
2480
 
 
2481
    // update and drop current wall
 
2482
    if(currentWall)
 
2483
    {
 
2484
        lastWall=currentWall;
 
2485
        currentWall->Update(lastTime,pos);
 
2486
        currentWall->CopyIntoGrid( grid );
 
2487
        currentWall=NULL;
 
2488
    }
 
2489
 
 
2490
    if ( buildNew && lastTime >= spawnTime_ + sg_cycleWallTime )
 
2491
        currentWall=new gNetPlayerWall(this,pos,dirDrive,lastTime,distance);
 
2492
 
 
2493
    // grid datastructures change on inserting a wall, better recheck
 
2494
    // all game objects. Temporarily override this cycle's driving direction.
 
2495
    eCoord dirDriveBack = dirDrive;
 
2496
    if ( sg_fakeDirDrive )
 
2497
        dirDrive = *sg_fakeDirDrive;
 
2498
 
 
2499
    if ( grid )
 
2500
    {
 
2501
        for(int i=grid->GameObjects().Len()-1;i>=0;i--)
 
2502
        {
 
2503
            eGameObject * c = grid->GameObjects()(i);
 
2504
            if (c->CurrentFace() && !c->CurrentFace()->IsInGrid() )
 
2505
                c->FindCurrentFace();
 
2506
        }
 
2507
    }
 
2508
    dirDrive = dirDriveBack;
 
2509
 
 
2510
    // reset flag
 
2511
    dropWallRequested_ = false;
 
2512
}
 
2513
 
 
2514
void gCycle::Kill(){
 
2515
    // keep this cycle alive
 
2516
    tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
 
2517
 
 
2518
    if (sn_GetNetState()!=nCLIENT){
 
2519
        RequestSync(true);
 
2520
        if (Alive()){
 
2521
            Die( lastTime );
 
2522
            tNEW(gExplosion)(grid, pos,lastTime, color_);
 
2523
            //   eEdge::SeethroughHasChanged();
 
2524
 
 
2525
            if ( currentWall )
 
2526
            {
 
2527
                // z-man: updating the wall so it reflects exactly the position of death looks like
 
2528
                // a good idea, but unfortunately, the collision position reported from above
 
2529
                // is inaccurate. It's better not to use it at all, or the cycle's wall will stick out
 
2530
                // a bit on the other side of the wall it crashed into.
 
2531
                // reason: the wall
 
2532
 
 
2533
                // currentWall->Update( lastTime, pos );
 
2534
 
 
2535
                // copy the wall into the grid, but not directly; the grid datastructures are probably currently traversed. Kill() is called from eGameObject::Move().
 
2536
                currentWall->CopyIntoGrid( 0 );
 
2537
 
 
2538
                currentWall = NULL;
 
2539
            }
 
2540
 
 
2541
            // request a new sync
 
2542
            RequestSync();
 
2543
        }
 
2544
    }
 
2545
    // z-man: another stupid idea. Why would we need a destination when we're dead?
 
2546
    //    else if (Owner() == ::sn_myNetID)
 
2547
    //        AddDestination();
 
2548
    /*
 
2549
      else if (owner!=::sn_myNetID)
 
2550
      speed=-.01;
 
2551
    */
 
2552
}
 
2553
 
 
2554
/*
 
2555
void gCycle::Turbo(bool turbo){
 
2556
    if (turbo && speed<30){
 
2557
        speed=40;
 
2558
        Turn(0);
 
2559
    }
 
2560
 
 
2561
    if (!turbo && speed>=30){
 
2562
        speed=20;
 
2563
        Turn(0);
 
2564
    }
 
2565
}
 
2566
*/
 
2567
 
 
2568
static rFileTexture cycle_shad(rTextureGroups::TEX_FLOOR,"textures/shadow.png",0,0,true);
 
2569
/*
 
2570
static tString lala_cycle_shad("Anonymous/original/textures/shadow.png");
 
2571
static nSettingItem<tString> lalala_cycle_shad("TEXTURE_CYCLE_SHADOW", lala_cycle_shad);
 
2572
rFileTexture cycle_shad(rTextureGroups::TEX_FLOOR, lala_cycle_shad, 0,0,true);
 
2573
*/
 
2574
 
 
2575
REAL sg_laggometerScale=1;
 
2576
static tSettingItem< REAL > sg_laggometerScaleConf( "LAG_O_METER_SCALE", sg_laggometerScale );
 
2577
 
 
2578
int sg_blinkFrequency=10;
 
2579
static tSettingItem< int > sg_blinkFrequencyConf( "CYCLE_BLINK_FREQUENCY", sg_blinkFrequency );
 
2580
 
 
2581
#ifndef DEDICATED
 
2582
void gCycle::Render(const eCamera *cam){
 
2583
    if ( lastTime > spawnTime_ && !Vulnerable() )
 
2584
    {
 
2585
        double time = tSysTimeFloat();
 
2586
        double wrap = time - floor(time);
 
2587
        int pulse = int ( 2 * wrap * sg_blinkFrequency );
 
2588
        if ( ( pulse & 1 ) == 0 )
 
2589
            return;
 
2590
    }
 
2591
 
 
2592
#ifdef USE_HEADLIGHT
 
2593
#ifdef LINUX
 
2594
    typedef void (*glProgramStringARB_Func)(GLenum, GLenum, GLsizei, const void*);
 
2595
    glProgramStringARB_Func glProgramStringARB_ptr = 0;
 
2596
 
 
2597
    typedef void (*glProgramLocalParameter4fARB_Func)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
 
2598
    glProgramLocalParameter4fARB_Func glProgramLocalParameter4fARB_ptr = 0;
 
2599
 
 
2600
    glProgramStringARB_ptr = (glProgramStringARB_Func) SDL_GL_GetProcAddress("glProgramStringARB");
 
2601
    glProgramLocalParameter4fARB_ptr = (glProgramLocalParameter4fARB_Func) SDL_GL_GetProcAddress("glProgramLocalParameter4fARB");
 
2602
#endif
 
2603
#endif    
 
2604
    if (!finite(z) || !finite(pos.x) ||!finite(pos.y)||!finite(dir.x)||!finite(dir.y)
 
2605
            || !finite(skew))
 
2606
        st_Breakpoint();
 
2607
    if (Alive()){
 
2608
        //con << "Drawing cycle at " << pos << '\n';
 
2609
 
 
2610
#ifdef DEBUG
 
2611
        /*     {
 
2612
                   gDestination *l = destinationList;
 
2613
                   glDisable(GL_LIGHTING);
 
2614
                   glColor3f(1,1,1);
 
2615
                   while(l){
 
2616
                   if (l == currentDestination)
 
2617
                   glColor3f(0,1,0);
 
2618
 
 
2619
                   glBegin(GL_LINES);
 
2620
                   glVertex3f(l->position.x, l->position.y, 0);
 
2621
                   glVertex3f(l->position.x, l->position.y, 100);
 
2622
                   glEnd();
 
2623
 
 
2624
                   if (l == currentDestination)
 
2625
                   glColor3f(0,1,1);
 
2626
 
 
2627
                   l=l->next;
 
2628
                   }
 
2629
                   } */
 
2630
#endif
 
2631
 
 
2632
        GLfloat color[4]={1,1,1,1};
 
2633
        static GLfloat lposa[4] = { 320, 240, 200,0};
 
2634
        static GLfloat lposb[4] = { -240, -100, 200,0};
 
2635
        static GLfloat lighta[4] = { 1, .7, .7, 1 };
 
2636
        static GLfloat lightb[4] = { .7, .7, 1, 1 };
 
2637
 
 
2638
        glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,color);
 
2639
        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color);
 
2640
 
 
2641
        glLightfv(GL_LIGHT0, GL_DIFFUSE, lighta);
 
2642
        glLightfv(GL_LIGHT0, GL_SPECULAR, lighta);
 
2643
        glLightfv(GL_LIGHT0, GL_POSITION, lposa);
 
2644
        glLightfv(GL_LIGHT1, GL_DIFFUSE, lightb);
 
2645
        glLightfv(GL_LIGHT1, GL_SPECULAR, lightb);
 
2646
        glLightfv(GL_LIGHT1, GL_POSITION, lposb);
 
2647
 
 
2648
 
 
2649
        ModelMatrix();
 
2650
        glPushMatrix();
 
2651
        eCoord p = PredictPosition();
 
2652
        glTranslatef(p.x,p.y,0);
 
2653
        glScalef(.5f,.5f,.5f);
 
2654
 
 
2655
 
 
2656
        eCoord ske(1,skew);
 
2657
        ske=ske*(1/sqrt(ske.NormSquared()));
 
2658
 
 
2659
        GLfloat m[4][4]={{dir.x,dir.y,0,0},
 
2660
                         {-dir.y,dir.x,0,0},
 
2661
                         {0,0,1,0},
 
2662
                         {0,0,0,1}};
 
2663
        glMultMatrixf(&m[0][0]);
 
2664
 
 
2665
        glPushMatrix();
 
2666
        //glTranslatef(-1.84,0,0);
 
2667
        if (!mp)
 
2668
            glTranslatef(-1.5,0,0);
 
2669
 
 
2670
        glPushMatrix();
 
2671
 
 
2672
        GLfloat sk[4][4]={{1,0,0,0},
 
2673
                          {0,ske.x,ske.y,0},
 
2674
                          {0,-ske.y,ske.x,0},
 
2675
                          {0,0,0,1}};
 
2676
 
 
2677
        glMultMatrixf(&sk[0][0]);
 
2678
 
 
2679
 
 
2680
        glEnable(GL_LIGHT0);
 
2681
        glEnable(GL_LIGHT1);
 
2682
        glEnable(GL_LIGHTING);
 
2683
 
 
2684
 
 
2685
 
 
2686
        TexMatrix();
 
2687
        IdentityMatrix();
 
2688
 
 
2689
        if (mp){
 
2690
 
 
2691
            ModelMatrix();
 
2692
            glPushMatrix();
 
2693
            /*
 
2694
              GLfloat sk[4][4]={{0,.1,0,0},
 
2695
              {-.1,0,0,0},
 
2696
              {0,0,.1,0},
 
2697
              {1,.2,-1.05,1}};
 
2698
 
 
2699
              if (moviepack_hack)
 
2700
              glMultMatrixf(&sk[0][0]);
 
2701
            */
 
2702
 
 
2703
            customTexture->Select();
 
2704
            //glDisable(GL_TEXTURE_2D);
 
2705
            //glDisable(GL_TEXTURE);
 
2706
            glColor3f(1,1,1);
 
2707
 
 
2708
            //glPolygonMode(GL_FRONT, GL_FILL);
 
2709
            //glDepthFunc(GL_LESS);
 
2710
            //glCullFace(GL_BACK);
 
2711
            customModel->Render();
 
2712
            //glLineWidth(2);
 
2713
            //glPolygonMode(GL_BACK,GL_LINE);
 
2714
            //glDepthFunc(GL_LEQUAL);
 
2715
            //glCullFace(GL_FRONT);
 
2716
            //customModel->Render();
 
2717
            //sr_ResetRenderState();
 
2718
 
 
2719
            glPopMatrix();
 
2720
            glPopMatrix();
 
2721
            glTranslatef(-1.5,0,0);
 
2722
        }
 
2723
        else{
 
2724
            /*
 
2725
              glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
 
2726
              glEnable(GL_TEXTURE_GEN_S);
 
2727
                
 
2728
              glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
 
2729
              glEnable(GL_TEXTURE_GEN_T);
 
2730
                
 
2731
              glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
 
2732
              glEnable(GL_TEXTURE_GEN_R);
 
2733
                
 
2734
              glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
 
2735
              glEnable(GL_TEXTURE_GEN_Q);
 
2736
            */
 
2737
 
 
2738
            glEnable(GL_TEXTURE_2D);
 
2739
 
 
2740
            /*
 
2741
                         static    GLfloat tswap[4][4]={{1,0,0,0},
 
2742
                         {0,0,1,0},
 
2743
                         {0,-1,0,0},
 
2744
                         {.5,.5,0,1}};
 
2745
                
 
2746
                         static    GLfloat tswapb[4][4]={{1,0,0,0},
 
2747
                         {0,0,1,0},
 
2748
                         {0,-1,0,0},
 
2749
                         {.2,1.2,0,1}};
 
2750
            */
 
2751
 
 
2752
            //       TexMatrix();
 
2753
            //       glLoadMatrixf(&tswapb[0][0]);
 
2754
            //       glScalef(.4,.4,.8);
 
2755
            ModelMatrix();
 
2756
 
 
2757
 
 
2758
            bodyTex->Select();
 
2759
            body->Render();
 
2760
 
 
2761
            wheelTex->Select();
 
2762
 
 
2763
            glPushMatrix();
 
2764
            glTranslatef(0,0,.73);
 
2765
 
 
2766
            GLfloat mr[4][4]={{rotationRearWheel.x,0,rotationRearWheel.y,0},
 
2767
                              {0,1,0,0},
 
2768
                              {-rotationRearWheel.y,0,rotationRearWheel.x,0},
 
2769
                              {0,0,0,1}};
 
2770
 
 
2771
 
 
2772
            glMultMatrixf(&mr[0][0]);
 
2773
 
 
2774
 
 
2775
            //       TexMatrix();
 
2776
            //       glLoadMatrixf(&tswap[0][0]);
 
2777
            //       glScalef(.65,.65,.65);
 
2778
            //       ModelMatrix();
 
2779
 
 
2780
            rear->Render();
 
2781
            glPopMatrix();
 
2782
 
 
2783
            glPushMatrix();
 
2784
            glTranslatef(1.84,0,.43);
 
2785
 
 
2786
            GLfloat mf[4][4]={{rotationFrontWheel.x,0,rotationFrontWheel.y,0},
 
2787
                              {0,1,0,0},
 
2788
                              {-rotationFrontWheel.y,0,rotationFrontWheel.x,0},
 
2789
                              {0,0,0,1}};
 
2790
 
 
2791
            glMultMatrixf(&mf[0][0]);
 
2792
 
 
2793
 
 
2794
            //       TexMatrix();
 
2795
            //       glLoadMatrixf(&tswap[0][0]);
 
2796
            //       glScalef(1.2,1.2,1.2);
 
2797
            //       ModelMatrix();
 
2798
 
 
2799
            front->Render();
 
2800
            glPopMatrix();
 
2801
            glPopMatrix();
 
2802
 
 
2803
 
 
2804
        }
 
2805
 
 
2806
 
 
2807
        //     TexMatrix();
 
2808
        //     IdentityMatrix();
 
2809
        ModelMatrix();
 
2810
 
 
2811
        /*
 
2812
          glDisable(GL_TEXTURE_GEN_S);
 
2813
          glDisable(GL_TEXTURE_GEN_T);
 
2814
          glDisable(GL_TEXTURE_GEN_Q);
 
2815
          glDisable(GL_TEXTURE_GEN_R);
 
2816
        */
 
2817
 
 
2818
        glDisable(GL_LIGHT0);
 
2819
        glDisable(GL_LIGHT1);
 
2820
        glDisable(GL_LIGHTING);
 
2821
 
 
2822
        //glDisable(GL_TEXTURE);
 
2823
        glDisable(GL_TEXTURE_2D);
 
2824
        glColor3f(1,1,1);
 
2825
 
 
2826
        {
 
2827
            bool renderPyramid = false;
 
2828
            gRealColor colorPyramid;
 
2829
            REAL alpha = 1;
 
2830
            const REAL timeout = .5f;
 
2831
 
 
2832
            if ( bool(player) )
 
2833
            {
 
2834
                if ( player->IsChatting() )
 
2835
                {
 
2836
                    renderPyramid = true;
 
2837
                    colorPyramid.b = 0.0f;
 
2838
                }
 
2839
                else if ( !player->IsActive() )
 
2840
                {
 
2841
                    renderPyramid = true;
 
2842
                    colorPyramid.b = 0.0f;
 
2843
                    colorPyramid.g = 0.0f;
 
2844
                }
 
2845
                else if ( cam && cam->Center() == this && se_GameTime() < timeout && player->CurrentTeam() && player->CurrentTeam()->NumPlayers() > 1 )
 
2846
                {
 
2847
                    renderPyramid = true;
 
2848
                    alpha = timeout - se_GameTime();
 
2849
                }
 
2850
            }
 
2851
 
 
2852
            if ( renderPyramid )
 
2853
            {
 
2854
                GLfloat s=sin(lastTime);
 
2855
                GLfloat c=cos(lastTime);
 
2856
 
 
2857
                GLfloat m[4][4]={{c,s,0,0},
 
2858
                                 {-s,c,0,0},
 
2859
                                 {0,0,1,0},
 
2860
                                 {0,0,1,1}};
 
2861
 
 
2862
                glPushMatrix();
 
2863
 
 
2864
                glMultMatrixf(&m[0][0]);
 
2865
                glScalef(.5,.5,.5);
 
2866
 
 
2867
 
 
2868
                BeginTriangles();
 
2869
 
 
2870
                glColor4f( colorPyramid.r,colorPyramid.g,colorPyramid.b, alpha );
 
2871
                glVertex3f(0,0,3);
 
2872
                glVertex3f(0,1,4.5);
 
2873
                glVertex3f(0,-1,4.5);
 
2874
 
 
2875
                glColor4f( colorPyramid.r * .7f,colorPyramid.g * .7f,colorPyramid.b * .7f, alpha );
 
2876
                glVertex3f(0,0,3);
 
2877
                glVertex3f(1,0,4.5);
 
2878
                glVertex3f(-1,0,4.5);
 
2879
 
 
2880
                RenderEnd();
 
2881
 
 
2882
                glPopMatrix();
 
2883
            }
 
2884
        }
 
2885
 
 
2886
#ifdef USE_HEADLIGHT
 
2887
        // Headlight contributed by Jonathan
 
2888
        if(headlights) {
 
2889
            if(!cycleprograminited) { // set to false on every sr_InitDisplay, without it I lost my program when I switched to windowed
 
2890
                const char *program =
 
2891
                    "!!ARBfp1.0\
 
2892
                    \
 
2893
                    PARAM normal = program.local[0];\
 
2894
                    ATTRIB texcoord = fragment.texcoord;\
 
2895
                    TEMP final, diffuse, distance;\
 
2896
                    \
 
2897
                    DP3 distance, texcoord, texcoord;\
 
2898
                    RSQ diffuse, distance.w;\
 
2899
                    RCP distance, distance.w;\
 
2900
                    MUL diffuse, texcoord, diffuse;\
 
2901
                    DP3 diffuse, diffuse, normal;\
 
2902
                    MUL final, diffuse, distance;\
 
2903
                    MOV result.color.w, fragment.color;\
 
2904
                    MUL result.color.xyz, fragment.color, final;\
 
2905
                    \
 
2906
                    END";
 
2907
#ifdef LINUX
 
2908
                glProgramStringARB_ptr(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(program), program);
 
2909
#else
 
2910
                glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(program), program);
 
2911
#endif
 
2912
                cycleprograminited = true;
 
2913
            }
 
2914
#ifdef LINUX
 
2915
            glProgramLocalParameter4fARB_ptr(GL_FRAGMENT_PROGRAM_ARB, 0, 0, 0, verletSpeed_ * verletSpeed_, 0);
 
2916
#else                   
 
2917
            glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, 0, 0, verletSpeed_ * verletSpeed_, 0);
 
2918
#endif
 
2919
            glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // blend func and depth mask. Efficient or not, glPushAttrib/glPopAttrib is a quick way to manage state.
 
2920
            glEnable(GL_FRAGMENT_PROGRAM_ARB); // doesn't check if it exists...
 
2921
 
 
2922
            const unsigned sensors = 32; // actually one more
 
2923
            const double mul = 0.25 * M_PI / sensors;
 
2924
            const double add = -0.125 * M_PI;
 
2925
 
 
2926
            double size = gArena::SizeMultiplier() * 500 * M_SQRT2; // is M_SQRT2 in your math.h?
 
2927
            GLfloat array[sensors+2][5];
 
2928
 
 
2929
            array[0][0] = 0;
 
2930
            array[0][1] = 0;
 
2931
            array[0][2] = p.x;
 
2932
            array[0][3] = p.y;
 
2933
            array[0][4] = 0.125;
 
2934
 
 
2935
            for(unsigned i=0; i<=sensors; i++) {
 
2936
                gSensor sensor(this, p, dir.Turn(cos(i * mul + add), sin(i * mul + add)));
 
2937
                sensor.detect(size);
 
2938
                array[i][5] = sensor.before_hit.x - p.x;
 
2939
                array[i][6] = sensor.before_hit.y - p.y;
 
2940
                array[i][7] = sensor.before_hit.x;
 
2941
                array[i][8] = sensor.before_hit.y;
 
2942
                array[i][9] = 0.125;
 
2943
            }
 
2944
 
 
2945
            glPushMatrix();
 
2946
            glLoadIdentity();
 
2947
 
 
2948
            glMatrixMode(GL_TEXTURE);
 
2949
            glPushMatrix();
 
2950
            glTranslatef(0, 0, 1);
 
2951
 
 
2952
            glBlendFunc(GL_ONE, GL_ONE);
 
2953
            glDepthMask(GL_FALSE);
 
2954
 
 
2955
            glColor3fv(reinterpret_cast<GLfloat *>(&color_)); // 8-)
 
2956
            glEnableClientState(GL_VERTEX_ARRAY);
 
2957
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
2958
 
 
2959
            glInterleavedArrays(GL_T2F_V3F, 0, array);
 
2960
            glDrawArrays(GL_TRIANGLE_FAN, 0, sensors+2);
 
2961
 
 
2962
            glDisableClientState(GL_VERTEX_ARRAY);
 
2963
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 
2964
 
 
2965
            glDisable(GL_FRAGMENT_PROGRAM_ARB);
 
2966
 
 
2967
            glPopMatrix();
 
2968
            glMatrixMode(GL_MODELVIEW);
 
2969
 
 
2970
            glPopMatrix();
 
2971
            glPopAttrib();
 
2972
        }
 
2973
#endif // USE_HEADLIGHT
 
2974
        // Name
 
2975
        RenderName( cam );
 
2976
 
 
2977
 
 
2978
        // shadow:
 
2979
 
 
2980
        sr_DepthOffset(true);
 
2981
 
 
2982
 
 
2983
        REAL h=0;//se_cameraZ*.005+.03;
 
2984
 
 
2985
        glEnable(GL_CULL_FACE);
 
2986
 
 
2987
        if(sr_floorDetail>rFLOOR_GRID && rTextureGroups::TextureMode[rTextureGroups::TEX_FLOOR]>0 && sr_alphaBlend){
 
2988
            glColor3f(0,0,0);
 
2989
            cycle_shad.Select();
 
2990
            BeginQuads();
 
2991
            glTexCoord2f(0,1);
 
2992
            glVertex3f(-.6,.4,h);
 
2993
 
 
2994
            glTexCoord2f(1,1);
 
2995
            glVertex3f(-.6,-.4,h);
 
2996
 
 
2997
            glTexCoord2f(1,0);
 
2998
            glVertex3f(2.1,-.4,h);
 
2999
 
 
3000
            glTexCoord2f(0,0);
 
3001
            glVertex3f(2.1,.4,h);
 
3002
 
 
3003
            RenderEnd();
 
3004
        }
 
3005
 
 
3006
        glDisable(GL_CULL_FACE);
 
3007
 
 
3008
        // sr_laggometer;
 
3009
 
 
3010
 
 
3011
        REAL f=verletSpeed_;
 
3012
 
 
3013
        REAL l=Lag();
 
3014
 
 
3015
        glPopMatrix();
 
3016
 
 
3017
        h=cam->CameraZ()*.005+.03;
 
3018
 
 
3019
        if (sn_GetNetState() != nSTANDALONE && sr_laggometer && f*l>.5){
 
3020
            //&& owner!=::sn_myNetID){
 
3021
            glPushMatrix();
 
3022
 
 
3023
            glColor3f(1,1,1);
 
3024
            //glDisable(GL_TEXTURE);
 
3025
            glDisable(GL_TEXTURE_2D);
 
3026
 
 
3027
            glTranslatef(0,0,h);
 
3028
            //glScalef(.5*f,.5*f,.5*f);
 
3029
 
 
3030
            // compensate for the .5 scaling further up
 
3031
            f *= 2 * sg_laggometerScale;
 
3032
 
 
3033
            glScalef(f,f,f);
 
3034
 
 
3035
            // move the sr_laggometer ahead a bit
 
3036
            if (!sr_predictObjects || sn_GetNetState()==nSERVER)
 
3037
                glTranslatef(l,0,0);
 
3038
 
 
3039
 
 
3040
            BeginLineLoop();
 
3041
 
 
3042
 
 
3043
            glVertex2f(-l,-l);
 
3044
            glVertex2f(0,0);
 
3045
            glVertex2f(-l,l);
 
3046
            REAL delay = GetTurnDelay();
 
3047
            if(l> 2*delay){
 
3048
                glVertex2f(-2*l+delay,delay);
 
3049
                glVertex2f(-2*l+2*delay,0);
 
3050
                glVertex2f(-2*l+delay,-delay);
 
3051
            }
 
3052
            else if (l>delay){
 
3053
                glVertex2f(-2*l+delay,delay);
 
3054
                glVertex2f(-l,2*delay-l);
 
3055
                glVertex2f(-l,-(2*delay-l));
 
3056
                glVertex2f(-2*l+delay,-delay);
 
3057
            }
 
3058
 
 
3059
            RenderEnd();
 
3060
            glPopMatrix();
 
3061
        }
 
3062
        sr_DepthOffset(false);
 
3063
 
 
3064
        glPopMatrix();
 
3065
 
 
3066
    }
 
3067
}
 
3068
 
 
3069
static REAL fadeOutNameAfter = 5.0f;    /* 0: never show, < 0 always show */
 
3070
//static int fadeOutNameMode = 1;                       // 0: never show, 1: show for fadeOutNameAfter, 2: always show
 
3071
static bool showOwnName = 0;                    // show name on own cycle?
 
3072
 
 
3073
static tSettingItem< bool > sg_showOwnName( "SHOW_OWN_NAME", showOwnName );
 
3074
//static tSettingItem< int > sg_fadeOutNameMode( "FADEOUT_NAME_MODE", showOwnName )
 
3075
static tSettingItem< REAL > sg_fadeOutNameAfter( "FADEOUT_NAME_DELAY", fadeOutNameAfter );
 
3076
 
 
3077
 
 
3078
void gCycle::RenderName( const eCamera* cam ) {
 
3079
    if ( !this->Player() )
 
3080
        return;
 
3081
 
 
3082
    float modelviewMatrix[16], projectionMatrix[16];
 
3083
    float x, y, z, w;
 
3084
    float xp, yp, wp;
 
3085
    float alpha = 0.75;
 
3086
 
 
3087
    if (fadeOutNameAfter == 0) return; /* XXX put that in ::Render() */
 
3088
    if ( !cam->RenderingMain() ) return; // no name in mirrored image
 
3089
    if ( !showOwnName && cam->Player() == this->player ) return; // don't show own name
 
3090
 
 
3091
    glPushMatrix();
 
3092
    /* position sign above center of cycle */
 
3093
    glTranslatef(0.8, 0, 2.0);
 
3094
    glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
 
3095
    glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);
 
3096
    glPopMatrix();
 
3097
 
 
3098
    /* get coordinates of sign */
 
3099
    x = modelviewMatrix[12];
 
3100
    y = modelviewMatrix[13];
 
3101
    z = modelviewMatrix[14];
 
3102
    w = modelviewMatrix[15];
 
3103
 
 
3104
    /* multiply by projection matrix */
 
3105
    xp = projectionMatrix[0] * x + projectionMatrix[4] * y +
 
3106
         projectionMatrix[8] * z + projectionMatrix[12] * w;
 
3107
    yp = projectionMatrix[1] * x + projectionMatrix[5] * y +
 
3108
         projectionMatrix[9] * z + projectionMatrix[13] * w;
 
3109
    wp = projectionMatrix[3] * x + projectionMatrix[7] * y +
 
3110
         projectionMatrix[11] * z + projectionMatrix[15] * w;
 
3111
 
 
3112
    if (wp <= 0) {
 
3113
        /* behind camera */
 
3114
        timeCameIntoView = 0;
 
3115
        return;
 
3116
    }
 
3117
 
 
3118
    xp /= wp;
 
3119
    yp /= wp;
 
3120
    yp += rCHEIGHT_NORMAL;// * 2.0;
 
3121
 
 
3122
    if (xp <= -1 || xp >= 1 || yp <= -1 || yp >= 1) {
 
3123
        /* out of screen */
 
3124
        timeCameIntoView = 0;
 
3125
        return;
 
3126
    }
 
3127
 
 
3128
    /* it is visible */
 
3129
 
 
3130
    if (fadeOutNameAfter > 0) {
 
3131
        REAL now = tSysTimeFloat();
 
3132
        if (timeCameIntoView == 0)
 
3133
            timeCameIntoView = now;
 
3134
 
 
3135
        if (now - timeCameIntoView > fadeOutNameAfter) {
 
3136
            return;
 
3137
        } else if (now - timeCameIntoView > fadeOutNameAfter - 1) {
 
3138
            /* start to fade out */
 
3139
            alpha = 0.75 - (now - timeCameIntoView -
 
3140
                            (fadeOutNameAfter - 1)) * 0.75;
 
3141
        }
 
3142
    }
 
3143
 
 
3144
    ModelMatrix();
 
3145
    glPushMatrix();
 
3146
    glLoadIdentity();
 
3147
 
 
3148
    ProjMatrix();
 
3149
    glPushMatrix();
 
3150
    glLoadIdentity();
 
3151
 
 
3152
    glColor4f(1, 1, 1, alpha);
 
3153
    DisplayText(xp, yp, rCWIDTH_NORMAL, rCHEIGHT_NORMAL, this->player->GetName(), 0, 0);
 
3154
 
 
3155
    ProjMatrix();
 
3156
    glPopMatrix();
 
3157
 
 
3158
    ModelMatrix();
 
3159
    glPopMatrix();
 
3160
}
 
3161
 
 
3162
 
 
3163
bool gCycle::RenderCockpitFixedBefore(bool){
 
3164
    /*
 
3165
      if (alive)
 
3166
      return true;
 
3167
      else{
 
3168
      REAL rd=se_GameTime()-deathTime;
 
3169
      if (rd<1)
 
3170
         return true;
 
3171
      else{
 
3172
         REAL d=1.25-rd;
 
3173
         d*=8;
 
3174
         if (d<0) d=0;
 
3175
         glColor3f(d,d/2,d/4);
 
3176
         glDisable(GL_TEXTURE_2D);
 
3177
         glDisable(GL_TEXTURE);
 
3178
         glDisable(GL_DEPTH_TEST);
 
3179
         glRectf(-1,-1,1,1);
 
3180
         glColor4f(1,1,1,rd*(2-rd/2));
 
3181
         DisplayText(0,0,.05,.15,"You have been deleted.");
 
3182
         return false;
 
3183
      }
 
3184
      }
 
3185
    */
 
3186
    return true;
 
3187
}
 
3188
 
 
3189
void gCycle::SoundMix(Uint8 *dest,unsigned int len,
 
3190
                      int viewer,REAL rvol,REAL lvol){
 
3191
    if (Alive()){
 
3192
        /*
 
3193
          if (!cycle_run.alt){
 
3194
          rvol*=4;
 
3195
          lvol*=4;
 
3196
          }
 
3197
        */
 
3198
 
 
3199
        if (engine)
 
3200
            engine->Mix(dest,len,viewer,rvol,lvol,verletSpeed_/(sg_speedCycleSound * SpeedMultiplier()));
 
3201
 
 
3202
        if (turning)
 
3203
            if (turn_wav.alt)
 
3204
                turning->Mix(dest,len,viewer,rvol,lvol,5);
 
3205
            else
 
3206
                turning->Mix(dest,len,viewer,rvol,lvol,1);
 
3207
 
 
3208
        if (spark)
 
3209
            spark->Mix(dest,len,viewer,rvol*.5,lvol*.5,4);
 
3210
    }
 
3211
}
 
3212
#endif
 
3213
 
 
3214
eCoord gCycle::PredictPosition(){
 
3215
    gSensor s(this,pos, dir * (verletSpeed_ * se_PredictTime()) + correctPosSmooth );
 
3216
    s.detect(1);
 
3217
    return s.before_hit;
 
3218
 
 
3219
    //    eCoord p = pos + dir * (speed * se_PredictTime());
 
3220
    //    return p + correctPosSmooth;
 
3221
}
 
3222
 
 
3223
eCoord gCycle::CamPos()
 
3224
{
 
3225
    return PredictPosition() + dir.Turn(0 ,-skew*z);
 
3226
 
 
3227
    //    gSensor s(this,pos, PredictPosition() - pos );
 
3228
    //    s.detect(1);
 
3229
 
 
3230
    //    return s.before_hit + dir.Turn(0 ,-skew*z);
 
3231
 
 
3232
    // return pos + dir.Turn(0 ,-skew*z);
 
3233
}
 
3234
 
 
3235
eCoord  gCycle::CamTop()
 
3236
{
 
3237
    return dir.Turn(0,-skew);
 
3238
}
 
3239
 
 
3240
 
 
3241
#ifdef POWERPAK_DEB
 
3242
void gCycle::PPDisplay(){
 
3243
    int R=int(r*255);
 
3244
    int G=int(g*255);
 
3245
    int B=int(b*255);
 
3246
    PD_PutPixel(DoubleBuffer,
 
3247
                se_X_ToScreen(pos.x),
 
3248
                se_Y_ToScreen(pos.y),
 
3249
                PD_CreateColor(DoubleBuffer,R,G,B));
 
3250
    /*
 
3251
      PD_PutPixel(DoubleBuffer,
 
3252
      se_X_ToScreen(pos.x+1),
 
3253
      se_Y_ToScreen(pos.y),
 
3254
      PD_CreateColor(DoubleBuffer,R,G,B));
 
3255
      PD_PutPixel(DoubleBuffer,
 
3256
      se_X_ToScreen(pos.x-1),
 
3257
      se_Y_ToScreen(pos.y),
 
3258
      PD_CreateColor(DoubleBuffer,R,G,B));
 
3259
      PD_PutPixel(DoubleBuffer,
 
3260
      se_X_ToScreen(pos.x),
 
3261
      se_Y_ToScreen(pos.y+1),
 
3262
      PD_CreateColor(DoubleBuffer,R,G,B));
 
3263
      PD_PutPixel(DoubleBuffer,
 
3264
      se_X_ToScreen(pos.x),
 
3265
      se_Y_ToScreen(pos.y-1),
 
3266
      PD_CreateColor(DoubleBuffer,R,G,B));
 
3267
    */
 
3268
}
 
3269
#endif
 
3270
 
 
3271
 
 
3272
 
 
3273
 
 
3274
 
 
3275
// cycle network routines:
 
3276
gCycle::gCycle(nMessage &m)
 
3277
        :gCycleMovement(m),
 
3278
        engine(NULL),
 
3279
        turning(NULL),
 
3280
        spark(NULL),
 
3281
        skew(0),skewDot(0),
 
3282
        rotationFrontWheel(1,0),rotationRearWheel(1,0),heightFrontWheel(0),heightRearWheel(0),
 
3283
        currentWall(NULL),
 
3284
        lastWall(NULL)
 
3285
{
 
3286
    lastNetWall=lastWall=currentWall=NULL;
 
3287
    windingNumberWrapped_ = windingNumber_ = Grid()->DirectionWinding(dirDrive);
 
3288
    dirDrive = Grid()->GetDirection(windingNumberWrapped_);
 
3289
    dir = dirDrive;
 
3290
 
 
3291
    rubber=0;
 
3292
    //correctTimeSmooth =0;
 
3293
    correctDistanceSmooth =0;
 
3294
    //correctSpeedSmooth =0;
 
3295
 
 
3296
    deathTime = 0;
 
3297
    spawnTime_ = se_GameTime() + 100;
 
3298
 
 
3299
    m >> color_.r;
 
3300
    m >> color_.g;
 
3301
    m >> color_.b;
 
3302
 
 
3303
    trailColor_ = color_;
 
3304
 
 
3305
    se_MakeColorValid( color_.r, color_.g, color_.b, 1.0f );
 
3306
    se_MakeColorValid( trailColor_.r, trailColor_.g, trailColor_.b, .5f );
 
3307
 
 
3308
    // set last time so that the first read_sync will not think this is old
 
3309
    lastTimeAnim = lastTime = -EPS;
 
3310
 
 
3311
    nextSync = nextSyncOwner = -1;
 
3312
}
 
3313
 
 
3314
 
 
3315
void gCycle::WriteCreate(nMessage &m){
 
3316
    eNetGameObject::WriteCreate(m);
 
3317
    m << color_.r;
 
3318
    m << color_.g;
 
3319
    m << color_.b;
 
3320
}
 
3321
 
 
3322
static nVersionFeature sg_verletIntegration( 7 );
 
3323
 
 
3324
void gCycle::WriteSync(nMessage &m){
 
3325
    //  eNetGameObject::WriteSync(m);
 
3326
 
 
3327
    if ( Alive() )
 
3328
    {
 
3329
        m << lastTime;
 
3330
    }
 
3331
    else
 
3332
    {
 
3333
        m << deathTime;
 
3334
    }
 
3335
    m << Direction();
 
3336
    m << Position();
 
3337
 
 
3338
    REAL speed = verletSpeed_;
 
3339
    // if the clients understand it, send them the real current speed
 
3340
    if ( sg_verletIntegration.Supported() )
 
3341
        speed = Speed();
 
3342
 
 
3343
    m << speed;
 
3344
    m << short( Alive() ? 1 : 0 );
 
3345
    m << distance;
 
3346
    if (!currentWall || currentWall->preliminary)
 
3347
        m.Write(0);
 
3348
    else
 
3349
        m.Write(currentWall->ID());
 
3350
 
 
3351
    m.Write(turns);
 
3352
    m.Write(braking);
 
3353
 
 
3354
    // write last turn position
 
3355
    m << GetLastTurnPos();
 
3356
 
 
3357
    // write rubber
 
3358
    compressZeroOne.Write( m, rubber/( sg_rubberCycle + .1 ) );
 
3359
    compressZeroOne.Write( m, 1/( 1 + rubberMalus ) );
 
3360
 
 
3361
    // write last clientside sync message ID
 
3362
    unsigned short lastMessageID = 0;
 
3363
    if ( lastDestination )
 
3364
        lastMessageID = lastDestination->messageID;
 
3365
    m.Write(lastMessageID);
 
3366
 
 
3367
    // write brake
 
3368
    compressZeroOne.Write( m, brakingReservoir );
 
3369
 
 
3370
    // set new sync times
 
3371
    nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
 
3372
    nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
 
3373
}
 
3374
 
 
3375
bool gCycle::SyncIsNew(nMessage &m){
 
3376
    bool ret=eNetGameObject::SyncIsNew(m);
 
3377
 
 
3378
 
 
3379
    REAL dummy2;
 
3380
    short al;
 
3381
    unsigned short Turns;
 
3382
 
 
3383
    m >> dummy2;
 
3384
    m >> al;
 
3385
    m >> dummy2;
 
3386
    m.Read(Turns);
 
3387
 
 
3388
#ifdef DEBUG_X
 
3389
    con << "received sync for player " << player->GetName() << "  ";
 
3390
    if (ret || al!=1){
 
3391
        con << "accepting..\n";
 
3392
        return true;
 
3393
    }
 
3394
    else
 
3395
    {
 
3396
        con << "rejecting..\n";
 
3397
        return false;
 
3398
    }
 
3399
#endif
 
3400
 
 
3401
    return ret || al!=1;
 
3402
}
 
3403
 
 
3404
// resets the extrapolator to the last known state
 
3405
void gCycle::ResetExtrapolator()
 
3406
{
 
3407
    resimulate_ = false;
 
3408
    if (!extrapolator_)
 
3409
    {
 
3410
        extrapolator_ = tNEW( gCycleExtrapolator )(grid, pos, dir );
 
3411
    }
 
3412
 
 
3413
    extrapolator_->CopyFrom( lastSyncMessage_, *this );
 
3414
}
 
3415
 
 
3416
// simulate the extrapolator at higher speed
 
3417
bool gCycle::Extrapolate( REAL dt )
 
3418
{
 
3419
    tASSERT( extrapolator_ );
 
3420
 
 
3421
    eCoord posBefore = extrapolator_->Position();
 
3422
 
 
3423
    // calculate target time
 
3424
    REAL newTime = extrapolator_->LastTime() + dt;
 
3425
 
 
3426
    bool ret = false;
 
3427
 
 
3428
    // clamp: don't simulate further than our current time
 
3429
    if ( newTime >= lastTime )
 
3430
    {
 
3431
        // simulate extrapolator until now
 
3432
        eGameObject::TimestepThis( lastTime, extrapolator_ );
 
3433
 
 
3434
        // test if there are real (the check for list does that) destinations left; we cannot call it finished if there are.
 
3435
        gDestination* unhandledDestination = extrapolator_->GetCurrentDestination();
 
3436
        ret = !unhandledDestination || !unhandledDestination->list;
 
3437
 
 
3438
        newTime = lastTime;
 
3439
    }
 
3440
    else
 
3441
    {
 
3442
        // simulate extrapolator as requested
 
3443
        eGameObject::TimestepThis( newTime, extrapolator_ );
 
3444
    }
 
3445
 
 
3446
    //eCoord posAfter = extrapolator_->Position();
 
3447
    //eDebugLine::SetTimeout( 1.0 );
 
3448
    //eDebugLine::SetColor( 1,0,0 );
 
3449
    //eDebugLine::Draw( posBefore, 8, posAfter, 4 );
 
3450
    //eDebugLine::Draw( posBefore, 4, posAfter, 4 );
 
3451
 
 
3452
    return ret;
 
3453
}
 
3454
 
 
3455
// makes sure the given displacement does not cross walls
 
3456
void se_SanifyDisplacement( eGameObject* base, eCoord& displacement )
 
3457
{
 
3458
    eCoord base_pos = base->Position();
 
3459
    eCoord reachable_pos = base->Position() + displacement;
 
3460
 
 
3461
    int timeout = 5;
 
3462
    while( timeout > 0 )
 
3463
    {
 
3464
        --timeout;
 
3465
 
 
3466
        // cast ray fron sync_pos to reachable_pos
 
3467
        gSensor test( base, base_pos, displacement );
 
3468
        test.detect(1);
 
3469
 
 
3470
        if ( timeout == 0 )
 
3471
        {
 
3472
            // emergency exit; take something closely before the wall
 
3473
            displacement = ( displacement ) * ( test.hit * .99 );
 
3474
            break;
 
3475
        }
 
3476
 
 
3477
        if ( !test.ehit )
 
3478
        {
 
3479
            // path is clear
 
3480
            break;
 
3481
        }
 
3482
        else
 
3483
        {
 
3484
#ifdef DEBUG
 
3485
            // see if the wall that was hit is nearly parallel to the desired move. maybe we should add
 
3486
            // special code for that. However, the projection idea seems to be universally robust.
 
3487
            float p = test.ehit->Vec() * displacement;
 
3488
            float m = se_EstimatedRangeOfMult( test.ehit->Vec(), displacement );
 
3489
            if ( fabs(p) < m * .001 )
 
3490
            {
 
3491
                con << "Almost missed wall during gCycle::ReadSync positon update";
 
3492
            }
 
3493
#endif
 
3494
 
 
3495
            // project reachable_pos to the line that was hit
 
3496
            REAL alpha = test.ehit->Ratio( base_pos + displacement );
 
3497
            displacement = *test.ehit->Point() + test.ehit->Vec() * alpha - base_pos;
 
3498
 
 
3499
            // move it a bit closer to the known good position
 
3500
            displacement = displacement * .99;
 
3501
        }
 
3502
    }
 
3503
}
 
3504
 
 
3505
void gCycle::TransferPositionCorrectionToDistanceCorrection()
 
3506
{
 
3507
    // REAL correctDist = eCoord::F( correctPosSmooth, dirDrive );
 
3508
    // correctDistanceSmooth += correctDist;
 
3509
    // correctPosSmooth = correctPosSmooth - dirDrive * correctDist;
 
3510
}
 
3511
 
 
3512
// take over the extrapolator's data
 
3513
void gCycle::SyncFromExtrapolator()
 
3514
{
 
3515
    // store old position
 
3516
    eCoord oldPos = pos;
 
3517
 
 
3518
    // con << "Copy: " << LastTime() << ", " << extrapolator_->LastTime() << ", " << extrapolator_->Position() << ", " << pos << "\n";
 
3519
 
 
3520
    tASSERT( extrapolator_ );
 
3521
 
 
3522
    // delegate
 
3523
    CopyFrom( *extrapolator_ );
 
3524
 
 
3525
    // adjust current wall (not essential, don't do it for the first wall)
 
3526
    if ( currentWall && currentWall->tBeg > spawnTime_ + sg_cycleWallTime + .01f )
 
3527
        currentWall->beg = extrapolator_->GetLastTurnPos();
 
3528
 
 
3529
    // smooth position correction
 
3530
    correctPosSmooth = correctPosSmooth + oldPos - pos;
 
3531
 
 
3532
#ifdef DEBUG
 
3533
    if ( correctPosSmooth.NormSquared() > .1f )
 
3534
    {
 
3535
        std::cout << "Lag slide! " << correctPosSmooth << "\n";
 
3536
        int x;
 
3537
        x = 0;
 
3538
    }
 
3539
#endif
 
3540
 
 
3541
    // calculate time difference between this cycle and extrapolator
 
3542
    REAL dt = this->LastTime() - extrapolator_->LastTime();
 
3543
 
 
3544
    // extrapolate true distance ( the best estimate we have for the distance on the server )
 
3545
    REAL trueDistance = extrapolator_->trueDistance_ + extrapolator_->Speed() * dt;
 
3546
 
 
3547
    // update distance correction
 
3548
    // con <<   correctDistanceSmooth << "," << trueDistance << "," << distance << "\n";
 
3549
    // correctDistanceSmooth = trueDistance - distance;
 
3550
    distance = trueDistance;
 
3551
 
 
3552
    // split away part in driving direction
 
3553
    TransferPositionCorrectionToDistanceCorrection();
 
3554
 
 
3555
    // make sure correction does not bring us to the wrong side of a wall
 
3556
    // se_SanifyDisplacement( this, correctPosSmooth );
 
3557
 
 
3558
    //eDebugLine::SetTimeout( 1.0 );
 
3559
    //eDebugLine::SetColor( 0,1,0 );
 
3560
    //eDebugLine::Draw( pos, 4, pos + dirDrive * 10, 14 );
 
3561
 
 
3562
    // delete extrapolator
 
3563
    extrapolator_ = 0;
 
3564
}
 
3565
 
 
3566
static int sg_useExtrapolatorSync=1;
 
3567
static tSettingItem<int> sg_useExtrapolatorSyncConf("EXTRAPOLATOR_SYNC",sg_useExtrapolatorSync);
 
3568
 
 
3569
// make sure no correction moves the cycle backwards beyond the beginning of the last wall
 
3570
void ClampForward( eCoord& newPos, const eCoord& startPos, const eCoord& dir )
 
3571
{
 
3572
    REAL forward = eCoord::F( newPos - startPos, dir );
 
3573
    if ( forward < 0 )
 
3574
        newPos = newPos - dir * forward;
 
3575
}
 
3576
 
 
3577
extern REAL sg_cycleBrakeRefill;
 
3578
extern REAL sg_cycleBrakeDeplete;
 
3579
 
 
3580
void gCycle::ReadSync( nMessage &m )
 
3581
{
 
3582
    // data from sync message
 
3583
    SyncData sync;
 
3584
 
 
3585
    short sync_alive;               // is this cycle alive?
 
3586
    unsigned short sync_wall=0;     // ID of wall
 
3587
 
 
3588
    eCoord new_pos = pos;       // the extrapolated position
 
3589
 
 
3590
    // warning: depends on the implementation of eNetGameObject::WriteSync
 
3591
    // since we don't call eNetGameObject::ReadSync.
 
3592
    m >> sync.time;
 
3593
 
 
3594
    // reset values not sent with old protocol messages
 
3595
    sync.rubber = rubber;
 
3596
    sync.turns = turns;
 
3597
    sync.braking = braking;
 
3598
    sync.messageID = 1;
 
3599
 
 
3600
    m >> sync.dir;
 
3601
    m >> sync.pos;
 
3602
 
 
3603
    //eDebugLine::SetTimeout( 1.0 );
 
3604
    //eDebugLine::SetColor( 1,1,1 );
 
3605
    //eDebugLine::Draw( lastSyncMessage_.pos, 0, lastSyncMessage_.pos, 20 );
 
3606
 
 
3607
    m >> sync.speed;
 
3608
    m >> sync_alive;
 
3609
    m >> sync.distance;
 
3610
    m.Read(sync_wall);
 
3611
    if (!m.End())
 
3612
        m.Read(sync.turns);
 
3613
    if (!m.End())
 
3614
        m.Read(sync.braking);
 
3615
 
 
3616
    if ( !m.End() )
 
3617
    {
 
3618
        m >> sync.lastTurn;
 
3619
    }
 
3620
    else if ( currentWall )
 
3621
    {
 
3622
        sync.lastTurn = currentWall->beg;
 
3623
    }
 
3624
 
 
3625
    bool canUseExtrapolatorMethod = false;
 
3626
 
 
3627
    bool rubberSent = false;
 
3628
    if ( !m.End() )
 
3629
    {
 
3630
        rubberSent = true;
 
3631
 
 
3632
        // read rubber
 
3633
        REAL preRubber, preRubberMalus;
 
3634
        preRubber = compressZeroOne.Read( m );
 
3635
        preRubberMalus = compressZeroOne.Read( m );
 
3636
 
 
3637
        // read last message ID
 
3638
        m.Read(sync.messageID);
 
3639
 
 
3640
        // read braking reservoir
 
3641
        sync.brakingReservoir = compressZeroOne.Read( m );
 
3642
        // std::cout << "sync: " << sync.brakingReservoir << ":" << sync.braking << "\n";
 
3643
 
 
3644
        // undo skewing
 
3645
        sync.rubber = preRubber * ( sg_rubberCycle + .1 );
 
3646
        sync.rubberMalus = 1/preRubberMalus - 1;
 
3647
 
 
3648
        // extrapolation is probably safe
 
3649
        canUseExtrapolatorMethod = sg_useExtrapolatorSync && lastTime > 0;
 
3650
    }
 
3651
    else
 
3652
    {
 
3653
        // try to extrapolate brake status backwards in time
 
3654
        sync.brakingReservoir = brakingReservoir;
 
3655
        if ( brakingReservoir > 0 && sync.braking )
 
3656
            sync.brakingReservoir += ( lastTime - sync.time ) * sg_cycleBrakeDeplete;
 
3657
        else if ( brakingReservoir < 1 && !sync.braking )
 
3658
            sync.brakingReservoir -= ( lastTime - sync.time ) * sg_cycleBrakeRefill;
 
3659
 
 
3660
        if ( sync.brakingReservoir < 0 )
 
3661
            sync.brakingReservoir = 0;
 
3662
        else if ( sync.brakingReservoir > 1 )
 
3663
            sync.brakingReservoir = 1;
 
3664
    }
 
3665
 
 
3666
    // abort if sync is not new
 
3667
    if ( lastSyncMessage_.time >= sync.time && lastSyncMessage_.turns >= sync.turns && sync_alive > 0 )
 
3668
    {
 
3669
        //eDebugLine::SetTimeout( 5 );
 
3670
        //eDebugLine::SetColor( 1,1,0);
 
3671
        //eDebugLine::Draw( lastSyncMessage_.pos, 1.5, lastSyncMessage_.pos, 5.0 );
 
3672
        return;
 
3673
    }
 
3674
    lastSyncMessage_ = sync;
 
3675
 
 
3676
    // store last known good position: a bit before the last position confirmed by the server
 
3677
    lastGoodPosition_ = sync.pos + ( sync.lastTurn - sync.pos ) *.01;
 
3678
    // offset it a tiny bit by our last driving direction
 
3679
    if ( eCoord::F( dirDrive, sync.dir ) > .99f )
 
3680
        lastGoodPosition_ = lastGoodPosition_ - this->lastDirDrive * .0001;
 
3681
 
 
3682
    //eDebugLine::SetTimeout( 2 );
 
3683
    //eDebugLine::SetColor( 0,1,0);
 
3684
    //eDebugLine::Draw( lastSyncMessage_.pos, 1.5, lastSyncMessage_.pos, 5.0 );
 
3685
 
 
3686
    // con << "Sync: " << lastTime << ", " << lastSyncMessage_.time << ", " << lastSyncMessage_.pos << ", " << pos << "\n";
 
3687
 
 
3688
    if ( canUseExtrapolatorMethod )
 
3689
    {
 
3690
        // reset extrapolator if information from server is more up to date
 
3691
        if ( extrapolator_ && extrapolator_->LastTime() < lastSyncMessage_.time )
 
3692
        {
 
3693
            extrapolator_ = 0;
 
3694
        }
 
3695
    }
 
3696
 
 
3697
    // killed?
 
3698
    if (Alive() && sync_alive!=1)
 
3699
    {
 
3700
        Die( lastSyncMessage_.time );
 
3701
        MoveSafely( lastSyncMessage_.pos, lastTime, deathTime );
 
3702
        distance=lastSyncMessage_.distance;
 
3703
 
 
3704
        DropWall( false );
 
3705
 
 
3706
        tNEW(gExplosion)( grid, lastSyncMessage_.pos, lastSyncMessage_.time ,color_ );
 
3707
 
 
3708
        return;
 
3709
    }
 
3710
 
 
3711
    gDestination emergency_aft(*this);
 
3712
 
 
3713
    // all the data was read. check where it fits in our destination list:
 
3714
    gDestination *bef= GetDestinationBefore( lastSyncMessage_, destinationList );
 
3715
    gDestination *aft=NULL;
 
3716
    if (bef){
 
3717
        aft=bef->next;
 
3718
        if (!aft)
 
3719
            aft=&emergency_aft;
 
3720
 
 
3721
        //eDebugLine::SetTimeout(1);
 
3722
        //eDebugLine::SetColor( 1,0,0 );
 
3723
        //eDebugLine::Draw( bef->position, 1.5, lastSyncMessage_.pos, 3.0 );
 
3724
        //eDebugLine::SetColor( 0,1,0 );
 
3725
        //eDebugLine::Draw( lastSyncMessage_.pos, 3.0, aft->position, 5.0 );
 
3726
 
 
3727
    }
 
3728
 
 
3729
 
 
3730
    // detect local tunneling by casting a ray from the server certified position
 
3731
    // to the next known client position
 
3732
    if ( lastTime > 0 )
 
3733
    {
 
3734
        eCoord position = pos;
 
3735
        if ( bef )
 
3736
            position = bef->position;
 
3737
        eSensor tunnel( this, lastSyncMessage_.pos, position - lastSyncMessage_.pos );
 
3738
        tunnel.detect( 1 );
 
3739
 
 
3740
        // use extrapolation to undo local tunneling
 
3741
        if ( tunnel.ehit )
 
3742
        {
 
3743
            canUseExtrapolatorMethod = true;
 
3744
            if ( 0 )
 
3745
            {
 
3746
                con << "possible local tunneling detected\n";
 
3747
                eSensor tunnel( this, lastSyncMessage_.pos, position - lastSyncMessage_.pos );
 
3748
                tunnel.detect( 1 );
 
3749
            }
 
3750
        }
 
3751
    }
 
3752
 
 
3753
    // determine whether we can use the distance based interpolating sync method here
 
3754
    bool distanceBased = aft && aft != &emergency_aft && Owner() == sn_myNetID;
 
3755
 
 
3756
    if ( canUseExtrapolatorMethod && Owner()==sn_myNetID )
 
3757
    {
 
3758
        // exactlu resimulate from sync position for cycles controlled by this client
 
3759
        resimulate_ = true;
 
3760
 
 
3761
        return;
 
3762
    }
 
3763
 
 
3764
    rubber = lastSyncMessage_.rubber;
 
3765
 
 
3766
    // bool fullSync = false;
 
3767
 
 
3768
    // try to get a distance value closer to the client's data by calculating the distance from the sync position to the next destination
 
3769
    REAL interpolatedDistance = lastSyncMessage_.distance;
 
3770
    if ( aft )
 
3771
    {
 
3772
        interpolatedDistance = aft->distance - sqrt((lastSyncMessage_.pos-aft->position).NormSquared());
 
3773
    }
 
3774
 
 
3775
    // determine true lag
 
3776
    // REAL lag = 1;
 
3777
    //if ( player )
 
3778
    //    lag = player->ping;
 
3779
 
 
3780
    if ( distanceBased )
 
3781
    {
 
3782
        // new way: correct our time and speed
 
3783
 
 
3784
#ifdef DEBUG
 
3785
        // destination *save = bef;
 
3786
#endif
 
3787
 
 
3788
        REAL ratio = (interpolatedDistance - bef->distance)/
 
3789
                     (aft->distance - bef->distance);
 
3790
 
 
3791
        if (!finite(ratio))
 
3792
            ratio = 0;
 
3793
 
 
3794
        // interpolate when the cycle was at the position the sync message was sent
 
3795
        REAL interpolatedTime = bef->gameTime * (1-ratio) + aft->gameTime * ratio;
 
3796
        // REAL interpolatedSpeed = bef->speed   * (1-ratio) + aft->speed    * ratio;
 
3797
 
 
3798
        // calculate deltas
 
3799
        REAL correctTime  = ( lastSyncMessage_.time     - interpolatedTime     );
 
3800
        // REAL correctSpeed = ( lastSyncMessage_.speed    - interpolatedSpeed    );
 
3801
        REAL correctDist  = ( lastSyncMessage_.distance - interpolatedDistance );
 
3802
 
 
3803
        // don't trust high ratios too much; they may be skewed by rubber application
 
3804
        {
 
3805
            REAL factor = (1 - ratio) * 5;
 
3806
            if ( factor < 1 )
 
3807
            {
 
3808
                if ( factor < 0 )
 
3809
                    factor = 0;
 
3810
                correctTime *= factor;
 
3811
                correctDist *= factor;
 
3812
            }
 
3813
        }
 
3814
 
 
3815
        //if (correctTime > 0)
 
3816
        //{
 
3817
        //    correctTime*=.5;
 
3818
        //    correctSpeed*=.5;
 
3819
        //    correctDist*=.5;
 
3820
        //}
 
3821
 
 
3822
        //correctTimeSmooth       += correctTime;
 
3823
        //correctSpeedSmooth      += correctSpeed;
 
3824
        // correctDistanceSmooth   += correctDist;
 
3825
 
 
3826
        // correctDistanceSmooth   += correctDist;
 
3827
 
 
3828
        // con << ratio << ", " << correctDist << ", " << correctTime << "\n";
 
3829
 
 
3830
        // correct distances according to sync
 
3831
        {
 
3832
            distance += correctDist;
 
3833
            gDestination * run = bef;
 
3834
            while ( run )
 
3835
            {
 
3836
                run->distance += correctDist;
 
3837
                run = run->next;
 
3838
            }
 
3839
        }
 
3840
 
 
3841
        // correct time by adapting position and distance
 
3842
        eCoord newPos = pos - Direction() * Speed() * correctTime;
 
3843
        if ( currentWall )
 
3844
        {
 
3845
            ClampForward( newPos, currentWall->beg, Direction() );
 
3846
        }
 
3847
 
 
3848
        // don't tunnel through walls
 
3849
        {
 
3850
            const eCoord & safePos = pos; // aft->position
 
3851
            gSensor test( this, safePos , newPos - safePos );
 
3852
            test.detect(1);
 
3853
            if ( test.ehit )
 
3854
            {
 
3855
                newPos = test.before_hit;
 
3856
 
 
3857
                // something bad must be going on, better recheck with accurate extrapolation
 
3858
                resimulate_ = true;
 
3859
            }
 
3860
        }
 
3861
 
 
3862
        correctPosSmooth = correctPosSmooth + pos - newPos;
 
3863
        distance += eCoord::F( newPos - pos, Direction() );
 
3864
 
 
3865
        MoveSafely( newPos, lastTime, lastTime );
 
3866
 
 
3867
        /*
 
3868
        REAL ts = lastSyncMessage_.time - lastTime;
 
3869
 
 
3870
        //        eCoord intPos = pos + dirDrive * (ts * speed + .5 * ts*ts*acceleration);
 
3871
        eCoord intPos = pos + dirDrive * ( ts * ( speed + lastSyncMessage_.speed ) * .5 );
 
3872
        REAL  int_speed = speed + acceleration*ts;
 
3873
 
 
3874
        dirDrive = lastSyncMessage_.dir;
 
3875
 
 
3876
        correctPosSmooth = lastSyncMessage_.pos - intPos;
 
3877
 
 
3878
        distance = lastSyncMessage_.distance - speed * ts - acceleration * ts*ts*.5;
 
3879
 
 
3880
        //correctTimeSmooth = 0;
 
3881
        */
 
3882
    }
 
3883
    else
 
3884
    {
 
3885
        // direct sync
 
3886
        if ( Owner() != sn_myNetID )
 
3887
        {
 
3888
            // direct extrapolation for cycles of other clients or if no turn is newer than the sync
 
3889
            SyncEnemy( lastSyncMessage_.lastTurn );
 
3890
 
 
3891
            // update beginning of current wall
 
3892
            if ( currentWall )
 
3893
            {
 
3894
                currentWall->beg = lastSyncMessage_.lastTurn;
 
3895
            }
 
3896
 
 
3897
            // update brake status
 
3898
            braking = lastSyncMessage_.braking;
 
3899
        }
 
3900
        else
 
3901
        {
 
3902
            // same algorithm, but update smooth position correction so that there is no immediate visual change
 
3903
            eCoord oldPos = pos + correctPosSmooth;
 
3904
            SyncEnemy( lastSyncMessage_.lastTurn );
 
3905
            correctPosSmooth = oldPos - pos;
 
3906
 
 
3907
#ifdef DEBUG
 
3908
            if ( correctPosSmooth.NormSquared() > .1f && lastTime > 0.0 )
 
3909
            {
 
3910
                std::cout << "Lag slide! " << correctPosSmooth << "\n";
 
3911
                int x;
 
3912
                x = 0;
 
3913
            }
 
3914
#endif
 
3915
        }
 
3916
 
 
3917
        // restore rubber meter
 
3918
        if ( !rubberSent )
 
3919
        {
 
3920
            rubber = lastSyncMessage_.rubber;
 
3921
        }
 
3922
    }
 
3923
 
 
3924
    sn_Update(turns,lastSyncMessage_.turns);
 
3925
 
 
3926
    //if (fabs(correctTimeSmooth > 5))
 
3927
    //    st_Breakpoint();
 
3928
 
 
3929
    // calculate new winding number. Try to change it as little as possible.
 
3930
    this->SetWindingNumberWrapped( Grid()->DirectionWinding(dirDrive) );
 
3931
 
 
3932
    // Resnap to the axis
 
3933
    dirDrive = Grid()->GetDirection(windingNumberWrapped_);
 
3934
}
 
3935
 
 
3936
void gCycle::SyncEnemy ( const eCoord& )
 
3937
{
 
3938
    // keep this cycle alive
 
3939
    tJUST_CONTROLLED_PTR< gCycle > keep( this->GetRefcount()>0 ? this : 0 );
 
3940
 
 
3941
    resimulate_ = false;
 
3942
 
 
3943
    // calculate turning
 
3944
    bool turned = false;
 
3945
    REAL turnDirection=( dirDrive*lastSyncMessage_.dir );
 
3946
    REAL notTurned=eCoord::F( dirDrive, lastSyncMessage_.dir );
 
3947
 
 
3948
    // calculate the position of the last turn from the sync data
 
3949
    if ( distance > 0 && ( notTurned < .99 || this->turns < lastSyncMessage_.turns ) )
 
3950
    {
 
3951
        // reset sound
 
3952
        if (turning)
 
3953
            turning->Reset();
 
3954
 
 
3955
        // update old wall as good as we can
 
3956
        eCoord crossPos = lastSyncMessage_.pos;
 
3957
        REAL crossDist = lastSyncMessage_.distance;
 
3958
        REAL crossTime = lastSyncMessage_.time;
 
3959
 
 
3960
        // calculate intersection of old and new trajectory (if only one turn was made)
 
3961
        // the second condition is for old servers that don't send the turn count; they
 
3962
        // don't support multiple axes, so we can possibly detect missed turns if
 
3963
        // the dot product of the current and last direction is negative.
 
3964
        if (this->turns+1 >= lastSyncMessage_.turns && ( lastSyncMessage_.turns > 0 || notTurned > -.5 ) )
 
3965
        {
 
3966
            if ( fabs( turnDirection ) > .01 )
 
3967
            {
 
3968
                REAL b = ( crossPos - pos ) * dirDrive;
 
3969
                REAL distplace = b/turnDirection;
 
3970
                crossPos = crossPos + lastSyncMessage_.dir * distplace;
 
3971
                crossDist += distplace;
 
3972
                if ( lastSyncMessage_.speed > 0 )
 
3973
                    crossTime += distplace / lastSyncMessage_.speed;
 
3974
 
 
3975
                tASSERT( fabs ( ( crossPos - pos ) * dirDrive ) < 1 );
 
3976
                tASSERT( fabs ( ( crossPos - lastSyncMessage_.pos ) * lastSyncMessage_.dir ) < 1 );
 
3977
 
 
3978
                // update the old wall
 
3979
                if (currentWall)
 
3980
                    currentWall->Update(crossTime,crossPos);
 
3981
            }
 
3982
        }
 
3983
        else
 
3984
        {
 
3985
            // a turn sync was dropped for whatever reason. The last wall is not reliable.
 
3986
            // make it disappear immediately.
 
3987
            if (currentWall)
 
3988
            {
 
3989
                currentWall->real_CopyIntoGrid(grid);
 
3990
            }
 
3991
        }
 
3992
 
 
3993
        eDebugLine::SetTimeout(5);
 
3994
        eDebugLine::SetColor( 1,0,0 );
 
3995
        eDebugLine::Draw( crossPos, 0, crossPos, 8 );
 
3996
 
 
3997
        // drop old wall
 
3998
        if(currentWall){
 
3999
            lastWall=currentWall;
 
4000
            currentWall->CopyIntoGrid( grid );
 
4001
            tControlledPTR< gNetPlayerWall > bounce( currentWall );
 
4002
            currentWall=NULL;
 
4003
        }
 
4004
 
 
4005
        // create new wall at sync location
 
4006
        distance = lastSyncMessage_.distance;
 
4007
        currentWall=new gNetPlayerWall
 
4008
                    (this,crossPos,lastSyncMessage_.dir,crossTime,crossDist);
 
4009
 
 
4010
        turned = true;
 
4011
 
 
4012
        // save last driving direction
 
4013
        lastDirDrive = dirDrive;
 
4014
    }
 
4015
 
 
4016
    // side bending
 
4017
    skewDot+=4*turnDirection;
 
4018
 
 
4019
    // calculate timestep
 
4020
    // REAL ts = lastSyncMessage_.time - lastTime;
 
4021
 
 
4022
    // update position, speed, distance and direction
 
4023
    MoveSafely( lastSyncMessage_.pos, lastTime, lastTime );
 
4024
    verletSpeed_  = lastSyncMessage_.speed;
 
4025
    lastTimestep_ = 0;
 
4026
    distance = lastSyncMessage_.distance;
 
4027
    dirDrive = lastSyncMessage_.dir;
 
4028
    rubber = lastSyncMessage_.rubber;
 
4029
    brakingReservoir = lastSyncMessage_.brakingReservoir;
 
4030
 
 
4031
    // update time to values from sync
 
4032
    REAL oldTime = lastTime;
 
4033
    lastTime = lastSyncMessage_.time;
 
4034
    if ( oldTime < 0 )
 
4035
        oldTime = lastTime;
 
4036
    clientside_action();
 
4037
 
 
4038
    // bend last driving direction if it is antiparallel to the current one
 
4039
    if ( lastDirDrive * dirDrive < EPS && eCoord::F( lastDirDrive, dirDrive ) < 0 )
 
4040
    {
 
4041
        lastDirDrive = dirDrive.Turn(0,1);
 
4042
    }
 
4043
 
 
4044
    // correct laggometer to actual facts
 
4045
    if (Owner() != sn_myNetID )
 
4046
    {
 
4047
        // calculate lag from sync delay
 
4048
        REAL lag = se_GameTime() - lastSyncMessage_.time;
 
4049
        if ( lag < 0 )
 
4050
            lag = 0;
 
4051
 
 
4052
        // try not to let the lag jump
 
4053
        REAL maxLag = laggometer * 1.2;
 
4054
        REAL minLag = laggometer * .8;
 
4055
 
 
4056
        // store it
 
4057
        laggometer = lag;
 
4058
 
 
4059
        // clamp smooth laggometer to it (or set it if a turn was made)
 
4060
        //if ( laggometerSmooth > lag )
 
4061
        //    laggometerSmooth = lag;
 
4062
 
 
4063
        // For the client and without prediction:
 
4064
        // do not simulate if a turn was made, just accept sync as it is
 
4065
        // (so turns don't look awkward)
 
4066
        if (
 
4067
            sn_GetNetState()==nCLIENT && turned )
 
4068
        {
 
4069
            laggometerSmooth = lag;
 
4070
            return;
 
4071
        }
 
4072
        else
 
4073
        {
 
4074
            // sanity check: the lag should not jump up suddenly
 
4075
            // (and if it does, we'll catch up at the next turn)
 
4076
            // REAL maxLag = 1.2 * laggometerSmooth;
 
4077
            if ( laggometer > maxLag )
 
4078
                laggometer = maxLag;
 
4079
            if ( laggometer < minLag )
 
4080
                laggometer = minLag;
 
4081
        }
 
4082
    }
 
4083
 
 
4084
    // simulate to extrapolate, but don't touch the smooth laggometer
 
4085
    {
 
4086
        REAL laggometerSmoothBackup = this->laggometerSmooth;
 
4087
        TimestepThis(oldTime, this);
 
4088
        this->laggometerSmooth = laggometerSmoothBackup;
 
4089
    }
 
4090
}
 
4091
 
 
4092
/*
 
4093
void gCycle::old_ReadSync(nMessage &m){
 
4094
  REAL oldTime=lastTime;
 
4095
  eCoord oldpos=pos;
 
4096
  //+correctPosSmooth;
 
4097
  //correctPosSmooth=0;
 
4098
  eCoord olddir=dir;
 
4099
  eNetGameObject::ReadSync(m);
 
4100
 
 
4101
  REAL t=(dirDrive*dir);
 
4102
  if (fabs(t)>.2){
 
4103
#ifdef DEBUG
 
4104
    if (owner==sn_myNetID)
 
4105
      con << "Warning! Turned cycle!\n";
 
4106
#endif
 
4107
    turning.Reset();
 
4108
  }
 
4109
 
 
4110
  // side bending
 
4111
  skewDot+=4*t;
 
4112
 
 
4113
 
 
4114
  dirDrive=dir;
 
4115
  dir=olddir;
 
4116
 
 
4117
  m >> speed;
 
4118
  short new_alive;
 
4119
  m >> new_alive;
 
4120
  if (alive && new_alive!=1){
 
4121
    new gExplosion(pos,oldTime,r,g,b);
 
4122
    deathTime=oldTime;
 
4123
    eEdge::SeethroughHasChanged();
 
4124
  }
 
4125
  alive=new_alive;
 
4126
 
 
4127
  m >> distance;
 
4128
 
 
4129
  // go to old time frame
 
4130
 
 
4131
  eCoord realpos=pos;
 
4132
  REAL realtime=lastTime;
 
4133
  REAL realdist=distance;
 
4134
 
 
4135
  if (currentWall)
 
4136
    lastWall=currentWall;
 
4137
 
 
4138
  m.Read(currentWallID);
 
4139
 
 
4140
  unsigned short Turns;
 
4141
  if (!m.End())
 
4142
    m.Read(Turns);
 
4143
  else
 
4144
    Turns=turns;
 
4145
 
 
4146
  if (!m.End())
 
4147
    m.Read(braking);
 
4148
  else
 
4149
    braking=false;
 
4150
 
 
4151
  TimestepThis(oldTime,this);
 
4152
 
 
4153
  if (!currentWall || fabs(t)>.3 || currentWall->inGrid){
 
4154
    //REAL d=eCoord::F(pos-realpos,olddir);
 
4155
    //eCoord crosspos=realpos+olddir*d;
 
4156
    //d=eCoord::F(oldpos-crosspos,dir);
 
4157
    //crosspos=crosspos+dir*d;
 
4158
 
 
4159
    eCoord crosspos=realpos;
 
4160
 
 
4161
    if (currentWall){
 
4162
      currentWall->Update(realtime,crosspos);
 
4163
      //currentWall->Update(realtime,realpos);
 
4164
      currentWall->CopyIntoGrid();
 
4165
    }
 
4166
    //con << "NEW\n";
 
4167
    currentWall=new gNetPlayerWall
 
4168
      (this,crosspos,dirDrive,realtime,realdist);
 
4169
  }
 
4170
 
 
4171
 
 
4172
  // smooth correction
 
4173
  if ((oldpos-pos).NormSquared()<4 && fabs(t)<.5){
 
4174
    correctPosSmooth=pos-oldpos;
 
4175
    pos=oldpos;
 
4176
  }
 
4177
  
 
4178
 
 
4179
 
 
4180
#ifdef DEBUG
 
4181
  //int old_t=turns;
 
4182
  //if(sn_Update(turns,Turns))
 
4183
  //con << "Updated turns form " << old_t << " to " << turns << "\n";
 
4184
#endif
 
4185
  sn_Update(turns,Turns);
 
4186
}
 
4187
*/
 
4188
 
 
4189
void gCycle::ReceiveControl(REAL time,uActionPlayer *act,REAL x){
 
4190
    //forceTime=true;
 
4191
    TimestepThis(time,this);
 
4192
    Act(act,x);
 
4193
    //forceTime=false;
 
4194
}
 
4195
 
 
4196
void gCycle::PrintName(tString &s) const
 
4197
{
 
4198
    s << "gCycle nr. " << ID();
 
4199
    if ( this->player )
 
4200
    {
 
4201
        s << " owned by ";
 
4202
        this->player->PrintName( s );
 
4203
    }
 
4204
}
 
4205
 
 
4206
bool gCycle::ActionOnQuit()
 
4207
{
 
4208
    //  currentWall = NULL;
 
4209
    //  lastWall = NULL;
 
4210
    if ( sn_GetNetState() == nSERVER )
 
4211
    {
 
4212
        TakeOwnership();
 
4213
        Kill();
 
4214
        return false;
 
4215
    }
 
4216
    else
 
4217
    {
 
4218
        return true;
 
4219
    }
 
4220
}
 
4221
 
 
4222
 
 
4223
nDescriptor &gCycle::CreatorDescriptor() const{
 
4224
    return cycle_init;
 
4225
}
 
4226
 
 
4227
/*
 
4228
void gCycle::AddDestination(gDestination *dest){
 
4229
    //  con << "got new dest " << dest->position << "," << dest->direction
 
4230
    // << "," << dest->distance << "\n";
 
4231
 
 
4232
    dest->InsertIntoList(&destinationList);
 
4233
 
 
4234
    // if the new destination was inserted at the end of the list
 
4235
    //if (!currentDestination && !dest->next &&
 
4236
    //if ((dest->distance >= distance || (!currentDestination && !dest->next)) &&
 
4237
 
 
4238
    if (dest->next && dest->next->hasBeenUsed){
 
4239
        delete dest;
 
4240
        return;
 
4241
    }
 
4242
 
 
4243
    // go back one destination if the new destination appears to be older than the current one
 
4244
    if ((!currentDestination || currentDestination == dest->next ) &&
 
4245
            sn_GetNetState()!=nSTANDALONE && Owner()!=::sn_myNetID){
 
4246
        currentDestination=dest;
 
4247
        // con << "setting new cd\n";
 
4248
        }
 
4249
}
 
4250
*/
 
4251
 
 
4252
void gCycle::RightBeforeDeath( int numTries )
 
4253
{
 
4254
    if ( player )
 
4255
    {
 
4256
        player->RightBeforeDeath( numTries );
 
4257
    }
 
4258
 
 
4259
    // delay syncs for old clients; they would tunnel locally
 
4260
    if ( sg_avoidBadOldClientSync && !sg_NoLocalTunnelOnSync.Supported( Owner() ) )
 
4261
    {
 
4262
        nextSync=tSysTimeFloat()+sg_syncIntervalEnemy;
 
4263
        nextSyncOwner=tSysTimeFloat()+sg_GetSyncIntervalSelf( this );
 
4264
    }
 
4265
 
 
4266
    correctPosSmooth = correctPosSmooth * .5;
 
4267
}
 
4268
 
 
4269
// *******************************************************************************************
 
4270
// *
 
4271
// *    DoIsDestinationUsed
 
4272
// *
 
4273
// *******************************************************************************************
 
4274
//!
 
4275
//!             @param  dest    the destination to test
 
4276
//!             @return                 true if the destination is still in active use
 
4277
//!
 
4278
// *******************************************************************************************
 
4279
 
 
4280
bool gCycle::DoIsDestinationUsed( const gDestination * dest ) const
 
4281
{
 
4282
    return ( extrapolator_ && extrapolator_->IsDestinationUsed( dest ) ) || gCycleMovement::DoIsDestinationUsed( dest );
 
4283
}
 
4284
 
 
4285
// *******************************************************************************************
 
4286
// *
 
4287
// *   DoGetDistanceSinceLastTurn
 
4288
// *
 
4289
// *******************************************************************************************
 
4290
//!
 
4291
//!        @return     the distance driven since the last turn
 
4292
//!
 
4293
// *******************************************************************************************
 
4294
 
 
4295
/*
 
4296
REAL gCycle::DoGetDistanceSinceLastTurn( void ) const
 
4297
{
 
4298
    if ( currentWall )
 
4299
    {
 
4300
        return ( currentWall->Pos(1) - currentWall->Pos(0) );
 
4301
    }
 
4302
 
 
4303
    return 0;
 
4304
}
 
4305
*/
 
4306
 
 
4307
// *******************************************************************************************
 
4308
// *
 
4309
// *   DoGetDistanceSinceLastTurn
 
4310
// *
 
4311
// *******************************************************************************************
 
4312
//!
 
4313
//!        @return     the distance driven since the last turn
 
4314
//!
 
4315
// *******************************************************************************************
 
4316
 
 
4317
/*
 
4318
REAL gCycleExtrapolator::DoGetDistanceSinceLastTurn( void ) const
 
4319
{
 
4320
    return eCoord::F( pos - lastTurn_, dirDrive );
 
4321
}
 
4322
*/
 
4323
 
 
4324
// *******************************************************************************************
 
4325
// *
 
4326
// *    Vulnerable
 
4327
// *
 
4328
// *******************************************************************************************
 
4329
//!
 
4330
//!             @return         true if the cycle is vulnerable
 
4331
//!
 
4332
// *******************************************************************************************
 
4333
 
 
4334
bool gCycle::Vulnerable() const
 
4335
{
 
4336
    return lastTime > spawnTime_ + sg_cycleInvulnerableTime;
 
4337
}