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

« back to all changes in this revision

Viewing changes to src/tron/gAIBase.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 "gAIBase.h"
 
29
#include "gArena.h"
 
30
#include "eGrid.h"
 
31
#include "ePath.h"
 
32
#include "eGameObject.h"
 
33
#include "gWall.h"
 
34
#include "gSensor.h"
 
35
#include "gCycle.h"
 
36
#include "tConsole.h"
 
37
#include "rFont.h"
 
38
#include "rScreen.h"
 
39
#include "eFloor.h"
 
40
#include "eDebugLine.h"
 
41
#include "gAICharacter.h"
 
42
#include "tReferenceHolder.h"
 
43
#include "tRandom.h"
 
44
#include "tRecorder.h"
 
45
#include <stdlib.h>
 
46
 
 
47
#define AI_REACTION          0 
 
48
#define AI_EMERGENCY         1 
 
49
#define AI_RANGE             2 
 
50
#define AI_STATE_TRACE       3 
 
51
#define AI_STATE_CLOSECOMBAT 4 
 
52
#define AI_STATE_PATH        5 
 
53
#define AI_LOOP              6 
 
54
#define AI_ENEMY             7 
 
55
#define AI_TUNNEL            8 
 
56
#define AI_DETECTTRACE       9 
 
57
#define AI_STARTSTATE        10
 
58
#define AI_STARTSTRAIGHT     11
 
59
#define AI_STATECHANGE       12
 
60
 
 
61
static tReferenceHolder< gAIPlayer > sg_AIReferences;
 
62
 
 
63
#ifdef DEBUG
 
64
//#define TESTSTATE AI_PATH
 
65
//#define TESTSTATE AI_TRACE
 
66
#endif
 
67
//#define DEBUGLINE
 
68
 
 
69
static tCONTROLLED_PTR(gAITeam) sg_AITeam = NULL;
 
70
 
 
71
static gAITeam* AITeam()
 
72
{
 
73
    if ( !sg_AITeam )
 
74
    {
 
75
        sg_AITeam = tNEW( gAITeam );
 
76
    }
 
77
 
 
78
    return sg_AITeam;
 
79
}
 
80
 
 
81
static void ClearAITeam()
 
82
{
 
83
    sg_AITeam = NULL;
 
84
}
 
85
 
 
86
// an instance of this class will prevent deterministic random lookups
 
87
class gRandomController
 
88
{
 
89
public:
 
90
    static gRandomController * random_;
 
91
    gRandomController * lastRandom_;
 
92
    tRandomizer & randomizer_;
 
93
 
 
94
    gRandomController( tRandomizer & randomizer = tRandomizer::GetInstance() )
 
95
            : lastRandom_( random_ ), randomizer_( randomizer )
 
96
    {
 
97
        random_ = this;
 
98
    }
 
99
 
 
100
    ~gRandomController()
 
101
    {
 
102
        random_ = lastRandom_;
 
103
    }
 
104
};
 
105
 
 
106
gRandomController * gRandomController::random_ = 0;
 
107
 
 
108
REAL Random()
 
109
{
 
110
    if ( gRandomController::random_ )
 
111
    {
 
112
        tRandomizer & randomizer = gRandomController::random_->randomizer_;
 
113
        return randomizer.Get();
 
114
    }
 
115
    else
 
116
    {
 
117
        tRandomizer & randomizer = tRandomizer::GetInstance();
 
118
        return randomizer.Get();
 
119
    }
 
120
}
 
121
 
 
122
static REAL Delay()
 
123
{
 
124
    REAL delay = sg_delayCycle * .9f;
 
125
 
 
126
    REAL fd    = se_AverageFrameTime()*1.5f;
 
127
    if ( fd > delay)
 
128
        delay = fd;
 
129
 
 
130
    return delay;
 
131
}
 
132
 
 
133
 
 
134
 
 
135
static gAICharacter* BestIQ( int iq )
 
136
{
 
137
    int i;
 
138
 
 
139
    static tArray<bool> inGame(gAICharacter::s_Characters.Len());
 
140
    for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
 
141
        inGame(i) = false;
 
142
 
 
143
    for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
 
144
    {
 
145
        // count the active AIs
 
146
        ePlayerNetID *p = se_PlayerNetIDs(i);
 
147
        gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
 
148
        if (ai && ai->Character() )
 
149
        {
 
150
            int index = ai->Character() - &(gAICharacter::s_Characters(0));
 
151
            inGame(index) = true;
 
152
        }
 
153
    }
 
154
 
 
155
    // find the best fitting IQ that could be inserted:
 
156
    gAICharacter* bestIQ = 0;
 
157
    for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
 
158
        if (!inGame(i))
 
159
            if (!bestIQ || fabs(bestIQ->iq - iq) > fabs(gAICharacter::s_Characters(i).iq - iq))
 
160
                bestIQ = &gAICharacter::s_Characters(i);
 
161
 
 
162
    return bestIQ;
 
163
}
 
164
 
 
165
 
 
166
 
 
167
gAITeam::gAITeam(nMessage &m) : eTeam(m)
 
168
{
 
169
    //  teams.Remove( this, listID );
 
170
}
 
171
 
 
172
gAITeam::gAITeam()
 
173
{
 
174
    //  teams.Remove( this, listID );
 
175
}
 
176
 
 
177
static nNOInitialisator<gAITeam> gAITeam_init(331,"gAITeam");
 
178
 
 
179
nDescriptor &gAITeam::CreatorDescriptor() const
 
180
{
 
181
    return gAITeam_init;
 
182
}
 
183
 
 
184
// fill empty team positions with AI players
 
185
void gAITeam::BalanceWithAIs(bool balanceWithAIs)
 
186
{
 
187
    // set correct team number
 
188
    EnforceConstraints();
 
189
 
 
190
    int numTeams = 0, numTeamsWithPlayers = 0;
 
191
 
 
192
    // determine the maximum number of human players on a team
 
193
    int i;
 
194
    int maxP = eTeam::minPlayers;
 
195
    for ( i = teams.Len()-1; i>=0; --i )
 
196
    {
 
197
        eTeam *t = teams(i);
 
198
 
 
199
        t->UpdateProperties();
 
200
 
 
201
        if ( t->BalanceThisTeam() )
 
202
            numTeams++;
 
203
 
 
204
        if ( t->NumHumanPlayers() > 0 )
 
205
            numTeamsWithPlayers++;
 
206
 
 
207
        int humans = t->NumHumanPlayers();
 
208
 
 
209
        if ( humans > maxP )
 
210
        {
 
211
            maxP = humans;
 
212
        }
 
213
    }
 
214
 
 
215
    // make sure all teams have equal number
 
216
    for ( i = teams.Len()-1; i>=0; --i )
 
217
    {
 
218
        eTeam *t = teams(i);
 
219
 
 
220
        if ( t->BalanceThisTeam() )
 
221
        {
 
222
            int wishAIs = maxP - t->NumHumanPlayers();
 
223
 
 
224
            // don't add AI players to human team if it is not requested
 
225
            if ( !balanceWithAIs && t->NumHumanPlayers() > 0 )
 
226
                wishAIs = 0;
 
227
 
 
228
            // don't genereate AI only teams if it is not requested by the min team count
 
229
            if ( numTeamsWithPlayers >= minTeams && t->NumHumanPlayers() == 0 )
 
230
                wishAIs = 0;
 
231
 
 
232
            // add AI only teams to team count
 
233
            if ( wishAIs > 0 && t->NumHumanPlayers() == 0 )
 
234
                numTeamsWithPlayers++;
 
235
 
 
236
            int AIs = t->NumAIPlayers();
 
237
 
 
238
            while ( AIs > wishAIs )
 
239
            {
 
240
                int j;
 
241
                gAIPlayer* throwOut = NULL;
 
242
                for ( j=t->NumPlayers()-1; j>=0; --j )
 
243
                {
 
244
                    gAIPlayer* player = dynamic_cast<gAIPlayer*>( t->Player( j ) );
 
245
                    if ( player && !player->IsHuman() && ( !throwOut || !throwOut->Character() || !player->Character() || throwOut->Character()->iq > player->Character()->iq ) )
 
246
                    {
 
247
                        throwOut = player;
 
248
                    }
 
249
                }
 
250
 
 
251
                if ( throwOut )
 
252
                {
 
253
                    throwOut->RemoveFromGame();
 
254
                    //                                  throwOut->ReleaseOwnership();
 
255
 
 
256
#ifdef DEBUG
 
257
                    if ( throwOut->GetRefcount() > 1 )
 
258
                    {
 
259
                        st_Breakpoint();
 
260
                    }
 
261
#endif
 
262
 
 
263
                    sg_AIReferences.Remove( throwOut );
 
264
                }
 
265
 
 
266
                --AIs;
 
267
            }
 
268
 
 
269
            while ( AIs < wishAIs )
 
270
            {
 
271
                // add the smartest AI player you can get
 
272
                gAICharacter* best = BestIQ( 1000 );
 
273
 
 
274
                if ( best )
 
275
                {
 
276
                    gAIPlayer *ai       = tNEW( gAIPlayer ) ();
 
277
                    ai->character       = best;
 
278
                    ai->SetName( best->name );
 
279
                    ai->SetTeam( t );
 
280
                    ai->UpdateTeam();
 
281
 
 
282
                    sg_AIReferences.Add( ai );
 
283
                }
 
284
 
 
285
                ++AIs;
 
286
            }
 
287
        }
 
288
    }
 
289
 
 
290
    // get rid of deleted netobjects (teams, mostly)
 
291
    nNetObject::ClearAllDeleted();
 
292
}
 
293
 
 
294
// may player join this team?
 
295
bool gAITeam::PlayerMayJoin(const ePlayerNetID* player) const
 
296
{
 
297
    return !player->IsHuman();
 
298
}
 
299
 
 
300
 
 
301
// this class describes a single wall close event
 
302
class gCycleTouchEvent{
 
303
public:
 
304
    REAL dist;      // the position on this cycle's wall the touching happened
 
305
    REAL otherDist; // the position on the other cylce's wall
 
306
    int  otherSide; // the side of the other cylce this wall touches
 
307
    int  winding;   // winding number to add if we cross here
 
308
 
 
309
    gCycleTouchEvent()
 
310
    {
 
311
        dist      = 0;
 
312
        otherDist = 0;
 
313
        otherSide = -100;
 
314
    }
 
315
};
 
316
 
 
317
class gCycleMemoryEntry{
 
318
public:
 
319
    gCycleMemory *memory;
 
320
    int id;
 
321
    nObserverPtr< gCycle > cycle;
 
322
 
 
323
    gCycleMemoryEntry(gCycleMemory* m, const gCycle* c)
 
324
            :memory(m),id(-1), cycle(c)
 
325
    {
 
326
        memory->memory.Add(this, id);
 
327
 
 
328
        max[0].dist = -1E+30;
 
329
        max[1].dist = -1E+30;
 
330
        min[0].dist =  1E+30;
 
331
        min[1].dist =  1E+30;
 
332
    }
 
333
 
 
334
    ~gCycleMemoryEntry()
 
335
    {
 
336
        memory->memory.Remove(this, id);
 
337
    }
 
338
 
 
339
    // usable data
 
340
    gCycleTouchEvent max[2]; // latest touch event (with the given cylce)
 
341
    gCycleTouchEvent min[2]; // earliest touch event
 
342
};
 
343
 
 
344
#define TOL 4
 
345
 
 
346
// look for a closed loop in the walls if cycle a hits cycle b's wall at
 
347
// distance bDist and on side bSide.
 
348
// Look for the loop in driving direction of b if dir is 1 or to the other side of dir is 0.
 
349
// the end of the loop is reached when the wall of cycle a is driven along in
 
350
// direction aEndDir, passing the distance aEndDist and the side's bit is set
 
351
// in aEndSides.
 
352
// Cycles that will be closed in the loop are stored in the array
 
353
// closedIn.
 
354
// return value: the number of open points this loop contains
 
355
// (if this is >0, that usually means the space is wide open)
 
356
//  or -1 if there is no loop.
 
357
static bool CheckLoop(const gCycle *a, const gCycle *b,
 
358
                      REAL bDist, int bSide, int dir,
 
359
                      tArray<const gCycle*>& closedIn, int& winding,
 
360
                      REAL aEndDist = 0, int aEndSides = 3, int aEndDir = 1 )
 
361
{
 
362
    tASSERT(0<= bSide && 1 >= bSide);
 
363
    tASSERT(0<= dir && 1 >= dir);
 
364
 
 
365
    int tries = 10;       // so long until we give up
 
366
    int ends  = 0;
 
367
 
 
368
    bool bClosedIn    = false;
 
369
 
 
370
    const gCycle *run = b;      // we run along this cycle's wall
 
371
    int end           = dir;    // and move towards this end its wall wall
 
372
    int side          = bSide;  // we are on this side of the cycle
 
373
    REAL dist         = bDist;  // and are at this distance.
 
374
    winding           = 0;      // the winding number we collected
 
375
 
 
376
    int turn     = a->Grid()->WindingNumber();
 
377
    int halfTurn = turn >> 1;
 
378
 
 
379
 
 
380
#ifdef DEBUG
 
381
    //  con << "\n";
 
382
#endif
 
383
 
 
384
    while(tries-- > 0 && run &&
 
385
            !(run == a &&
 
386
              end == aEndDir &&
 
387
              aEndSides & (1 << side) &&
 
388
              (end > 0 ? dist >= aEndDist : dist <= aEndDist ) ) )
 
389
    {
 
390
#ifdef DEBUG
 
391
        //      con << "end = " << end << ", side = " << side << ", dist = " << dist
 
392
        //        << ", winding = " << winding << "\n";
 
393
#endif
 
394
        if (end > 0)
 
395
        {
 
396
 
 
397
            // find the last connection
 
398
            gCycleMemoryEntry* last = run->memory.Latest(side);
 
399
            if (!last || last->max[side].dist <= dist + TOL)
 
400
            {
 
401
                // no interference. We can move directly around the cylce
 
402
                // and close it in.
 
403
#ifdef DEBUG
 
404
                //            con << "Turning around...\n";
 
405
#endif
 
406
 
 
407
                winding += halfTurn *
 
408
                           ( side > 0 ? -1 : 1);
 
409
 
 
410
                end  = 0;
 
411
                side = 1-side;
 
412
                closedIn[closedIn.Len()] = run;
 
413
                dist = run->GetDistance();
 
414
 
 
415
                // detect early loop
 
416
                if (run == b)
 
417
                    if (bClosedIn)
 
418
                    {
 
419
                        winding = 0;
 
420
                        return false;
 
421
                    }
 
422
                    else
 
423
                        bClosedIn = true;
 
424
            }
 
425
            else
 
426
            {
 
427
#ifdef DEBUG
 
428
                //            con << "Crossing...\n";
 
429
#endif
 
430
 
 
431
                // find the first connection
 
432
                gCycleMemoryEntry* first = run->memory.Earliest(side);
 
433
                if (first && first->min[side].dist >= dist + TOL)
 
434
                {
 
435
                    // we cross the connection:
 
436
                    winding += first->min[side].winding;
 
437
                    run      = first->cycle;
 
438
                    end      = (side == first->min[side].otherSide) ? 1 : 0;
 
439
 
 
440
                    //            if (end == 0)
 
441
                    // we need to turn around to follow
 
442
                    winding += halfTurn * (side > 0 ? 1 : -1);
 
443
 
 
444
                    dist     = first->min[side].otherDist;
 
445
                    side     = first->min[side].otherSide;
 
446
                }
 
447
                else
 
448
                {
 
449
                    winding = 0;
 
450
                    return false;
 
451
                }
 
452
            }
 
453
        }
 
454
        else // dir = -1, we move towards the end
 
455
        {
 
456
            // find the first connection
 
457
            gCycleMemoryEntry* first = run->memory.Earliest(side);
 
458
            if (!first || first->min[side].dist >= dist - TOL)
 
459
            {
 
460
#ifdef DEBUG
 
461
                //            con << "Turning around...\n";
 
462
#endif
 
463
 
 
464
 
 
465
                // no interference. We can move directly around the cylce's end.
 
466
                winding += halfTurn * ( side > 0 ? 1 : -1);
 
467
 
 
468
                end  = 1;
 
469
                side = 1-side;
 
470
                ends++;
 
471
                dist = -2 * TOL;
 
472
            }
 
473
            else
 
474
            {
 
475
#ifdef DEBUG
 
476
                //            con << "Crossing...\n";
 
477
#endif
 
478
 
 
479
 
 
480
                // find the latest connection
 
481
                gCycleMemoryEntry* last = run->memory.Latest(side);
 
482
                if (last && last->max[side].dist <= dist - TOL)
 
483
                {
 
484
                    // we cross the connection:
 
485
                    winding += last->max[side].winding;
 
486
 
 
487
                    // we need to turn around to start:
 
488
                    winding += halfTurn * (side > 0 ? -1 : 1);
 
489
 
 
490
                    run      = last->cycle;
 
491
                    end      = (side == last->max[side].otherSide) ? 0 : 1;
 
492
 
 
493
                    //            if (end == 1)
 
494
                    // we need to turn around to follow
 
495
                    //            winding -= halfTurn * (side > 0 ? 1 : -1);
 
496
 
 
497
                    dist     = last->max[side].otherDist;
 
498
                    side     = last->max[side].otherSide;
 
499
                }
 
500
                else
 
501
                    // uh oh. we are already closed in. No chance...
 
502
                {
 
503
                    winding = 0;
 
504
                    return false;
 
505
                }
 
506
            }
 
507
        }
 
508
    }
 
509
 
 
510
#ifdef DEBUG
 
511
    //      con << "end = " << end << ", side = " << side << ", dist = " << dist
 
512
    //    << ", winding = " << winding << "\n\n";
 
513
#endif
 
514
 
 
515
    if (tries >= 0)
 
516
    {
 
517
        return true;
 
518
    }
 
519
    else
 
520
    {
 
521
        winding = 0;
 
522
        return false;
 
523
    }
 
524
}
 
525
 
 
526
 
 
527
// see if the given Cycle is trapped currently
 
528
static bool IsTrapped(const gCycle *trapped, const gCycle *other)
 
529
{
 
530
    tArray<const gCycle*> closedIn;
 
531
    int winding = 0;
 
532
    if (CheckLoop(trapped, trapped, trapped->GetDistance(), 1, 0, closedIn, winding, trapped->GetDistance() - 1))
 
533
    {
 
534
        if (winding + 2 < 0)
 
535
        {
 
536
            // see if the other cylce is trapped with him
 
537
            for (int i = closedIn.Len()-1; i>=0; i--)
 
538
                if (other == closedIn(i))
 
539
                    return false;  // we can get him!
 
540
 
 
541
            // no. trapped is trapped allone.
 
542
            return true;
 
543
        }
 
544
    }
 
545
 
 
546
    return false;
 
547
}
 
548
 
 
549
 
 
550
 
 
551
 
 
552
// data about a loop
 
553
class gLoopData{
 
554
public:
 
555
    bool loop;                       // is there a loop?
 
556
    int  winding;                    // in what direction does it go?
 
557
    tArray<const gCycle*>closedIn;   // which cycles are closed in?
 
558
 
 
559
    gLoopData():loop(false), winding(0){}
 
560
    void AddCycle(const gCycle* c){closedIn[closedIn.Len()] = c;}
 
561
};
 
562
 
 
563
// hit data
 
564
class gHitData{
 
565
public:
 
566
    const eHalfEdge*  edge;      // edge we hit
 
567
    gSensorWallType   wallType;  // type of the wall hit
 
568
    int               lr;        // does the wall go left or right?
 
569
    REAL              distance;  // distance to the wall
 
570
 
 
571
    // additional info if the wall that got hit is a cycle wall
 
572
    gCycle         *otherCycle;    // the cylce that the hit wall belongs to
 
573
    REAL            driveDistance; // the distance it had travelled when it was at the place we hit
 
574
    int             windingNumber; // the winding number at the place hit
 
575
 
 
576
    gHitData():edge(NULL), wallType(gSENSOR_NONE), otherCycle(NULL){}
 
577
 
 
578
    bool Hit() const {return edge;}
 
579
 
 
580
    void AddHit(const eCoord& origin, const eCoord& dir, const gSensor& sensor, int winding)
 
581
    {
 
582
        if (!sensor.ehit)
 
583
            return; // no hit, nothing to do
 
584
 
 
585
        /*
 
586
        REAL otherDist = eCoord::F(*sensor.ehit->Point(), dir)
 
587
                         - eCoord::F(origin, dir);
 
588
 
 
589
        {
 
590
            REAL otherDist2 = eCoord::F(*sensor.ehit->Other()->Point(), dir)
 
591
                              - eCoord::F(origin, dir);
 
592
            if (otherDist2 < otherDist)
 
593
                otherDist = otherDist2;
 
594
        }
 
595
        */
 
596
        REAL otherDist = eCoord::F( dir, sensor.before_hit - origin );
 
597
 
 
598
        if (otherDist < EPS)
 
599
            return; // the new hit is a wall we apparently drive along; nothing to do
 
600
 
 
601
        if (Hit() && distance < otherDist)
 
602
            return; // the hit that is already stored is more relevant. Ignore the new hit.
 
603
 
 
604
        // copy the relevant data
 
605
        edge     = sensor.ehit;
 
606
        wallType = sensor.type;
 
607
        lr       = sensor.lr;
 
608
        distance = otherDist;
 
609
 
 
610
        REAL alpha = edge->Ratio(sensor.before_hit);
 
611
 
 
612
        // get the extra information from the wall we hit:
 
613
        gPlayerWall *w = dynamic_cast<gPlayerWall*>(edge->GetWall());
 
614
        if (!w)
 
615
        {
 
616
            alpha = 1-alpha;
 
617
            w = dynamic_cast<gPlayerWall*>(edge->Other()->GetWall());
 
618
        }
 
619
 
 
620
        if (w)
 
621
        {
 
622
            otherCycle     = w->Cycle();
 
623
            driveDistance  = w->Pos(alpha);
 
624
            windingNumber  = w->WindingNumber() - winding;
 
625
        }
 
626
    }
 
627
};
 
628
 
 
629
// special sensor that scans a broader area (not just a raycast)
 
630
class gAISensor{
 
631
public:
 
632
    // the raw data we collect:
 
633
 
 
634
    const gCycle* cycle; // the cycle that sent out this sensor
 
635
 
 
636
    bool     hit;       // whether a dangerous spot is hit
 
637
 
 
638
    gHitData front;      // what happens in our front
 
639
    gHitData sides[2];   // and on our sides
 
640
 
 
641
    // what we make of it:
 
642
    gLoopData frontLoop[2];   // does the front wall we hit cause us to be trapped if we turn left or right?
 
643
    gLoopData sideLoop[2][2]; // do the two side walls cause us to be trapped if we turn/drive straight on?
 
644
 
 
645
    REAL distance;            // distance to the closest wall
 
646
 
 
647
    bool Hit() const
 
648
    {
 
649
        return hit;
 
650
    }
 
651
 
 
652
    void DetectLoop(const gHitData& hit, gLoopData loopData[2])
 
653
    {
 
654
        // avoid loops:
 
655
        gCycle *other = hit.otherCycle;
 
656
 
 
657
        if (other)
 
658
        {
 
659
            //    if (other!= Object())
 
660
            {
 
661
                REAL    dist  = hit.driveDistance;
 
662
                int otherSide = hit.lr < 0 ? 0 : 1;
 
663
                for (int i=1; i>=0; i--)
 
664
                {
 
665
                    loopData[i].closedIn.SetLen(0);
 
666
                    loopData[i].loop = false;
 
667
 
 
668
                    int lr   = i+i-1;
 
669
                    int dir  = hit.lr * lr > 0 ? 1 : 0;
 
670
                    int winding = 0;
 
671
                    bool loop = CheckLoop(cycle, other,
 
672
                                          dist, otherSide, dir,
 
673
                                          loopData[i].closedIn, winding);
 
674
 
 
675
                    if (loop)
 
676
                    {
 
677
                        // complete the winding calculation: the target winding
 
678
                        // is the one of this cycle:
 
679
                        winding += cycle->WindingNumber();
 
680
                        // and the source is the winding of the wall we hit
 
681
                        winding -= hit.windingNumber;
 
682
 
 
683
                        //                    if (dir == 0)
 
684
                        //                      winding -= lr * (Object()->Grid()->WindingNumber() >> 1);
 
685
 
 
686
                        winding += lr;
 
687
 
 
688
#ifdef DEBUG_X
 
689
                        if (winding != 4 && winding != -4)
 
690
                        {
 
691
                            gRandomController noRandom;
 
692
                            if (winding != 0)
 
693
                                //                      st_Breakpoint();
 
694
 
 
695
                                winding = 0;
 
696
                            CheckLoop(cycle, other,
 
697
                                      dist, otherSide, dir,
 
698
                                      loopData[i].closedIn, winding);
 
699
 
 
700
                            winding += cycle->WindingNumber();
 
701
                            winding -= hit.windingNumber;
 
702
                            winding += lr;
 
703
                        }
 
704
#endif
 
705
 
 
706
#ifdef DEBUG
 
707
                        //                    con << "winding = " << winding << " ,direction = " << lr << "\n";
 
708
#endif
 
709
                        // if the winding continues the direction we would turn in,
 
710
                        // we're trapped.
 
711
                        if (winding * lr > 0)
 
712
                            loopData[i].loop = true;
 
713
 
 
714
                        loopData[i].winding = winding;
 
715
                    }
 
716
                }
 
717
            }
 
718
        }
 
719
    }
 
720
 
 
721
    gAISensor(const gCycle* c,
 
722
              const eCoord& start, const eCoord& dir,
 
723
              REAL sideScan, // the amout of space we should scan left and right
 
724
              REAL frontScan, // the total front scanning range
 
725
              REAL corridorScan, // the corridor scanning range
 
726
              int winding        // direction relative to the cycle's driving direction
 
727
             )
 
728
            :cycle(c), distance(frontScan*2)
 
729
    {
 
730
        gAIPlayer* ai = dynamic_cast<gAIPlayer*>(c->Player());
 
731
        tASSERT(ai);
 
732
        gAICharacter* character = ai->Character();
 
733
        tASSERT(character);
 
734
 
 
735
        hit = false;
 
736
 
 
737
        eDebugLine::SetTimeout(.5);
 
738
 
 
739
        gCycle* cycle = const_cast<gCycle*>( this->cycle );
 
740
 
 
741
        // detect straight ahead
 
742
        gSensor ahead(cycle, start, dir);
 
743
 
 
744
        int count = 0;
 
745
 
 
746
        do{
 
747
            ahead.detect(frontScan);
 
748
            front.AddHit(start, dir, ahead, winding);
 
749
            if (front.Hit())
 
750
            {
 
751
                hit = true;
 
752
                distance = front.distance;
 
753
                if (character->properties[AI_LOOP] > 3 + fabsf(winding) * 3)
 
754
                    DetectLoop(front, frontLoop);
 
755
            }
 
756
        } while (!front.Hit() && count++ < character->properties[AI_RANGE]);
 
757
 
 
758
        // adapt the corridor distance so the corridor is not looked for too far away
 
759
        if (distance*.99f < corridorScan)
 
760
            corridorScan = distance * .99f;
 
761
        corridorScan -= sideScan * .02;
 
762
        if (corridorScan < 0.1f)
 
763
            corridorScan = 0.1f;
 
764
 
 
765
        if (Random() * 10 < character->properties[AI_TUNNEL])
 
766
        {
 
767
            // check the sides
 
768
            eCoord lookTunnel = start + dir * corridorScan;
 
769
 
 
770
            int i;
 
771
 
 
772
            for (i = 1; i>=0; i--)
 
773
            {
 
774
                int lr       = i+i-1;
 
775
                eCoord lrDir = dir.Turn(eCoord(.01f, -lr));
 
776
 
 
777
                gSensor side(cycle, start, lrDir);
 
778
                side.detect(sideScan*1.01f);
 
779
                REAL thisSideScan = side.hit*.99f;
 
780
 
 
781
                gSensor tunnel(cycle, lookTunnel, lrDir);
 
782
                tunnel.detect(thisSideScan);
 
783
                sides[i].AddHit(start, dir, tunnel, winding + lr);
 
784
 
 
785
                gSensor parallel(cycle, start + lrDir * thisSideScan, dir);
 
786
                parallel.detect(corridorScan);
 
787
                sides[i].AddHit(start, dir, parallel, winding);
 
788
 
 
789
                if (sides[i].Hit())
 
790
                {
 
791
                    if (character->properties[AI_LOOP] > 6 + fabsf(winding) * 3)
 
792
                        DetectLoop(sides[i], sideLoop[i]);
 
793
 
 
794
                    if (sideLoop[i][1-i].loop ||
 
795
                            (character->properties[AI_TUNNEL] >= 10 &&
 
796
                             sides[i].otherCycle &&
 
797
                             sides[i].otherCycle->Team() != cycle->Team() &&
 
798
                             sides[i].lr * (i+i-1) < 0 &&
 
799
                             sides[i].otherCycle->GetDistance() < sides[i].driveDistance + sides[i].otherCycle->Speed() * 20)
 
800
                       )
 
801
                    {
 
802
                        if (sides[i].distance < distance)
 
803
                            distance = sides[i].distance;
 
804
 
 
805
                        hit = true;
 
806
                    }
 
807
                }
 
808
 
 
809
#ifdef DEBUG_X
 
810
                {
 
811
                    gRandomController noRandom;
 
812
                    gSensor ahead(cycle, start, dir);
 
813
                    ahead.detect(frontScan);
 
814
 
 
815
                    gSensor side(cycle, start, lrDir);
 
816
                    side.detect(sideScan*1.01f);
 
817
                    REAL thisSideScan = side.hit*.99f;
 
818
 
 
819
                    gSensor tunnel(cycle, lookTunnel, lrDir);
 
820
                    tunnel.detect(thisSideScan);
 
821
                    //    sides[i].AddHit(start, dir, tunnel, winding + lr);
 
822
 
 
823
                    gSensor parallel(cycle, start + lrDir * thisSideScan, dir);
 
824
                    parallel.detect(corridorScan);
 
825
                    // sides[i].AddHit(start, dir, parallel, winding);
 
826
                }
 
827
#endif
 
828
 
 
829
            }
 
830
        }
 
831
        if (sides[1].otherCycle == sides[0].otherCycle && sides[0].otherCycle)
 
832
            hit = true;
 
833
 
 
834
#ifdef DEBUGLINE
 
835
        if (Hit())
 
836
        {
 
837
            eDebugLine::SetColor  (1, 1, 1);
 
838
            eDebugLine::SetTimeout(1);
 
839
            eDebugLine::Draw(start, .5, start + dir*distance, .5);
 
840
 
 
841
            eDebugLine::SetColor  (1, .5, 1);
 
842
            eDebugLine::Draw(start + dir*distance, .5, start + dir*distance, 1.5);
 
843
        }
 
844
#endif
 
845
 
 
846
        eDebugLine::SetTimeout(0);
 
847
 
 
848
    }
 
849
 
 
850
};
 
851
 
 
852
 
 
853
 
 
854
 
 
855
 
 
856
 
 
857
 
 
858
 
 
859
gCycleMemoryEntry* gCycleMemory::Latest (int side)  const
 
860
{
 
861
    side = (side > 0 ? 1 : 0);
 
862
    gCycleMemoryEntry* ret = NULL;
 
863
    for (int i=memory.Len()-1; i>=0; i--)
 
864
    {
 
865
        gCycleMemoryEntry* m = memory(i);
 
866
        if ((!ret || (m->max[side].dist > ret->max[side].dist)
 
867
                && bool( m->cycle ) && m->cycle->Alive() ))
 
868
            ret = memory(i);
 
869
    }
 
870
 
 
871
    return ret;
 
872
}
 
873
 
 
874
gCycleMemoryEntry* gCycleMemory::Earliest (int side)  const
 
875
{
 
876
    side = (side > 0 ? 1 : 0);
 
877
    gCycleMemoryEntry* ret = NULL;
 
878
    for (int i=memory.Len()-1; i>=0; i--)
 
879
    {
 
880
        gCycleMemoryEntry* m = memory(i);
 
881
        if ((!ret || (m->min[side].dist < ret->min[side].dist)
 
882
                && bool( m->cycle ) && m->cycle->Alive()))
 
883
            ret = memory(i);
 
884
    }
 
885
    return ret;
 
886
}
 
887
 
 
888
 
 
889
gCycleMemoryEntry* gCycleMemory::Remember(const gCycle *cycle)
 
890
{
 
891
    for (int i=memory.Len()-1; i>=0; i--)
 
892
        if (memory(i)->cycle == cycle)
 
893
            return memory(i);
 
894
 
 
895
    return tNEW(gCycleMemoryEntry)(this, cycle);
 
896
}
 
897
 
 
898
gCycleMemory::gCycleMemory()
 
899
{
 
900
}
 
901
 
 
902
 
 
903
gCycleMemory::~gCycleMemory()
 
904
{
 
905
    Clear();
 
906
}
 
907
 
 
908
void gCycleMemory::Clear()
 
909
{
 
910
    for (int i = memory.Len()-1; i>=0; i--)
 
911
        delete memory(i);
 
912
}
 
913
 
 
914
gCycleMemoryEntry* gCycleMemory::operator()(int i) const
 
915
{
 
916
    tASSERT(0 <= i && i < Len());
 
917
    return memory(i);
 
918
}
 
919
 
 
920
 
 
921
// deeper analysis functions:
 
922
 
 
923
// helper function for gAIPlayer::CycleBlocksWay()
 
924
static void CycleBlocksWayHelper(const gCycle *a, const gCycle *b,
 
925
                                 int aDir, int bDir, REAL aDist, REAL bDist, int winding)
 
926
{
 
927
    gCycleMemoryEntry* aEntry = (const_cast<gCycleMemory&>(a->memory)).Remember(b);
 
928
 
 
929
    if (aDist > aEntry->max[aDir].dist)
 
930
    {
 
931
        aEntry->max[aDir].dist      = aDist;
 
932
        aEntry->max[aDir].otherSide = bDir;
 
933
        aEntry->max[aDir].otherDist = bDist;
 
934
        aEntry->max[aDir].winding   = winding;
 
935
    }
 
936
 
 
937
    if (aDist < aEntry->min[aDir].dist)
 
938
    {
 
939
        aEntry->min[aDir].dist      = aDist;
 
940
        aEntry->min[aDir].otherSide = bDir;
 
941
        aEntry->min[aDir].otherDist = bDist;
 
942
        aEntry->min[aDir].winding   = winding;
 
943
    }
 
944
}
 
945
 
 
946
 
 
947
 
 
948
 
 
949
// called whenever cylce a drives close to the wall of cylce b.
 
950
// directions: aDir tells whether the wall is to the left (-1) or right(1)
 
951
// of a
 
952
// bDir tells the direction the wall of b is going (-1: to the left, 1:...)
 
953
// bDist is the distance of b's wall to its start.
 
954
void gAIPlayer::CycleBlocksWay(const gCycle *a, const gCycle *b,
 
955
                               int aDir, int bDir, REAL bDist, int winding)
 
956
{
 
957
    tASSERT(a && b);
 
958
 
 
959
    REAL aDist = a->GetDistance();
 
960
    aDir = (aDir > 0 ? 1 : 0);
 
961
    bDir = (bDir > 0 ? 1 : 0);
 
962
 
 
963
    int w = winding + aDir * 2 + bDir * 2;
 
964
    while (w < 0)
 
965
        w+=400;
 
966
    if (w % 4 != 2)
 
967
        return;
 
968
 
 
969
    CycleBlocksWayHelper(a,b,aDir,bDir,aDist,bDist,  winding);
 
970
    CycleBlocksWayHelper(b,a,bDir,aDir,bDist,aDist, -winding);
 
971
 
 
972
    // what to do if cylce a tries to trace cylce b?
 
973
    if (a->Team() != b->Team())
 
974
    {
 
975
        gAIPlayer* ai = dynamic_cast<gAIPlayer*>(b->Player());
 
976
        if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 5)
 
977
            if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 &&
 
978
                    ai->lastChangeAttempt < se_GameTime() - 5 )
 
979
            {
 
980
                REAL behind = b->GetDistance() - bDist;
 
981
                if (a->Speed() > b->Speed() * 1.2f && behind < (a->Speed() - b->Speed()) * 10)
 
982
                { // a is faster. Try to escape.
 
983
                    ai->SetTraceSide(aDir > 0 ? 1 : -1);
 
984
                    ai->SwitchToState(AI_TRACE, 10);
 
985
                    ai->target = const_cast< gCycle * >( a );
 
986
                }
 
987
                else// if (a->Speed() < b->Speed() * 1.1f)
 
988
                { // b is faster. Attack.
 
989
                    ai->SetTraceSide(aDir > 0 ? -1 : 1);
 
990
                    ai->SwitchToState(AI_TRACE, 10 + behind / ( a->Speed() + b->Speed() ) );
 
991
                    ai->target = const_cast< gCycle * >( a );
 
992
                }
 
993
            }
 
994
 
 
995
        // what to do if the AI player traces his opponent by accident? Trace On!
 
996
        ai = dynamic_cast<gAIPlayer*>(a->Player());
 
997
        if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 0)
 
998
            if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 &&
 
999
                    ai->lastChangeAttempt < se_GameTime() - 5 )
 
1000
            {
 
1001
                REAL behind = b->GetDistance() - bDist;
 
1002
                ai->SetTraceSide(aDir > 0 ? 1 : -1);
 
1003
                ai->SwitchToState(AI_TRACE, 10 + 4 * behind / a->Speed());
 
1004
                ai->target = const_cast< gCycle * >( b );
 
1005
            }
 
1006
    }
 
1007
}
 
1008
 
 
1009
// called whenever a cylce blocks the rim wall.
 
1010
void gAIPlayer::CycleBlocksRim(const gCycle *a, int aDir)
 
1011
{
 
1012
}
 
1013
 
 
1014
// called whenever a hole is ripped in a's wall at distance aDist.
 
1015
void gAIPlayer::BreakWall(const gCycle *a, REAL aDist)
 
1016
{}
 
1017
 
 
1018
 
 
1019
 
 
1020
 
 
1021
 
 
1022
 
 
1023
 
 
1024
 
 
1025
//#define DEBUG
 
1026
 
 
1027
// which eWall is detected?
 
1028
 
 
1029
#define MAXAI_COLOR 13
 
1030
 
 
1031
static int current_ai;
 
1032
static REAL rgb_ai[MAXAI_COLOR][3]={
 
1033
                                       {1,.2,.2},
 
1034
                                       {.2,1,.2},
 
1035
                                       {.2,.2,1},
 
1036
                                       {1,1,.2},
 
1037
                                       {1,.2,1},
 
1038
                                       {.2,1,1},
 
1039
                                       {1,.6,.2},
 
1040
                                       {1,.2,.6},
 
1041
                                       {.6,.2,1},
 
1042
                                       {.2,.6,1},
 
1043
                                       {1,1,1},
 
1044
                                       {.2,.2,.2},
 
1045
                                       {.5,.5,.5}
 
1046
                                   };
 
1047
 
 
1048
static nNOInitialisator<gAIPlayer> gAIPlayer_init(330,"gAIPlayer");
 
1049
 
 
1050
nDescriptor &gAIPlayer::CreatorDescriptor() const{
 
1051
    return gAIPlayer_init;
 
1052
}
 
1053
 
 
1054
gAIPlayer::gAIPlayer(nMessage &m) :
 
1055
        ePlayerNetID(m),
 
1056
        character(NULL),
 
1057
        //      target(NULL),
 
1058
        lastPath(se_GameTime()-100),
 
1059
        lastTime(se_GameTime()),
 
1060
        nextTime(0),
 
1061
        concentration(1),
 
1062
        log(NULL)
 
1063
{
 
1064
}
 
1065
 
 
1066
 
 
1067
gAIPlayer::gAIPlayer():
 
1068
        character(NULL),
 
1069
        //      target(NULL),
 
1070
        lastPath(se_GameTime()-100),
 
1071
        lastTime(se_GameTime()),
 
1072
        nextTime(0),
 
1073
        concentration(1),
 
1074
        log(NULL)
 
1075
{
 
1076
    character = NULL;
 
1077
    ClearTarget();
 
1078
    traceSide = 1;
 
1079
    freeSide  = 0;
 
1080
    log       = NULL;
 
1081
 
 
1082
    // find a good color
 
1083
 
 
1084
    current_ai=(current_ai+1) % MAXAI_COLOR;
 
1085
    int take_ai=current_ai;
 
1086
    int try_ai=current_ai;
 
1087
 
 
1088
    REAL maxmindist=-10000;
 
1089
 
 
1090
    for(int i=MAXAI_COLOR-1;i>=0;i--){
 
1091
        REAL mindist=4;
 
1092
        REAL score=0;
 
1093
        for (int j=se_PlayerNetIDs.Len()-1;j>=-1;j--){
 
1094
            REAL R, G, B; // the color we want to avoid
 
1095
 
 
1096
            if (j >= 0)
 
1097
            {
 
1098
                ePlayerNetID *p=se_PlayerNetIDs(j);
 
1099
                if (p && p != this)
 
1100
                {
 
1101
                    R = p->r/15.0;
 
1102
                    G = p->g/15.0;
 
1103
                    B = p->b/15.0;
 
1104
                }
 
1105
                else
 
1106
                    continue;
 
1107
            }
 
1108
            else // last case: j = -1. Test against floor color
 
1109
            {
 
1110
                se_FloorColor(R, G, B);
 
1111
            }
 
1112
            REAL dist=
 
1113
                fabs(R - rgb_ai[try_ai][0])+
 
1114
                fabs(G - rgb_ai[try_ai][1])+
 
1115
                fabs(B - rgb_ai[try_ai][2]);
 
1116
 
 
1117
            score+=exp(-dist*dist*4);
 
1118
 
 
1119
            if (dist<mindist){
 
1120
                mindist=dist;
 
1121
                /*         con << c->r << ":" << rgb_ai[try_ai][0] << '\t'
 
1122
                           << c->g << ":" << rgb_ai[try_ai][1] << '\t'
 
1123
                           << c->b << ":" << rgb_ai[try_ai][2] << '\t' << dist << '\n'; */
 
1124
            }
 
1125
        }
 
1126
        //con << "md=" << mindist << "\n\n";
 
1127
        if (mindist>2)
 
1128
            mindist=2;
 
1129
 
 
1130
        mindist=-score;
 
1131
 
 
1132
        if (mindist>maxmindist){
 
1133
            maxmindist=mindist;
 
1134
            take_ai=try_ai;
 
1135
        }
 
1136
 
 
1137
 
 
1138
 
 
1139
        try_ai = ((try_ai+1) % MAXAI_COLOR);
 
1140
    }
 
1141
 
 
1142
 
 
1143
    r = static_cast<int>(rgb_ai[take_ai][0] * 15);
 
1144
    g = static_cast<int>(rgb_ai[take_ai][1] * 15);
 
1145
    b = static_cast<int>(rgb_ai[take_ai][2] * 15);
 
1146
 
 
1147
 
 
1148
    ping = 0;
 
1149
    pingCharity = 300;
 
1150
 
 
1151
    NewObject();
 
1152
 
 
1153
    // AI players don't need to log in
 
1154
    Auth();
 
1155
}
 
1156
 
 
1157
void gAIPlayer::ConfigureAIs()  // ai configuration menu
 
1158
{
 
1159
 
 
1160
}
 
1161
 
 
1162
 
 
1163
// make sure this many AI players are in the game
 
1164
void gAIPlayer::SetNumberOfAIs(int num, int minPlayers, int iq, int tries)
 
1165
{
 
1166
    // balance the human teams with AI players
 
1167
    gAITeam::BalanceWithAIs();
 
1168
 
 
1169
    // remove AI players that got kicked out of their team
 
1170
    for (int i = se_PlayerNetIDs.Len()-1; i>=0; i--)
 
1171
    {
 
1172
        // count the active AIs
 
1173
        ePlayerNetID *p = se_PlayerNetIDs(i);
 
1174
        gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
 
1175
        if ( ai && !bool( ai->NextTeam() ) )
 
1176
        {
 
1177
            ai->RemoveFromGame();
 
1178
 
 
1179
#ifdef DEBUG
 
1180
            if ( ai->GetRefcount() > 1 )
 
1181
            {
 
1182
                st_Breakpoint();
 
1183
            }
 
1184
#endif
 
1185
 
 
1186
            sg_AIReferences.Remove( ai );
 
1187
        }
 
1188
    }
 
1189
 
 
1190
    int count = 0;
 
1191
 
 
1192
    bool iqperfect = false;
 
1193
 
 
1194
    // repeat until we run out of tries or the total amount of AIs is correct
 
1195
    do
 
1196
    {
 
1197
        count = 0;
 
1198
 
 
1199
        int i;
 
1200
        gAIPlayer* worstIQ = NULL; // worst fitting AI player that is in the game
 
1201
 
 
1202
        //              static tArray<bool> inGame(gAICharacter::s_Characters.Len());
 
1203
        //              for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
 
1204
        //                      inGame(i) = false;
 
1205
 
 
1206
        for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
 
1207
        {
 
1208
            // count the active AIs
 
1209
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
1210
            gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
 
1211
            if (ai && ai->NextTeam() == sg_AITeam )
 
1212
            {
 
1213
                //                              int index = ((int)ai->character - (int)&gAICharacter::s_Characters(0))/sizeof(gAICharacter);
 
1214
                //                              inGame(index) = true;
 
1215
 
 
1216
                if (!worstIQ || !worstIQ->character || fabs(worstIQ->character->iq - iq) < fabs(ai->character->iq - iq) )
 
1217
                    worstIQ = ai;
 
1218
 
 
1219
                count++;
 
1220
            }
 
1221
        }
 
1222
 
 
1223
        gAICharacter* bestIQ = BestIQ( iq );
 
1224
 
 
1225
        count -= num;
 
1226
 
 
1227
        // count the active players
 
1228
        int pcount = - minPlayers;
 
1229
        for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
 
1230
        {
 
1231
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
1232
            if ( !p->IsSpectating() )
 
1233
                ++pcount;
 
1234
        }
 
1235
 
 
1236
        if (pcount < count)
 
1237
            count = pcount;
 
1238
 
 
1239
        iqperfect = true;
 
1240
        if (bestIQ && worstIQ && worstIQ->character )
 
1241
            iqperfect = (fabs(bestIQ->iq - iq) > fabs(worstIQ->character->iq - iq) * .9f);
 
1242
 
 
1243
 
 
1244
        // count complete. Do something!
 
1245
        if (worstIQ && (count > 0 || (count >= 0 && !iqperfect)))
 
1246
        {
 
1247
            // too many AIs. Delete the one with the least fitting intelligence.
 
1248
            worstIQ->RemoveFromGame();
 
1249
            //                  worstIQ->ReleaseOwnership();
 
1250
 
 
1251
#ifdef DEBUG
 
1252
            if ( worstIQ->GetRefcount() > 1 )
 
1253
            {
 
1254
                st_Breakpoint();
 
1255
            }
 
1256
#endif
 
1257
 
 
1258
            sg_AIReferences.Remove( worstIQ );
 
1259
            count--;
 
1260
 
 
1261
            // remove empty AI team
 
1262
            if ( 0 == AITeam()->NumPlayers() )
 
1263
            {
 
1264
                ClearAITeam();
 
1265
            }
 
1266
        }
 
1267
        if (bestIQ && count < 0)
 
1268
        {
 
1269
            // too litte AIs. Create one.
 
1270
            gAIPlayer *ai = tNEW(gAIPlayer)();
 
1271
            ai->SetName( bestIQ->name );
 
1272
            ai->character = bestIQ;
 
1273
 
 
1274
            sg_AIReferences.Add( ai );
 
1275
 
 
1276
            ai->SetTeam ( AITeam() );
 
1277
            ai->UpdateTeam();
 
1278
 
 
1279
            /*
 
1280
            {
 
1281
                tOutput mess;
 
1282
                tColoredString printname;
 
1283
                printname << *(ePlayerNetID*)ai << tColoredString::ColorString(.5,1,.5);
 
1284
 
 
1285
                mess.SetTemplateParameter(1, printname);
 
1286
                mess << "$player_entered_game";
 
1287
 
 
1288
                sn_ConsoleOut( mess );
 
1289
            }
 
1290
            */
 
1291
 
 
1292
            count++;
 
1293
        }
 
1294
 
 
1295
    }
 
1296
    while ((count != 0 ||
 
1297
            !iqperfect) &&
 
1298
            tries-- != 0);
 
1299
 
 
1300
}
 
1301
 
 
1302
 
 
1303
 
 
1304
// Possible state changes:
 
1305
// Every state -> Survive for 20 seconds if the victim is dead or can be assumed dead soon, or if the situation gets too dangerous
 
1306
 
 
1307
// Survive -> CloseCombat if survival gets too boring
 
1308
 
 
1309
// Trace -> Closecombat
 
1310
// Path  -> Closecombat  if the victim gets in view
 
1311
 
 
1312
// Survive -> Trace if an enemy wall is hit
 
1313
// Path    -> Trace
 
1314
 
 
1315
// CloseCombat -> Path if the vicim gets out of view
 
1316
 
 
1317
 
 
1318
void gAIPlayer::SetTraceSide(int side)
 
1319
{
 
1320
    REAL time = se_GameTime();
 
1321
    REAL ts   = time - lastChangeAttempt + 1;
 
1322
    lastChangeAttempt = time;
 
1323
 
 
1324
    lazySideChange += ts * side;
 
1325
    if (lazySideChange * traceSide <= 0)
 
1326
    {
 
1327
        // state change!
 
1328
        traceSide = lazySideChange > 0 ? 1 : -1;
 
1329
        lazySideChange = 10 * traceSide;
 
1330
    }
 
1331
 
 
1332
    if (lazySideChange > 10)
 
1333
        lazySideChange = 10;
 
1334
    if (lazySideChange < -10)
 
1335
        lazySideChange = -10;
 
1336
}
 
1337
 
 
1338
// state change:
 
1339
void gAIPlayer::SwitchToState(gAI_STATE nextState, REAL minTime)
 
1340
{
 
1341
    int thisAbility = 10 - character->properties[AI_STATE_TRACE];
 
1342
    switch (state)
 
1343
    {
 
1344
    case AI_TRACE:
 
1345
        thisAbility = character->properties[AI_STATE_TRACE];
 
1346
        break;
 
1347
    case AI_CLOSECOMBAT:
 
1348
        thisAbility = character->properties[AI_STATE_CLOSECOMBAT];
 
1349
        break;
 
1350
    case AI_PATH:
 
1351
        thisAbility = character->properties[AI_STATE_PATH];
 
1352
        break;
 
1353
    case AI_SURVIVE:
 
1354
        break;
 
1355
    };
 
1356
 
 
1357
    int nextAbility = 10;
 
1358
    switch (nextState)
 
1359
    {
 
1360
    case AI_TRACE:
 
1361
        nextAbility = character->properties[AI_STATE_TRACE];
 
1362
        break;
 
1363
    case AI_CLOSECOMBAT:
 
1364
        nextAbility = character->properties[AI_STATE_CLOSECOMBAT];
 
1365
        break;
 
1366
    case AI_PATH:
 
1367
        nextAbility = character->properties[AI_STATE_PATH];
 
1368
        break;
 
1369
    case AI_SURVIVE:
 
1370
        break;
 
1371
    };
 
1372
 
 
1373
 
 
1374
    if (nextAbility > thisAbility && Random() * 10 > nextAbility)
 
1375
        return;
 
1376
 
 
1377
#ifdef DEBUG
 
1378
    if (state != nextState)
 
1379
        con << "Switching to state " << nextState << "\n";
 
1380
#endif
 
1381
 
 
1382
    state           = nextState;
 
1383
    nextStateChange = se_GameTime() + minTime;
 
1384
}
 
1385
 
 
1386
// state update functions:
 
1387
void gAIPlayer::ThinkSurvive(  ThinkData & data )
 
1388
{
 
1389
    REAL random = 0;
 
1390
    // do nothing much. Rely on the emergency program.
 
1391
    /*
 
1392
      random=10*(Random()/float(1));
 
1393
      if (random < .2)
 
1394
      EmergencySurvive(front, left, right, -1, 1);
 
1395
      else if (random > 9.8)
 
1396
      EmergencySurvive(front, left, right, -1, -1);
 
1397
      else
 
1398
      if (front.front.wallType == gSENSOR_RIM && front.distance < 10)
 
1399
      st_Breakpoint();
 
1400
 
 
1401
 
 
1402
    */
 
1403
 
 
1404
    if (data.left.front.wallType == gSENSOR_RIM)
 
1405
        EmergencySurvive( data, 1);
 
1406
    else if (data.right.front.wallType == gSENSOR_RIM)
 
1407
        EmergencySurvive( data, -1);
 
1408
    else
 
1409
        EmergencySurvive( data );
 
1410
 
 
1411
 
 
1412
 
 
1413
    if (nextStateChange > se_GameTime())
 
1414
    {
 
1415
        data.thinkAgain = .5f;
 
1416
        return;
 
1417
    }
 
1418
 
 
1419
    // switch from Survival to close combat if surviving is too boring
 
1420
    random=10*Random();
 
1421
    if (random < 5)
 
1422
    {
 
1423
        // find a new victim:
 
1424
        eCoord enemypos=eCoord(1000,100);
 
1425
 
 
1426
        const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
 
1427
        gCycle *secondbest = NULL;
 
1428
 
 
1429
        // find the closest enemy
 
1430
        for (int i=gameObjects.Len()-1;i>=0;i--){
 
1431
            gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
 
1432
 
 
1433
            if (other && other->Team()!=Object()->Team() &&
 
1434
                    !IsTrapped(other, Object())){
 
1435
                // then, enemy is realy an enemy
 
1436
                eCoord otherpos=other->Position()-Object()->Position();
 
1437
                if (otherpos.NormSquared()<enemypos.NormSquared()){
 
1438
                    // check if the path is clear
 
1439
                    gSensor p(Object(),Object()->Position(),otherpos);
 
1440
                    p.detect(REAL(.98));
 
1441
                    secondbest = dynamic_cast<gCycle *>(other);
 
1442
                    if (p.hit>=.98){
 
1443
                        enemypos = otherpos;
 
1444
                        target = secondbest;
 
1445
                    }
 
1446
                }
 
1447
            }
 
1448
        }
 
1449
 
 
1450
        if (!target)
 
1451
            target = secondbest;
 
1452
 
 
1453
        if (target)
 
1454
            SwitchToState(AI_CLOSECOMBAT, 1);
 
1455
    }
 
1456
 
 
1457
    data.thinkAgain = 1;
 
1458
}
 
1459
 
 
1460
void gAIPlayer::ThinkTrace( ThinkData & data )
 
1461
{
 
1462
    gAISensor const & front = data.front;
 
1463
    gAISensor const & left = data.left;
 
1464
    gAISensor const & right = data.right;
 
1465
 
 
1466
    bool inverse = front.Hit() && front.distance < Object()->Speed() * Delay();
 
1467
 
 
1468
    if (left.front.wallType == gSENSOR_RIM)
 
1469
        SetTraceSide(1);
 
1470
 
 
1471
    if (right.front.wallType == gSENSOR_RIM)
 
1472
        SetTraceSide(-1);
 
1473
 
 
1474
    bool success = EmergencySurvive(data, 0, traceSide * ( inverse ? -1 : 1));
 
1475
 
 
1476
    REAL & nextTurn = data.thinkAgain;
 
1477
    nextTurn = 100;
 
1478
    if (left.front.edge)
 
1479
    {
 
1480
        REAL a = eCoord::F(Object()->Direction(), *left.front.edge->Point() - Object()->Position());
 
1481
        REAL b = eCoord::F(Object()->Direction(), *left.front.edge->Other()->Point() - Object()->Position());
 
1482
 
 
1483
        if (a < b)
 
1484
            a = b;
 
1485
        if ( a > 0 )
 
1486
            nextTurn = a;
 
1487
    }
 
1488
 
 
1489
    if (right.front.edge)
 
1490
    {
 
1491
        REAL a = eCoord::F(Object()->Direction(), *right.front.edge->Point() - Object()->Position());
 
1492
        REAL b = eCoord::F(Object()->Direction(), *right.front.edge->Other()->Point() - Object()->Position());
 
1493
 
 
1494
        if (a < b)
 
1495
            a = b;
 
1496
        if ( a > 0 && a < nextTurn || !left.front.edge)
 
1497
            nextTurn = a;
 
1498
    }
 
1499
 
 
1500
    nextTurn/= Object()->Speed() * .98f;
 
1501
 
 
1502
    REAL delay = Delay() * 1.5f;
 
1503
    if ((!Object()->CanMakeTurn() || success) && nextTurn > delay)
 
1504
        nextTurn = delay;
 
1505
 
 
1506
    if (nextTurn > .3f)
 
1507
        nextTurn = .3f;
 
1508
 
 
1509
    if (nextStateChange > se_GameTime())
 
1510
        return;
 
1511
 
 
1512
    // find a new victim:
 
1513
    eCoord enemypos=eCoord(1000,100);
 
1514
 
 
1515
    const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
 
1516
    gCycle *secondbest = NULL;
 
1517
 
 
1518
    // find the closest enemy
 
1519
    for (int i=gameObjects.Len()-1;i>=0;i--){
 
1520
        gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
 
1521
 
 
1522
        if (other && other->Team()!=Object()->Team() &&
 
1523
                !IsTrapped(other, Object())){
 
1524
            // then, enemy is realy an enemy
 
1525
            eCoord otherpos=other->Position()-Object()->Position();
 
1526
            if (otherpos.NormSquared()<enemypos.NormSquared()){
 
1527
                // check if the path is clear
 
1528
                gSensor p(Object(),Object()->Position(),otherpos);
 
1529
                p.detect(REAL(.98));
 
1530
                secondbest = dynamic_cast<gCycle *>(other);
 
1531
 
 
1532
                if (!target)
 
1533
                    enemypos = otherpos;
 
1534
 
 
1535
                if (p.hit>=.98){
 
1536
                    enemypos = otherpos;
 
1537
                    target = secondbest;
 
1538
                }
 
1539
            }
 
1540
        }
 
1541
    }
 
1542
 
 
1543
    eCoord relpos=enemypos.Turn(Object()->Direction().Conj()).Turn(0,1);
 
1544
 
 
1545
 
 
1546
    if (!target)
 
1547
        target = secondbest;
 
1548
    else
 
1549
        SwitchToState(AI_CLOSECOMBAT, 1);
 
1550
 
 
1551
    if (target)
 
1552
        SetTraceSide((relpos.x  > 0 ? 10 : -10) *
 
1553
                     (target->Speed() > Object()->Speed() ? -1 : 1));
 
1554
 
 
1555
    nextStateChange = se_GameTime() + 10;
 
1556
 
 
1557
    //  SwitchToState(AI_SURVIVE, 1);
 
1558
    return;
 
1559
}
 
1560
 
 
1561
 
 
1562
void gAIPlayer::ThinkPath( ThinkData & data )
 
1563
{
 
1564
    int lr = 0;
 
1565
    REAL mindist = 10;
 
1566
 
 
1567
    eCoord dir = Object()->Direction();
 
1568
    // REAL fs=front.distance;
 
1569
    REAL ls=data.left.distance;
 
1570
    REAL rs=data.right.distance;
 
1571
 
 
1572
 
 
1573
    if (!target->CurrentFace() || IsTrapped(target, Object()))
 
1574
    {
 
1575
        SwitchToState(AI_SURVIVE, 1);
 
1576
        EmergencySurvive( data );
 
1577
 
 
1578
        data.thinkAgain = 4;
 
1579
        return;
 
1580
    }
 
1581
 
 
1582
    eCoord tDir = target->Position() - Object()->Position();
 
1583
 
 
1584
    if ( nextStateChange < se_GameTime() )
 
1585
    {
 
1586
        gSensor p(Object(),Object()->Position(), tDir);
 
1587
        p.detect(REAL(.9999999));
 
1588
        if (p.hit >=  .9999999)  // free line of sight to victim. Switch to close combat.
 
1589
        {
 
1590
            SwitchToState(AI_CLOSECOMBAT, 5);
 
1591
            EmergencySurvive( data );
 
1592
 
 
1593
            return;
 
1594
        }
 
1595
    }
 
1596
 
 
1597
 
 
1598
 
 
1599
    // find a new path if the one we got is outdated:
 
1600
    if (lastPath < se_GameTime() - 10)
 
1601
        if (target->CurrentFace())
 
1602
        {
 
1603
            Object()->FindCurrentFace();
 
1604
            eHalfEdge::FindPath(Object()->Position(), Object()->CurrentFace(),
 
1605
                                target->Position(), target->CurrentFace(),
 
1606
                                Object(),
 
1607
                                path);
 
1608
            lastPath = se_GameTime();
 
1609
        }
 
1610
 
 
1611
    if (!path.Valid())
 
1612
    {
 
1613
        data.thinkAgain = 1;
 
1614
        return;
 
1615
    }
 
1616
 
 
1617
    // find the most advanced path point that is in our viewing range:
 
1618
 
 
1619
    for (int z = 10; z>=0; z--)
 
1620
        path.Proceed();
 
1621
 
 
1622
    bool goon   = path.Proceed();
 
1623
    bool nogood = false;
 
1624
 
 
1625
    do
 
1626
    {
 
1627
        if (goon)
 
1628
            goon = path.GoBack();
 
1629
        else
 
1630
            goon = true;
 
1631
 
 
1632
        eCoord pos   = path.CurrentPosition() + path.CurrentOffset() * 0.1f;
 
1633
        eCoord opos  = Object()->Position();
 
1634
        eCoord odir  = pos - opos;
 
1635
 
 
1636
        eCoord intermediate = opos + dir * eCoord::F(odir, dir);
 
1637
 
 
1638
        gSensor p(Object(), opos, intermediate - opos);
 
1639
        p.detect(1.1f);
 
1640
        nogood = (p.hit <= .999999999 || eCoord::F(path.CurrentOffset(), odir) < 0);
 
1641
 
 
1642
        if (!nogood)
 
1643
        {
 
1644
            gSensor p(Object(), intermediate, pos - intermediate);
 
1645
            p.detect(1);
 
1646
            nogood = (p.hit <= .99999999 || eCoord::F(path.CurrentOffset(), odir) < 0);
 
1647
        }
 
1648
 
 
1649
    }
 
1650
    while (goon && nogood);
 
1651
 
 
1652
    if (goon)
 
1653
    {
 
1654
        // now we have found our next goal. Try to get there.
 
1655
        eCoord pos    = Object()->Position();
 
1656
        eCoord target = path.CurrentPosition();
 
1657
 
 
1658
        // look how far ahead the target is:
 
1659
        REAL ahead = eCoord::F(target - pos, dir)
 
1660
                     + eCoord::F(path.CurrentOffset(), dir);
 
1661
 
 
1662
        if ( ahead > 0)
 
1663
        {         // it is still before us. just wait a while.
 
1664
            mindist = ahead;
 
1665
        }
 
1666
        else
 
1667
        { // we have passed it. Make a turn towards it.
 
1668
            REAL side = (target - pos) * dir;
 
1669
 
 
1670
            if ( !((side > 0 && ls < 3) || (side < 0 && rs < 3))
 
1671
                    && (fabs(side) > 3 || ahead < -10) )
 
1672
            {
 
1673
#ifdef DEBUG
 
1674
                con << "Following path...\n";
 
1675
#endif
 
1676
                lr += (side > 0 ? 1 : -1);
 
1677
            }
 
1678
        }
 
1679
    }
 
1680
    else // nogood
 
1681
    {
 
1682
        lastPath -= 1;
 
1683
        SwitchToState(AI_SURVIVE);
 
1684
    }
 
1685
 
 
1686
    EmergencySurvive( data, 1, -lr );
 
1687
 
 
1688
    REAL d = sqrt(tDir.NormSquared()) * .2f;
 
1689
    if (d < mindist)
 
1690
        mindist = d;
 
1691
 
 
1692
    data.thinkAgain = mindist / Object()->Speed();
 
1693
    if (data.thinkAgain > .4)
 
1694
        data.thinkAgain *= .7;
 
1695
}
 
1696
 
 
1697
 
 
1698
void gAIPlayer::ThinkCloseCombat( ThinkData & data )
 
1699
{
 
1700
    int lr=0;
 
1701
 
 
1702
    REAL nextThought = 0;
 
1703
 
 
1704
    const gAISensor* sides[2];
 
1705
    sides[0] = &data.left;
 
1706
    sides[1] = &data.right;
 
1707
 
 
1708
    eCoord dir = Object()->Direction();
 
1709
    REAL fs=data.front.distance;
 
1710
    //  REAL ls=left.hit;
 
1711
    //  REAL rs=right.hit;
 
1712
 
 
1713
    if ( bool( target ) && !IsTrapped(target, Object()) && nextStateChange < se_GameTime() )
 
1714
    {
 
1715
        gSensor p(Object(),Object()->Position(),target->Position() - Object()->Position());
 
1716
        p.detect(REAL(1));
 
1717
        if (p.hit <=  .999999)  // no free line of sight to victim. Switch to path mode.
 
1718
        {
 
1719
            SwitchToState(AI_PATH, 5);
 
1720
            EmergencySurvive( data );
 
1721
            return;
 
1722
        }
 
1723
    }
 
1724
 
 
1725
    REAL ed = 0;
 
1726
 
 
1727
    const REAL fear=REAL(.01);
 
1728
    const REAL caution=.001;
 
1729
    const REAL evasive=100;
 
1730
    const REAL attack=100;
 
1731
    const REAL seek=REAL(1);
 
1732
    const REAL trap=REAL(.01);
 
1733
    const REAL ffar=20;
 
1734
    //    const REAL close=1000;
 
1735
 
 
1736
    REAL random=10*Random()*Random();
 
1737
 
 
1738
    if ( bool( target ) && target->Alive()){
 
1739
 
 
1740
        eCoord enemypos=target->Position()-Object()->Position();
 
1741
        eCoord enemydir=target->Direction();
 
1742
        REAL enemyspeed=target->Speed();
 
1743
 
 
1744
        ed=REAL(fabs(enemypos.x)+fabs(enemypos.y));
 
1745
        ed/=enemyspeed;
 
1746
 
 
1747
        // transform coordinates relative to us:
 
1748
        enemypos=enemypos.Turn(dir.Conj()).Turn(0,1);
 
1749
        enemydir=enemydir.Turn(dir.Conj()).Turn(0,1);
 
1750
 
 
1751
        // now we are at the center of the coordinate system facing
 
1752
        // in direction (0,1).
 
1753
 
 
1754
        // rules are symmetrical: exploit that.
 
1755
        int side=1;
 
1756
        if (enemypos.x<0){
 
1757
            side*=-1;
 
1758
            enemypos.x*=-1;
 
1759
            enemydir.x*=-1;
 
1760
 
 
1761
            sides[1] = &data.left;
 
1762
            sides[0] = &data.right;
 
1763
            //      Swap(ls,rs);
 
1764
        }
 
1765
        // now we can even assume the enemy is on our right side.
 
1766
 
 
1767
        // consider his ping and our reaction time
 
1768
#define REACTION .2
 
1769
 
 
1770
 
 
1771
        //REAL enemyspeed=target->speed;
 
1772
        REAL ourspeed=Object()->Speed();
 
1773
 
 
1774
        REAL enemydist=target->Lag()*enemyspeed;
 
1775
 
 
1776
        // redo the prediction
 
1777
#ifndef DEDICATED
 
1778
        if (sn_GetNetState()==nCLIENT && !sr_predictObjects)
 
1779
#endif
 
1780
            enemypos=enemypos-enemydir*enemydist;
 
1781
        enemydist+=2*REACTION *enemyspeed;
 
1782
 
 
1783
        REAL ourdist=REACTION*ourspeed;;
 
1784
 
 
1785
 
 
1786
        // now we consider the worst case: we drive straight on,
 
1787
        enemypos.y-=ourdist;
 
1788
        // while the enemy cuts us: he goes in front of us
 
1789
        REAL forward=-enemypos.y+.01;
 
1790
        if (forward<0) // no need to go to much ahead
 
1791
            forward=0;
 
1792
        if (forward>enemydist)
 
1793
            forward=enemydist;
 
1794
 
 
1795
        enemypos.y+=forward;
 
1796
        enemydist-=forward;
 
1797
 
 
1798
        // and then he turns left.
 
1799
        enemypos.x-=enemydist;
 
1800
 
 
1801
        if (enemypos.y*enemyspeed>enemypos.x*ourspeed){ // he is right ahead of us.
 
1802
            if (random<fear){ // evade him
 
1803
#ifdef DEBUG
 
1804
                con << "fear!\n";
 
1805
#endif
 
1806
                lr+=side;
 
1807
 
 
1808
                nextThought += 1;
 
1809
            }
 
1810
            if (enemypos.y<=ffar &&
 
1811
                    ((enemydir.x<0 && random<evasive) ||
 
1812
                     (enemydir.y>0 && random<caution) ||
 
1813
                     (enemydir.y<0 && random<attack))){
 
1814
#ifdef DEBUG
 
1815
                con << "caution!\n";
 
1816
#endif
 
1817
                lr+=side;
 
1818
 
 
1819
                nextThought += 1;
 
1820
 
 
1821
                if (enemyspeed > ourspeed)
 
1822
                {
 
1823
                    SetTraceSide(-side);
 
1824
                    SwitchToState(AI_TRACE, 10);
 
1825
                }
 
1826
            }
 
1827
        }
 
1828
        else if (enemypos.y*ourspeed<-enemypos.x*enemyspeed){
 
1829
            /*
 
1830
              // good attack position
 
1831
              if (enemypos.x<rs && rs < range*.99){
 
1832
              #ifdef DEBUG
 
1833
              con << "BOX!\n";
 
1834
              #endif
 
1835
              turn+=10;
 
1836
              lr-=side;
 
1837
              }
 
1838
 
 
1839
            */
 
1840
 
 
1841
            REAL canCutIfDriveOn = enemypos.x*ourspeed - fs * (enemyspeed - ourspeed);
 
1842
            canCutIfDriveOn -= enemypos.y*enemyspeed;
 
1843
 
 
1844
            REAL canCutIfAttack  = - sides[1]->distance * enemyspeed
 
1845
                                   - (sides[1]->distance - enemypos.x -enemypos.y*ourspeed) * ourspeed;
 
1846
 
 
1847
            if (random<attack && (!(data.front.Hit() && data.front.distance < 20) || canCutIfAttack > canCutIfDriveOn)){
 
1848
#ifdef DEBUG
 
1849
                con << "attack!\n";
 
1850
#endif
 
1851
                lr-=side;
 
1852
 
 
1853
                nextThought += 1;
 
1854
            }
 
1855
        }
 
1856
        else if(enemypos.x>ffar*4){
 
1857
            if(random<seek){
 
1858
#ifdef DEBUG
 
1859
                con << "seek!\n";
 
1860
#endif
 
1861
                lr-=side;
 
1862
 
 
1863
                nextThought += 1;
 
1864
            }
 
1865
        }
 
1866
        else if (enemypos.x<ffar*2 && fabs(enemypos.y)<ffar){
 
1867
            if(random<trap){
 
1868
#ifdef DEBUG
 
1869
                con << "trap!\n";
 
1870
#endif
 
1871
                lr+=side;
 
1872
 
 
1873
                nextThought += 1;
 
1874
            }
 
1875
        }
 
1876
    }
 
1877
 
 
1878
    if (!EmergencySurvive(data, 1, -lr))
 
1879
        nextThought = 0;
 
1880
 
 
1881
    data.thinkAgain = ed/2 + nextThought;
 
1882
}
 
1883
 
 
1884
 
 
1885
/*
 
1886
static void PretendFrontHit(const gAISensor& f, const gAISensor &corridor,
 
1887
                            const eCoord& origin, const eCoord& direction)
 
1888
{
 
1889
  gAISensor& front = (gAISensor&)(f);
 
1890
 
 
1891
  // transfer the easy data
 
1892
  front.ehit = corridor.ehit;
 
1893
  front.lr   = corridor.lr;
 
1894
  front.type = corridor.type;
 
1895
 
 
1896
  REAL a = eCoord::F(*corridor.ehit->Point()          - origin, direction);
 
1897
  REAL b = eCoord::F(*corridor.ehit->Other()->Point() - origin, direction);
 
1898
 
 
1899
  if (front.hit > a)
 
1900
    front.hit = a;
 
1901
  if (front.hit > b)
 
1902
    front.hit = b;
 
1903
 
 
1904
  front.before_hit = origin + direction * front.hit * .99f;
 
1905
}
 
1906
*/
 
1907
 
 
1908
 
 
1909
#define DANGERLEVELS 4
 
1910
#define LOOPLEVEL   0
 
1911
#define SPACELEVEL  1
 
1912
#define TRAPLEVEL   2
 
1913
#define COLIDELEVEL 2
 
1914
#define TEAMLEVEL   3
 
1915
 
 
1916
 
 
1917
class gAILogEntry{
 
1918
public:
 
1919
    int sideDanger[DANGERLEVELS][2];
 
1920
    int frontDanger[DANGERLEVELS];
 
1921
    int turn;
 
1922
    int tries;
 
1923
    REAL time;
 
1924
};
 
1925
 
 
1926
#define ENTRIES 10
 
1927
 
 
1928
class gAILog{
 
1929
public:
 
1930
    gAILogEntry entries[ENTRIES+1];
 
1931
    int         current;
 
1932
    int         del;
 
1933
 
 
1934
    gAILog():current(0), del(0){}
 
1935
 
 
1936
    void DeleteEntry()
 
1937
    {
 
1938
        del = 1;
 
1939
        if (current > 0)
 
1940
            current--;
 
1941
    }
 
1942
 
 
1943
    gAILogEntry& NextEntry()
 
1944
    {
 
1945
        del = 0;
 
1946
 
 
1947
        if (current >= ENTRIES)
 
1948
        {
 
1949
            for (int i=1; i<ENTRIES; i++)
 
1950
                entries[i-1] = entries[i];
 
1951
        }
 
1952
        else
 
1953
            current++;
 
1954
 
 
1955
        gAILogEntry& ret = entries[current-1];
 
1956
        ret.time = se_GameTime();
 
1957
        return ret;
 
1958
    }
 
1959
 
 
1960
    void Print()
 
1961
    {
 
1962
#ifdef DEBUG
 
1963
        con << "Log:\n";
 
1964
        for (int i = current + del - 1; i>=0; i--)
 
1965
        {
 
1966
            for (int j=0; j < DANGERLEVELS; j++)
 
1967
            {
 
1968
                con << entries[i].sideDanger[j][0] << ' ';
 
1969
                con << entries[i].frontDanger[j]   << ' ';
 
1970
                con << entries[i].sideDanger[j][1] << "    ";
 
1971
            }
 
1972
            con << entries[i].turn << ", " << entries[i].tries << "\n";
 
1973
        }
 
1974
#ifndef DEDICATED
 
1975
        //              se_PauseGameTimer(true);
 
1976
#endif
 
1977
#endif
 
1978
    }
 
1979
};
 
1980
 
 
1981
// emergency functions:
 
1982
bool gAIPlayer::EmergencySurvive( ThinkData & data, int enemyevade, int preferedSide)
 
1983
{
 
1984
    gAISensor const & front = data.front;
 
1985
    gAISensor const & left = data.left;
 
1986
    gAISensor const & right = data.right;
 
1987
 
 
1988
    if (!log)
 
1989
        log = tNEW(gAILog);
 
1990
 
 
1991
#ifdef DEBUG
 
1992
    static int last = 0;
 
1993
    if (log->current >= 4
 
1994
            && log->entries[log->current-2].time > se_GameTime() - .2
 
1995
            && log->entries[log->current-1].turn * last <= 0
 
1996
            && log->entries[log->current-1].turn * log->entries[log->current-2].turn < 0
 
1997
            //      && log->entries[log->current-3].turn * log->entries[log->current-2].turn < 0
 
1998
            //      && log->entries[log->current-3].turn * log->entries[log->current-4].turn < 0
 
1999
       )
 
2000
    {
 
2001
        log->Print();
 
2002
        //      st_Breakpoint();
 
2003
    }
 
2004
    last = log->entries[log->current-1].turn;
 
2005
#endif
 
2006
 
 
2007
    triesLeft = (triesLeft * character->properties[AI_EMERGENCY])/10;
 
2008
 
 
2009
    freeSide *= .95;
 
2010
 
 
2011
    int i, j;
 
2012
 
 
2013
    // don't do a thing if there may be a better way out of we drive on:
 
2014
    if (triesLeft > 0 &&
 
2015
            front.front.otherCycle &&
 
2016
            front.front.otherCycle != Object() &&
 
2017
            ((front.frontLoop[1].loop && front.front.otherCycle != left .front.otherCycle && left .front.otherCycle)||
 
2018
             (front.frontLoop[0].loop && front.front.otherCycle != right.front.otherCycle && right.front.otherCycle ) )
 
2019
       )
 
2020
        return false;
 
2021
 
 
2022
    // get the delay between two turns
 
2023
    REAL delay = Delay();
 
2024
    REAL range = Object()->Speed() * delay;
 
2025
 
 
2026
    // nothing we can do if we cannot make a turn immediately
 
2027
    if (!Object()->CanMakeTurn())
 
2028
        return false;
 
2029
 
 
2030
    //  bool dontCheckForLoop[2] = { false, false };
 
2031
 
 
2032
 
 
2033
    // look out if there is anything bad going on in one of the directions:
 
2034
    // [signifficance: danger level of n: You'll be (as good as) dead in [10/n delay times] if you drive that way
 
2035
    int sideDanger[DANGERLEVELS][2];
 
2036
    int frontDanger[DANGERLEVELS];
 
2037
    for(i = DANGERLEVELS-1; i>=0; i--)
 
2038
    {
 
2039
        sideDanger[i][0] = 0;
 
2040
        sideDanger[i][1] = 0;
 
2041
        frontDanger[i]   = 0;
 
2042
    }
 
2043
 
 
2044
    bool canTrapEnemy = false;
 
2045
 
 
2046
    if (emergency)
 
2047
    {
 
2048
        frontDanger[SPACELEVEL] += 40;
 
2049
    }
 
2050
 
 
2051
    const gAISensor* sides[2];
 
2052
    sides[0] = &left;
 
2053
    sides[1] = &right;
 
2054
 
 
2055
    // avoid loops:
 
2056
 
 
2057
    bool isTrapped = IsTrapped(Object(), NULL);
 
2058
 
 
2059
    /*
 
2060
      if (front.front.wallType == gSENSOR_ENEMY)
 
2061
      sideDanger[LOOPLEVEL][(1-front.front.lr*enemyevade) >> 1] += 5;
 
2062
    */  
 
2063
 
 
2064
    if (!isTrapped)
 
2065
        for (i = 1; i>=0; i--)
 
2066
        {
 
2067
            if (front.frontLoop[i].loop && front.distance < 5*sides[i]->distance)
 
2068
            {
 
2069
                // if we would close ourself in, make the danger bigger
 
2070
                if (front.front.otherCycle == Object() && i+i-1 == front.front.lr)
 
2071
                    sideDanger[LOOPLEVEL][i]+=40;
 
2072
 
 
2073
                sideDanger[LOOPLEVEL][i]+=40;
 
2074
                for (j = front.frontLoop[i].closedIn.Len()-1; j>=0; j--)
 
2075
                    if (front.frontLoop[i].closedIn(j) == target)
 
2076
                        canTrapEnemy = true;
 
2077
            }
 
2078
 
 
2079
            for (j = 1; j>=0; j--)
 
2080
                if (front.sideLoop[i][j].loop)
 
2081
                    sideDanger[LOOPLEVEL][j]++;
 
2082
 
 
2083
            // if we would close ourselfs in by a zigzag in direction i,
 
2084
            // but not by a u-turn and there is enough space for a u-turn,
 
2085
            // do it.
 
2086
            if (sides[i]->frontLoop[1-i].loop  &&
 
2087
                    !sides[i]->frontLoop[i].loop)
 
2088
            {
 
2089
                if (sides[i]->distance > range)
 
2090
                {
 
2091
                    frontDanger[LOOPLEVEL]     += 20;
 
2092
                    sideDanger[LOOPLEVEL][1-i] += 10;
 
2093
                }
 
2094
                else // try to make some room so we can evade:
 
2095
                {
 
2096
                    frontDanger[LOOPLEVEL]     += 20;
 
2097
 
 
2098
                    sideDanger[LOOPLEVEL][i]   += 10;
 
2099
                }
 
2100
            }
 
2101
 
 
2102
            // if we would close ourselves in by a U-Turn, don't do it.
 
2103
            //  if (sides[i]->frontLoop[i].loop && sides[i].distance < range * 2)
 
2104
            //    sideDanger[LOOPLEVEL][i] += 40;
 
2105
        }
 
2106
 
 
2107
    // try to trap the enemy
 
2108
    if (character->properties[AI_LOOP] >= 10 && canTrapEnemy && !emergency)
 
2109
        return false;
 
2110
 
 
2111
 
 
2112
    /*
 
2113
      // avoid closing yourself or a teammate in.
 
2114
      if (front.type == gSENSOR_SELF || front.type == gSENSOR_TEAMMATE)
 
2115
      {
 
2116
         if (front.lr > 0)
 
2117
      sideDanger[][1] +=2;
 
2118
         else
 
2119
      sideDanger[][0] +=2;
 
2120
      }
 
2121
    */
 
2122
 
 
2123
 
 
2124
 
 
2125
    {
 
2126
        if (front.Hit() &&
 
2127
                ( front.distance + range < sides[0]->distance ||
 
2128
                  front.distance + range < sides[1]->distance) )
 
2129
        {
 
2130
            if ( front.front.wallType == gSENSOR_RIM)
 
2131
                frontDanger[SPACELEVEL] += static_cast<int>(100 * range * gArena::SizeMultiplier() / (front.distance + range * .1));
 
2132
 
 
2133
            frontDanger[SPACELEVEL] += static_cast<int>(5 * range / (front.distance + range *.2));
 
2134
 
 
2135
            if (front.distance < range)
 
2136
                frontDanger[SPACELEVEL] += static_cast<int>(20 * range / (front.distance + range *.2)) + 1;
 
2137
        }
 
2138
 
 
2139
 
 
2140
        // avoid close corners:
 
2141
        for (i = 1; i>=0; i--)
 
2142
        {
 
2143
            if (sides[i]->Hit() && //sides[i]->distance < range * 3 &&
 
2144
                    sides[i]->distance < front.distance + range)
 
2145
            {
 
2146
                if ( sides[i]->front.wallType == gSENSOR_RIM)
 
2147
                    sideDanger[SPACELEVEL][i] += static_cast<int>(150 * range * gArena::SizeMultiplier() / (sides[i]->distance + range * .1));
 
2148
 
 
2149
                sideDanger[SPACELEVEL][i] += static_cast<int>
 
2150
                                             (range * 5 / (sides[i]->distance + range * .1));
 
2151
 
 
2152
                if (sides[i]->distance < range)
 
2153
                    sideDanger[SPACELEVEL][i] += static_cast<int>
 
2154
                                                 (range * 20 / (sides[i]->distance + range * .1));
 
2155
            }
 
2156
 
 
2157
            // give us a chance to turn around:
 
2158
            if (frontDanger[SPACELEVEL] * 2 < sideDanger[SPACELEVEL][i])
 
2159
                sideDanger[LOOPLEVEL][i-i] -= sideDanger[SPACELEVEL][i] * 2;
 
2160
        }
 
2161
    }
 
2162
 
 
2163
    // avoid close proximity to other cycles
 
2164
    const gCycle* target = NULL;
 
2165
    const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
 
2166
    gCycle *secondbest = NULL;
 
2167
    REAL closest = 1000000;
 
2168
    eCoord dir     = Object()->Direction();
 
2169
 
 
2170
    // find the closest enemy
 
2171
    for (i=gameObjects.Len()-1;i>=0;i--){
 
2172
        gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
 
2173
 
 
2174
        if (other && other->Alive() && other != Object())
 
2175
        {
 
2176
            eCoord otherpos=other->Position()-Object()->Position();
 
2177
            REAL otherNorm = otherpos.NormSquared();
 
2178
 
 
2179
            bool nothit = false;
 
2180
            if (otherNorm < closest * 4)
 
2181
            {
 
2182
                gSensor p(other, other->Position(), -otherpos);
 
2183
                p.detect(REAL(.9999));
 
2184
                gSensor q(Object(), Object()->Position(), otherpos);
 
2185
                q.detect(REAL(.9999));
 
2186
 
 
2187
                nothit = p.hit>=.999 && q.hit >=.999;
 
2188
            }
 
2189
 
 
2190
            if (other->Team() != Object()->Team())
 
2191
            {
 
2192
                // then, enemy is realy an enemy
 
2193
                //      REAL s = Object()->Speed() * 50;
 
2194
                if (/* otherNorm < s*s && */ otherNorm < closest)
 
2195
                {
 
2196
                    // check if the path is clear
 
2197
                    secondbest = dynamic_cast<gCycle *>(other);
 
2198
                    if (nothit){
 
2199
                        closest = otherNorm;
 
2200
                        target = secondbest;
 
2201
                    }
 
2202
                }
 
2203
            }
 
2204
            else if (nothit)
 
2205
            {
 
2206
                // he is a teammate. Avoid him.
 
2207
 
 
2208
                eCoord friendpos=other->Position() - Object()->Position();
 
2209
 
 
2210
                // transform coordinates relative to us:
 
2211
                friendpos=friendpos.Turn(dir.Conj()).Turn(0,1);
 
2212
 
 
2213
                if (friendpos.y > fabs(friendpos.x) * 1.5f)
 
2214
                    frontDanger[TEAMLEVEL] += 10;
 
2215
                if (friendpos.x * 2 > -friendpos.y)
 
2216
                    sideDanger[TEAMLEVEL][1] += 10;
 
2217
                else if (-friendpos.x * 2 > -friendpos.y)
 
2218
                    sideDanger[TEAMLEVEL][0] += 10;
 
2219
            }
 
2220
        }
 
2221
    }
 
2222
 
 
2223
    //  if (!target)
 
2224
    //target = secondbest;
 
2225
 
 
2226
    if (target && character->properties[AI_ENEMY] > 0)
 
2227
    {
 
2228
        bool sdanger = false;
 
2229
        for (i = DANGERLEVELS-1; i>=0; i--)
 
2230
            sdanger |= sideDanger[i][0] > 4 || sideDanger[i][1] > 4;
 
2231
 
 
2232
        eCoord enemypos=target->Position() - Object()->Position();
 
2233
        eCoord enemydir=target->Direction();
 
2234
        REAL enemyspeed=target->Speed();
 
2235
 
 
2236
 
 
2237
        // transform coordinates relative to us:
 
2238
        enemypos=enemypos.Turn(dir.Conj()).Turn(0,1);
 
2239
        enemydir=enemydir.Turn(dir.Conj()).Turn(0,1);
 
2240
 
 
2241
        if (character->properties[AI_ENEMY] > 7)
 
2242
        {
 
2243
            // would he be able to trap us if we drive straight on?
 
2244
            bool trap[2] = {false, false};
 
2245
 
 
2246
            if (!isTrapped)
 
2247
                for (i = 1; i>=0; i--)
 
2248
                {
 
2249
                    // if the enemy comes racing towards us, check if he could
 
2250
                    // close us in by touching our own line ON THE OPPOSITE side of i
 
2251
                    tArray<const gCycle*> closedIn;
 
2252
                    int winding = 0;
 
2253
 
 
2254
                    bool loop = CheckLoop(target, Object(),
 
2255
                                          Object()->GetDistance() + 4 * TOL, i, 0,
 
2256
                                          closedIn, winding);
 
2257
 
 
2258
                    winding -= Object()->WindingNumber();
 
2259
                    winding += target->WindingNumber();
 
2260
 
 
2261
 
 
2262
                    // yes! we shoult turn in direction 1-i to get the target
 
2263
                    // to the other side.
 
2264
                    if (loop)
 
2265
                        if (winding * (i+i-1) < 0)
 
2266
                        {
 
2267
                            trap[i] = true;
 
2268
                            REAL x = enemypos.x * (i+i-1);
 
2269
                            REAL y = enemypos.y;
 
2270
 
 
2271
                            bool canAccelerateByTurning =
 
2272
                                ( sides[1-i]->Hit() &&
 
2273
                                  sides[1-i]->distance < Object()->Speed() * delay * 5 &&
 
2274
                                  sides[i-i]->distance > Object()->Speed() * delay &&
 
2275
                                  !sides[i-i]->frontLoop[i].loop) ;
 
2276
 
 
2277
                            bool ohShit = target->Speed() > Object()->Speed() + sqrt(closest);
 
2278
 
 
2279
                            if (ohShit)
 
2280
                            {
 
2281
                                SetTraceSide(-(i+i-1));
 
2282
                                SwitchToState(AI_TRACE, 10);
 
2283
                            }
 
2284
 
 
2285
                            bool turningIsFutile =
 
2286
                                front.front.otherCycle == Object() &&
 
2287
                                sides[1-i]->front.otherCycle == Object() &&
 
2288
                                front.distance < sides[1-1]->distance * 10 ;
 
2289
 
 
2290
                            if (
 
2291
                                x < 0 &&
 
2292
                                (
 
2293
                                    x * Object()->Speed() < -y * target->Speed() + 1000 ||
 
2294
                                    canAccelerateByTurning || ohShit
 
2295
                                )
 
2296
                                &&
 
2297
                                !turningIsFutile
 
2298
                            )
 
2299
                            {
 
2300
                                if (enemydir.y < -.2f && y < 0)
 
2301
                                    SetTraceSide(-(i+i-1));
 
2302
 
 
2303
                                frontDanger[TRAPLEVEL]    += 10;
 
2304
                            }
 
2305
 
 
2306
                            if ( y > 0 || x < 0 || ohShit
 
2307
                                    //                     ( y * Object()->Speed() > x * target->Speed()*.9 - 200 || enemyspeed.x * (i+i-1)
 
2308
                               )
 
2309
                                sideDanger[TRAPLEVEL][i] += 20;
 
2310
                            // sideDanger[TRAPLEVEL][i] ++;
 
2311
                        }
 
2312
                }
 
2313
        }
 
2314
 
 
2315
        if (character->properties[AI_ENEMY] > 0)
 
2316
        {
 
2317
            // imminent collision check
 
2318
            REAL totalspeed = enemyspeed + Object()->Speed();
 
2319
 
 
2320
            if ((fabs(enemypos.y) < totalspeed * .3f && fabs(enemypos.x) < totalspeed * .3f))
 
2321
            {
 
2322
                REAL diffSpeed  = -enemydir.y * enemyspeed + Object()->Speed();
 
2323
                if (diffSpeed > 0 && enemydir.y <= .2)
 
2324
                {
 
2325
                    REAL enemyFront = enemypos.y / diffSpeed;
 
2326
                    REAL enemySide  = fabs(enemypos.x) / diffSpeed;
 
2327
                    if (enemyFront > 0 && enemyFront < .4 + enemySide && fabs(enemypos.y) > fabs(enemypos.x))
 
2328
                    {
 
2329
                        frontDanger[COLIDELEVEL] += 1 + int(4 / (enemyFront + .01));
 
2330
                        //                    SwitchToState( AI_SURVIVE, enemyFront * 4 + 2 );
 
2331
                    }
 
2332
                }
 
2333
 
 
2334
                int side = enemypos.x > 0 ? 1 : 0;
 
2335
 
 
2336
                // can we cut him instead of evade him?
 
2337
                if (Object()->Team() != target->Team() &&
 
2338
                        ( ( enemydir.y <= -.2 &&
 
2339
                            enemypos.y*target->Speed()*1.1 > fabs(enemypos.x) * Object()->Speed() ) ||
 
2340
                          sideDanger[COLIDELEVEL][side] > 0))
 
2341
                    sideDanger[COLIDELEVEL][1-side]+=5;
 
2342
                else if ( -(enemypos.y + .3f) * Object()->Speed() < fabs(enemypos.x) * target->Speed()*1.2)
 
2343
                    sideDanger[COLIDELEVEL][side]+=10;
 
2344
            }
 
2345
        }
 
2346
    }
 
2347
 
 
2348
    eDebugLine::SetTimeout(.5);
 
2349
    eDebugLine::SetColor  (1, 0, 1);
 
2350
    eCoord p = Object()->Position();
 
2351
    eDebugLine::Draw(p, .5, p, 8.5);
 
2352
    eDebugLine::SetTimeout(0);
 
2353
 
 
2354
 
 
2355
 
 
2356
    // determine the total danger levels by taking the max of the individual experts:
 
2357
    int fDanger = 0;
 
2358
    int sDanger[2] = { 0, 0 };
 
2359
    for (i = 0; i<DANGERLEVELS; i++)
 
2360
        // for (i = 1; i< 2; i++)
 
2361
    {
 
2362
        if (!fDanger || frontDanger[i] > fDanger + 2)
 
2363
            fDanger = frontDanger[i];
 
2364
 
 
2365
        for (int j=1; j>=0; j--)
 
2366
            if (!sDanger[j] || sideDanger[i][j] > sDanger[j] + 2)
 
2367
                sDanger[j] = sideDanger[i][j];
 
2368
    }
 
2369
 
 
2370
    // nothing to do if we are not in immediate danger.
 
2371
    if (!fDanger && !preferedSide)
 
2372
        return false;
 
2373
 
 
2374
 
 
2375
    // decide about your direction:
 
2376
    int turn = 0;
 
2377
 
 
2378
    turn += sDanger[0];
 
2379
    turn -= sDanger[1];
 
2380
 
 
2381
    if (!turn)
 
2382
        turn = (int) freeSide;
 
2383
 
 
2384
    if (!turn && front.front.wallType != gSENSOR_RIM)
 
2385
        turn = front.front.lr * enemyevade;
 
2386
 
 
2387
    if (!turn)
 
2388
        turn = (sides[0]->distance > sides[1]->distance ? -1 : 1);
 
2389
 
 
2390
    if (!turn && log->current)
 
2391
        turn = log->entries[log->current-1].turn;
 
2392
 
 
2393
 
 
2394
 
 
2395
    // switch to survival mode if we just trapped an enemy
 
2396
    if (canTrapEnemy)
 
2397
    {
 
2398
#ifdef DEBUG
 
2399
        if ( !tRecorder::IsRunning() )
 
2400
            Chat(tString( "Hehe! Got you!" ) );
 
2401
#endif
 
2402
        SwitchToState(AI_TRACE, 10);
 
2403
 
 
2404
        if (turn)
 
2405
            this->SetTraceSide(-turn);
 
2406
    }
 
2407
 
 
2408
    gAILogEntry&e = log->NextEntry();
 
2409
    e.turn = 0;
 
2410
    e.tries = triesLeft;
 
2411
    for (i = DANGERLEVELS-1; i>=0; i--)
 
2412
    {
 
2413
        e.frontDanger[i]   = frontDanger[i];
 
2414
        e.sideDanger[i][0] = sideDanger[i][0];
 
2415
        e.sideDanger[i][1] = sideDanger[i][1];
 
2416
    }
 
2417
 
 
2418
 
 
2419
    int side = 1;
 
2420
    if (preferedSide < 0)
 
2421
    {
 
2422
        for (i = DANGERLEVELS-1; i>=0; i--)
 
2423
        {
 
2424
            int dSwap = sideDanger[i][0];
 
2425
            sideDanger[i][0] = sideDanger[i][1];
 
2426
            sideDanger[i][1] = dSwap;
 
2427
        }
 
2428
 
 
2429
        int dSwap = sDanger[0];
 
2430
        sDanger[0] = sDanger[1];
 
2431
        sDanger[1] = dSwap;
 
2432
 
 
2433
        sides[1] = &left;
 
2434
        sides[0] = &right;
 
2435
        side     = -1;
 
2436
        preferedSide = 1;
 
2437
    }
 
2438
 
 
2439
 
 
2440
    // no problem in the preferred direction. Just take it.
 
2441
    if (preferedSide)
 
2442
    {
 
2443
        if( fDanger  * 3 >= sDanger[1] * 2 - 5 &&
 
2444
                sDanger[0] * 3 >= sDanger[1] * 2 - 5)
 
2445
        {
 
2446
            freeSide -= side*100;
 
2447
            e.turn = side;
 
2448
            data.turn = side;
 
2449
            return true;
 
2450
        }
 
2451
 
 
2452
        if (fDanger * 2 <= sDanger[0] * 3 + 3)
 
2453
        {
 
2454
            log->DeleteEntry();
 
2455
            return false;
 
2456
        }
 
2457
    }
 
2458
 
 
2459
    // it is safer driving straight on
 
2460
    if (fDanger <= sDanger[0] + 3 && fDanger <= sDanger[1] + 3 && fDanger < 20)
 
2461
    {
 
2462
        log->DeleteEntry();
 
2463
        return false;
 
2464
    }
 
2465
 
 
2466
 
 
2467
    if (turn)
 
2468
    {
 
2469
        freeSide -= side*100;
 
2470
        e.turn = turn;
 
2471
        data.turn = turn;
 
2472
    }
 
2473
    else
 
2474
        log->DeleteEntry();
 
2475
 
 
2476
    return turn;
 
2477
 
 
2478
    eDebugLine::SetTimeout(0);
 
2479
}
 
2480
 
 
2481
 
 
2482
void gAIPlayer::EmergencyTrace( ThinkData & data )
 
2483
{
 
2484
    EmergencySurvive( data, -1, -traceSide );
 
2485
}
 
2486
 
 
2487
 
 
2488
void gAIPlayer::EmergencyPath( ThinkData & data )
 
2489
{
 
2490
    EmergencySurvive( data );
 
2491
}
 
2492
 
 
2493
void gAIPlayer::EmergencyCloseCombat( ThinkData & data )
 
2494
{
 
2495
    EmergencySurvive( data );
 
2496
 
 
2497
    /*
 
2498
      int dir = 0;
 
2499
 
 
2500
      if (target)
 
2501
      {
 
2502
         eCoord enemyPos = target->Position() - Object()->Position();
 
2503
         eCoord dirRel   = Object()->Direction();
 
2504
         if (enemyPos * dirRel < 0)
 
2505
      dir --;
 
2506
         else
 
2507
      dir ++;
 
2508
      }
 
2509
 
 
2510
 
 
2511
      EmergencySurvive(front, left, right, 1, dir);
 
2512
    */
 
2513
}
 
2514
 
 
2515
 
 
2516
 
 
2517
 
 
2518
void gAIPlayer::RightBeforeDeath(int triesLeft) // is called right before the vehicle gets destroyed.
 
2519
{
 
2520
    if ( nCLIENT == sn_GetNetState() )
 
2521
        return;
 
2522
 
 
2523
    gRandomController random( randomizer_ );
 
2524
 
 
2525
    // think again immediately after this
 
2526
    nextTime = lastTime;
 
2527
 
 
2528
#ifdef DEBUG_X
 
2529
    if (log && !Object()->Alive())
 
2530
    {
 
2531
        log->Print();
 
2532
        //      st_Breakpoint();
 
2533
        delete log;
 
2534
        log = NULL;
 
2535
    }
 
2536
#endif
 
2537
 
 
2538
    if (!Object()->Alive() || ( character && Random() * 10 > character->properties[AI_EMERGENCY] ) )
 
2539
        return;
 
2540
 
 
2541
 
 
2542
    // get the delay between two turns
 
2543
    REAL delay = Delay();
 
2544
 
 
2545
    this->triesLeft = triesLeft;
 
2546
    this->emergency = (triesLeft < 2);
 
2547
 
 
2548
    REAL speed=Object()->Speed();
 
2549
    REAL range=speed;
 
2550
    eCoord dir=Object()->Direction();
 
2551
    REAL  side = speed*delay;
 
2552
 
 
2553
    gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
 
2554
    gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
 
2555
    gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
 
2556
 
 
2557
 
 
2558
 
 
2559
 
 
2560
#ifdef TESTSTATE  
 
2561
    state = TESTSTATE;
 
2562
    nextStateChange = se_GameTime() + 100;
 
2563
#else
 
2564
    // switch to survival state if our victim died:
 
2565
    if ((!target || !target->Alive()) && state != AI_TRACE)
 
2566
        SwitchToState(AI_SURVIVE, 1);
 
2567
#endif
 
2568
 
 
2569
    ThinkData data( front, left, right);
 
2570
    switch (state)
 
2571
    {
 
2572
    case AI_SURVIVE:
 
2573
        EmergencySurvive(data);
 
2574
        break;
 
2575
    case AI_PATH:
 
2576
        EmergencyPath(data);
 
2577
        break;
 
2578
    case AI_TRACE:
 
2579
        EmergencyTrace(data);
 
2580
        break;
 
2581
    case AI_CLOSECOMBAT:
 
2582
        EmergencyCloseCombat(data);
 
2583
        break;
 
2584
    }
 
2585
    ActOnData( data );
 
2586
 
 
2587
#ifdef DEBUG_X
 
2588
    {
 
2589
        gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
 
2590
        gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
 
2591
        gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
 
2592
    }
 
2593
#endif
 
2594
}
 
2595
 
 
2596
void gAIPlayer::NewObject()         // called when we control a new object
 
2597
{
 
2598
    lastTime = 0;
 
2599
    lastPath = 0;
 
2600
    lastChangeAttempt = 0;
 
2601
    lazySideChange = 0;
 
2602
    path.Clear();
 
2603
 
 
2604
    if (character)
 
2605
    {
 
2606
        nextTime        = character->properties[AI_STARTSTRAIGHT] * gArena::SizeMultiplier()/gCycleMovement::SpeedMultiplier();
 
2607
        nextStateChange = character->properties[AI_STATECHANGE];
 
2608
        state           = (gAI_STATE)character->properties[AI_STARTSTATE];
 
2609
    }
 
2610
    else
 
2611
    {
 
2612
        nextTime        = 10;
 
2613
        nextStateChange = 30;
 
2614
        state           = AI_TRACE;
 
2615
    }
 
2616
 
 
2617
    if (log)
 
2618
        delete log;
 
2619
    log = NULL;
 
2620
 
 
2621
    ClearTarget();
 
2622
}
 
2623
 
 
2624
static gAISensor * sg_GetSensor( int currentDirectionNumber, gCycle const & object, int turn, REAL side, REAL range, REAL corridor, REAL & mindist )
 
2625
{
 
2626
    // determine the current direction
 
2627
    eGrid & grid = *object.Grid();
 
2628
    eCoord origDir = grid.GetDirection( currentDirectionNumber );
 
2629
 
 
2630
    // determine sensors
 
2631
    gAISensor * ret = 0;
 
2632
 
 
2633
    // turn direction
 
2634
    int direction = currentDirectionNumber;
 
2635
    int turns = 1;
 
2636
    grid.Turn( direction, turn );
 
2637
    eCoord dir = grid.GetDirection( direction );
 
2638
    while ( turn * ( origDir * dir ) > .01 && currentDirectionNumber != direction )
 
2639
    {
 
2640
        // cast rays
 
2641
        gAISensor * sensor = tNEW(gAISensor)(&object,object.Position(),dir, side, range, corridor*.5f, turn );
 
2642
        if ( !ret || sensor->distance > ret->distance )
 
2643
        {
 
2644
            if ( ret )
 
2645
            {
 
2646
                // calculate effective distance required to turn around in time
 
2647
                REAL dist = ret->distance - 1.2 * object.Speed() * Delay() * turns;
 
2648
                if ( mindist > dist )
 
2649
                    mindist = dist;
 
2650
            }
 
2651
 
 
2652
            delete ret;
 
2653
            ret = sensor;
 
2654
        }
 
2655
        else
 
2656
        {
 
2657
            delete sensor;
 
2658
        }
 
2659
 
 
2660
        // go on turning
 
2661
        grid.Turn( direction, turn );
 
2662
        dir = grid.GetDirection( direction );
 
2663
        ++turns;
 
2664
    }
 
2665
 
 
2666
    return ret;
 
2667
}
 
2668
 
 
2669
REAL gAIPlayer::Think(){
 
2670
    // get the delay between two turns
 
2671
    REAL delay = Delay();
 
2672
 
 
2673
#ifdef DEBUG_X  
 
2674
    if (log && !Object()->Alive())
 
2675
    {
 
2676
        log->Print();
 
2677
        st_Breakpoint();
 
2678
        delete log;
 
2679
        log = NULL;
 
2680
    }
 
2681
#endif
 
2682
 
 
2683
    if (!Object()->Alive())
 
2684
        return 100;
 
2685
 
 
2686
    emergency = false;
 
2687
    //  return 1;
 
2688
 
 
2689
    // first, find close eWalls and evade them.
 
2690
    REAL speed=Object()->Speed();
 
2691
    REAL range=speed;
 
2692
    eCoord dir=Object()->Direction();
 
2693
    REAL side=speed*delay;
 
2694
 
 
2695
    REAL corridor = range;
 
2696
    if (corridor < side * 2)
 
2697
        corridor = side * 2;
 
2698
 
 
2699
    gAISensor front(Object(),Object()->Position(),dir, side * 2, range, corridor, 0);
 
2700
 
 
2701
#ifdef DEBUG_X
 
2702
    if (front.Hit())
 
2703
    {
 
2704
        gRandomController noRandom;
 
2705
 
 
2706
        if (front.distance < 1)
 
2707
        {
 
2708
            int x;
 
2709
            x = 1;
 
2710
        }
 
2711
        gAISensor front(Object(),Object()->Position(),dir, side, range, corridor, 0);
 
2712
    }
 
2713
#endif
 
2714
 
 
2715
    // get the sensors to the left and right with the most free space
 
2716
    int currentDirectionNumber = Object()->Grid()->DirectionWinding( dir );
 
2717
    REAL mindistLeft = 1E+30, mindistRight = 1E+30;
 
2718
    std::auto_ptr< gAISensor > left  ( sg_GetSensor( currentDirectionNumber, *Object(), -1, side, range, corridor, mindistLeft ) );
 
2719
    std::auto_ptr< gAISensor > right ( sg_GetSensor( currentDirectionNumber, *Object(), 1, side, range, corridor, mindistRight ) );
 
2720
 
 
2721
    // count intermediate walls to the left and right as if they were in front
 
2722
    {
 
2723
        REAL mindistFront = mindistLeft > mindistRight ? mindistLeft : mindistRight;
 
2724
        if ( mindistFront < front.distance )
 
2725
        {
 
2726
            front.distance = mindistFront;
 
2727
        }
 
2728
    }
 
2729
 
 
2730
#ifdef TESTSTATE  
 
2731
    state = TESTSTATE;
 
2732
    nextStateChange = se_GameTime() + 100;
 
2733
#else
 
2734
    // switch to survival state if our victim died:
 
2735
    if (state != AI_SURVIVE && state != AI_TRACE && (!target || !target->Alive()))
 
2736
        SwitchToState(AI_SURVIVE, 1);
 
2737
#endif
 
2738
 
 
2739
    {
 
2740
        eDebugLine::SetTimeout(.5);
 
2741
        eDebugLine::SetColor  (0, 1, 0);
 
2742
        eCoord p = Object()->Position();
 
2743
        eDebugLine::Draw(p, .5, p, 5.5);
 
2744
        eDebugLine::SetTimeout(0);
 
2745
    }
 
2746
 
 
2747
    triesLeft = 10;
 
2748
 
 
2749
    REAL ret = 1;
 
2750
 
 
2751
    ThinkData data( front, *left, *right);
 
2752
    switch (state)
 
2753
    {
 
2754
    case AI_SURVIVE:
 
2755
        ThinkSurvive(data);
 
2756
        break;
 
2757
    case AI_PATH:
 
2758
        ThinkPath(data);
 
2759
        break;
 
2760
    case AI_TRACE:
 
2761
        ThinkTrace(data);
 
2762
        break;
 
2763
    case AI_CLOSECOMBAT:
 
2764
        ThinkCloseCombat(data);
 
2765
        break;
 
2766
    }
 
2767
    ActOnData( data );
 
2768
    ret = data.thinkAgain;
 
2769
 
 
2770
#ifdef DEBUG_X
 
2771
    {
 
2772
        gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
 
2773
        gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
 
2774
        gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
 
2775
    }
 
2776
#endif
 
2777
 
 
2778
    REAL mindist = front.distance * front.distance * 8;
 
2779
 
 
2780
    const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
 
2781
 
 
2782
    // find the closest enemy
 
2783
    for (int i=gameObjects.Len()-1;i>=0;i--){
 
2784
        gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
 
2785
 
 
2786
        if (other && other != Object()){
 
2787
            // then, enemy is realy an enemy
 
2788
            eCoord otherpos=other->Position()-Object()->Position();
 
2789
            REAL dist = otherpos.NormSquared();
 
2790
 
 
2791
            if (dist < mindist)
 
2792
                mindist = dist;
 
2793
        }
 
2794
    }
 
2795
 
 
2796
    mindist = sqrt(mindist) / (3 * Object()->Speed());
 
2797
 
 
2798
    if (ret > mindist)
 
2799
        ret = mindist;
 
2800
 
 
2801
    return ret;
 
2802
}
 
2803
 
 
2804
std::ostream & operator << ( std::ostream & s, gAIPlayer::ThinkDataBase const & data )
 
2805
{
 
2806
    s << data.turn << " " << data.thinkAgain;
 
2807
 
 
2808
    return s;
 
2809
}
 
2810
 
 
2811
std::istream & operator >> ( std::istream & s, gAIPlayer::ThinkDataBase & data )
 
2812
{
 
2813
    s >> data.turn >> data.thinkAgain;
 
2814
 
 
2815
    return s;
 
2816
}
 
2817
 
 
2818
static char const * section = "AI";
 
2819
 
 
2820
void gAIPlayer::ActOnData( ThinkData & data )
 
2821
{
 
2822
    // delegate
 
2823
    ThinkDataBase & base = data;
 
2824
    ActOnData( base );
 
2825
 
 
2826
    // sanitize next think time so it will be before we hit the next wall
 
2827
    if ( Object()->Speed() > 0 && data.thinkAgain > 0 )
 
2828
    {
 
2829
        gSensor front( Object(), Object()->Position(), Object()->Direction() );
 
2830
        front.detect( Object()->Speed() * data.thinkAgain * 1.5 );
 
2831
        if ( front.ehit )
 
2832
        {
 
2833
            REAL thinkAgain = ( front.hit / Object()->Speed() ) * .8;
 
2834
            if ( data.thinkAgain > thinkAgain )
 
2835
                data.thinkAgain = thinkAgain;
 
2836
        }
 
2837
    }
 
2838
}
 
2839
 
 
2840
void gAIPlayer::ActOnData( ThinkDataBase & data )
 
2841
{
 
2842
    // archive decision
 
2843
    ThinkDataBase copy = data;
 
2844
    if ( tRecorder::Playback( section, data ) )
 
2845
    {
 
2846
        if ( copy.turn != data.turn )
 
2847
        {
 
2848
            // AI made a different decision than recorded, better let a programmer have a look at it
 
2849
            std::cout << "AI turn decision changed!\n";
 
2850
            st_Breakpoint();
 
2851
        }
 
2852
 
 
2853
        REAL difference =  fabs( copy.thinkAgain - data.thinkAgain );
 
2854
        static REAL minReport = EPS;
 
2855
        if ( difference > minReport )
 
2856
        {
 
2857
            minReport = difference * 2;
 
2858
            std::cout << "AI timing decision changed by " << difference
 
2859
            << " from " << data.thinkAgain << " to " << copy.thinkAgain <<"!\n";
 
2860
            st_Breakpoint();
 
2861
        }
 
2862
    }
 
2863
    tRecorder::Record( section, data );
 
2864
 
 
2865
    // execute turn
 
2866
    if ( data.turn )
 
2867
        Object()->Turn( data.turn );
 
2868
}
 
2869
 
 
2870
const REAL relax=25;
 
2871
 
 
2872
void gAIPlayer::Timestep(REAL time){
 
2873
    if (!character)
 
2874
    {
 
2875
        st_Breakpoint();
 
2876
        return;
 
2877
    }
 
2878
 
 
2879
    REAL ts=time-lastTime;
 
2880
    lastTime=time;
 
2881
 
 
2882
    if (concentration < 0)
 
2883
        concentration = 0;
 
2884
 
 
2885
    concentration += 4*(character->properties[AI_REACTION]+1) * ts/relax;
 
2886
    concentration=concentration/(1+ts/relax);
 
2887
 
 
2888
    if (bool(Object()) && Object()->Alive() && nextTime<time){
 
2889
        gRandomController random( randomizer_ );
 
2890
 
 
2891
        REAL nextthought=Think();
 
2892
        //    if (nextthought>.9) nextthought=REAL(.9);
 
2893
 
 
2894
        if (nextthought<REAL(.6-concentration)) nextthought=REAL(.6-concentration);
 
2895
 
 
2896
        nextTime=nextTime+nextthought;
 
2897
 
 
2898
        //con << concentration << "\t" << nextthought << '\t' << ts << '\n';
 
2899
 
 
2900
        if(.1+4*nextthought<1)
 
2901
            concentration*=REAL(.1+4*nextthought);
 
2902
    }
 
2903
}
 
2904
 
 
2905
 
 
2906
void gAIPlayer::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
 
2907
{
 
2908
    ePlayerNetID::Color( a_r, a_g, a_b );
 
2909
}
 
2910
 
 
2911
gAIPlayer::~gAIPlayer()
 
2912
{
 
2913
    target=NULL;
 
2914
    ClearObject();
 
2915
    tCHECK_DEST;
 
2916
 
 
2917
    delete log;
 
2918
    log = NULL;
 
2919
}
 
2920
 
 
2921
void gAIPlayer::ClearAll()
 
2922
{
 
2923
    sg_AIReferences.ReleaseAll();
 
2924
 
 
2925
    // remove empty AI team
 
2926
    if ( 0 == AITeam()->NumPlayers() )
 
2927
    {
 
2928
        ClearAITeam();
 
2929
    }
 
2930
}
 
2931
/*
 
2932
void gAIPlayer::AddRef()
 
2933
{
 
2934
        ePlayerNetID::AddRef();
 
2935
}
 
2936
 
 
2937
void gAIPlayer::Release()
 
2938
{
 
2939
        ePlayerNetID::Release();
 
2940
}
 
2941
*/