src/tron/gAIBase.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023   
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "gAIBase.h"
00029 #include "gArena.h"
00030 #include "eGrid.h"
00031 #include "ePath.h"
00032 #include "eGameObject.h"
00033 #include "gWall.h"
00034 #include "gSensor.h"
00035 #include "gCycle.h"
00036 #include "tConsole.h"
00037 #include "rFont.h"
00038 #include "rScreen.h"
00039 #include "eFloor.h"
00040 #include "eDebugLine.h"
00041 #include "gAICharacter.h"
00042 #include "tReferenceHolder.h"
00043 #include "tRandom.h"
00044 #include "tRecorder.h"
00045 #include <stdlib.h>
00046 #include <cstdlib>
00047 #include <memory>
00048 
00049 #define AI_REACTION          0 
00050 #define AI_EMERGENCY         1 
00051 #define AI_RANGE             2 
00052 #define AI_STATE_TRACE       3 
00053 #define AI_STATE_CLOSECOMBAT 4 
00054 #define AI_STATE_PATH        5 
00055 #define AI_LOOP              6 
00056 #define AI_ENEMY             7 
00057 #define AI_TUNNEL            8 
00058 #define AI_DETECTTRACE       9 
00059 #define AI_STARTSTATE        10
00060 #define AI_STARTSTRAIGHT     11
00061 #define AI_STATECHANGE       12
00062 
00063 static tReferenceHolder< gAIPlayer > sg_AIReferences;
00064 
00065 #ifdef DEBUG
00066 //#define TESTSTATE AI_PATH
00067 //#define TESTSTATE AI_TRACE
00068 #endif
00069 //#define DEBUGLINE
00070 
00071 static tCONTROLLED_PTR(gAITeam) sg_AITeam = NULL;
00072 
00073 gSimpleAIFactory *gSimpleAIFactory::factory_ = NULL;
00074 
00075 gSimpleAI::~gSimpleAI()
00076 {
00077     con << "simple AI destroyed.\n";
00078 }
00079 
00080 gSimpleAI * gSimpleAIFactory::Create( gCycle * object ) const
00081 {
00082     gSimpleAI * ai = DoCreate();
00083     if ( ai )
00084         ai->SetObject( object );
00085     return ai;
00086 }
00087 
00088 gSimpleAIFactory * gSimpleAIFactory::Get()
00089 {
00090     return factory_;
00091 }
00092 
00093 void gSimpleAIFactory::Set( gSimpleAIFactory * factory )
00094 {
00095     factory_ = factory;
00096 }
00097 
00098 static gAITeam* AITeam()
00099 {
00100     if ( !sg_AITeam )
00101     {
00102         sg_AITeam = tNEW( gAITeam );
00103     }
00104 
00105     return sg_AITeam;
00106 }
00107 
00108 static void ClearAITeam()
00109 {
00110     sg_AITeam = NULL;
00111 }
00112 
00113 // an instance of this class will prevent deterministic random lookups
00114 class gRandomController
00115 {
00116 public:
00117     static gRandomController * random_;
00118     gRandomController * lastRandom_;
00119     tRandomizer & randomizer_;
00120 
00121     gRandomController( tRandomizer & randomizer = tRandomizer::GetInstance() )
00122             : lastRandom_( random_ ), randomizer_( randomizer )
00123     {
00124         random_ = this;
00125     }
00126 
00127     ~gRandomController()
00128     {
00129         random_ = lastRandom_;
00130     }
00131 };
00132 
00133 gRandomController * gRandomController::random_ = 0;
00134 
00135 REAL Random()
00136 {
00137     if ( gRandomController::random_ )
00138     {
00139         tRandomizer & randomizer = gRandomController::random_->randomizer_;
00140         return randomizer.Get();
00141     }
00142     else
00143     {
00144         tRandomizer & randomizer = tRandomizer::GetInstance();
00145         return randomizer.Get();
00146     }
00147 }
00148 
00149 static REAL Delay()
00150 {
00151     REAL delay = sg_delayCycle * .9f;
00152 
00153     REAL fd    = se_AverageFrameTime()*1.5f;
00154     if ( fd > delay)
00155         delay = fd;
00156 
00157     return delay;
00158 }
00159 
00160 
00161 
00162 static gAICharacter* BestIQ( int iq )
00163 {
00164     int i;
00165 
00166     static tArray<bool> inGame(gAICharacter::s_Characters.Len());
00167     for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
00168         inGame(i) = false;
00169 
00170     for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
00171     {
00172         // count the active AIs
00173         ePlayerNetID *p = se_PlayerNetIDs(i);
00174         gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
00175         if (ai && ai->Character() )
00176         {
00177             int index = ai->Character() - &(gAICharacter::s_Characters(0));
00178             inGame(index) = true;
00179         }
00180     }
00181 
00182     // find the best fitting IQ that could be inserted:
00183     gAICharacter* bestIQ = 0;
00184     for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
00185         if (!inGame(i))
00186             if (!bestIQ || fabs(bestIQ->iq - iq) > fabs(gAICharacter::s_Characters(i).iq - iq))
00187                 bestIQ = &gAICharacter::s_Characters(i);
00188 
00189     return bestIQ;
00190 }
00191 
00192 
00193 
00194 gAITeam::gAITeam(nMessage &m) : eTeam(m)
00195 {
00196     //  teams.Remove( this, listID );
00197 }
00198 
00199 gAITeam::gAITeam()
00200 {
00201     //  teams.Remove( this, listID );
00202 }
00203 
00204 static nNOInitialisator<gAITeam> gAITeam_init(331,"gAITeam");
00205 
00206 nDescriptor &gAITeam::CreatorDescriptor() const
00207 {
00208     return gAITeam_init;
00209 }
00210 
00211 // fill empty team positions with AI players
00212 void gAITeam::BalanceWithAIs(bool balanceWithAIs)
00213 {
00214     // set correct team number
00215     EnforceConstraints();
00216 
00217     int numTeams = 0, numTeamsWithPlayers = 0;
00218 
00219     // determine the maximum number of human players on a team
00220     int i;
00221     int maxP = eTeam::minPlayers;
00222     for ( i = teams.Len()-1; i>=0; --i )
00223     {
00224         eTeam *t = teams(i);
00225 
00226         t->UpdateProperties();
00227 
00228         if ( t->BalanceThisTeam() )
00229             numTeams++;
00230 
00231         if ( t->NumHumanPlayers() > 0 )
00232             numTeamsWithPlayers++;
00233 
00234         int humans = t->NumHumanPlayers();
00235 
00236         if ( humans > maxP )
00237         {
00238             maxP = humans;
00239         }
00240     }
00241 
00242     // make sure all teams have equal number
00243     for ( i = teams.Len()-1; i>=0; --i )
00244     {
00245         eTeam *t = teams(i);
00246 
00247         if ( t->BalanceThisTeam() )
00248         {
00249             int wishAIs = maxP - t->NumHumanPlayers();
00250 
00251             // don't add AI players to human team if it is not requested
00252             if ( !balanceWithAIs && t->NumHumanPlayers() > 0 )
00253                 wishAIs = 0;
00254 
00255             // don't genereate AI only teams if it is not requested by the min team count
00256             if ( numTeamsWithPlayers >= minTeams && t->NumHumanPlayers() == 0 )
00257                 wishAIs = 0;
00258 
00259             // add AI only teams to team count
00260             if ( wishAIs > 0 && t->NumHumanPlayers() == 0 )
00261                 numTeamsWithPlayers++;
00262 
00263             int AIs = t->NumAIPlayers();
00264 
00265             while ( AIs > wishAIs )
00266             {
00267                 int j;
00268                 gAIPlayer* throwOut = NULL;
00269                 for ( j=t->NumPlayers()-1; j>=0; --j )
00270                 {
00271                     gAIPlayer* player = dynamic_cast<gAIPlayer*>( t->Player( j ) );
00272                     if ( player && !player->IsHuman() && ( !throwOut || !throwOut->Character() || !player->Character() || throwOut->Character()->iq > player->Character()->iq ) )
00273                     {
00274                         throwOut = player;
00275                     }
00276                 }
00277 
00278                 if ( throwOut )
00279                 {
00280                     throwOut->RemoveFromGame();
00281                     //                                  throwOut->ReleaseOwnership();
00282 
00283 #ifdef DEBUG
00284                     if ( throwOut->GetRefcount() > 1 )
00285                     {
00286                         st_Breakpoint();
00287                     }
00288 #endif
00289 
00290                     sg_AIReferences.Remove( throwOut );
00291                 }
00292 
00293                 --AIs;
00294             }
00295 
00296             while ( AIs < wishAIs )
00297             {
00298                 // add the smartest AI player you can get
00299                 gAICharacter* best = BestIQ( 1000 );
00300 
00301                 if ( best )
00302                 {
00303                     gAIPlayer *ai       = tNEW( gAIPlayer ) ();
00304                     ai->character       = best;
00305                     ai->SetName( best->name );
00306                     ai->SetTeam( t );
00307                     ai->UpdateTeam();
00308 
00309                     sg_AIReferences.Add( ai );
00310                 }
00311 
00312                 ++AIs;
00313             }
00314         }
00315     }
00316 
00317     // get rid of deleted netobjects (teams, mostly)
00318     nNetObject::ClearAllDeleted();
00319 }
00320 
00321 // may player join this team?
00322 bool gAITeam::PlayerMayJoin(const ePlayerNetID* player) const
00323 {
00324     return !player->IsHuman();
00325 }
00326 
00327 
00328 // this class describes a single wall close event
00329 class gCycleTouchEvent{
00330 public:
00331     REAL dist;      // the position on this cycle's wall the touching happened
00332     REAL otherDist; // the position on the other cylce's wall
00333     int  otherSide; // the side of the other cylce this wall touches
00334     int  winding;   // winding number to add if we cross here
00335 
00336     gCycleTouchEvent()
00337     {
00338         dist      = 0;
00339         otherDist = 0;
00340         otherSide = -100;
00341     }
00342 };
00343 
00344 class gCycleMemoryEntry{
00345 public:
00346     gCycleMemory *memory;
00347     int id;
00348     nObserverPtr< gCycle > cycle;
00349 
00350     gCycleMemoryEntry(gCycleMemory* m, const gCycle* c)
00351             :memory(m),id(-1), cycle(c)
00352     {
00353         memory->memory.Add(this, id);
00354 
00355         max[0].dist = -1E+30;
00356         max[1].dist = -1E+30;
00357         min[0].dist =  1E+30;
00358         min[1].dist =  1E+30;
00359     }
00360 
00361     ~gCycleMemoryEntry()
00362     {
00363         memory->memory.Remove(this, id);
00364     }
00365 
00366     // usable data
00367     gCycleTouchEvent max[2]; // latest touch event (with the given cylce)
00368     gCycleTouchEvent min[2]; // earliest touch event
00369 };
00370 
00371 #define TOL 4
00372 
00373 // look for a closed loop in the walls if cycle a hits cycle b's wall at
00374 // distance bDist and on side bSide.
00375 // Look for the loop in driving direction of b if dir is 1 or to the other side of dir is 0.
00376 // the end of the loop is reached when the wall of cycle a is driven along in
00377 // direction aEndDir, passing the distance aEndDist and the side's bit is set
00378 // in aEndSides.
00379 // Cycles that will be closed in the loop are stored in the array
00380 // closedIn.
00381 // return value: the number of open points this loop contains
00382 // (if this is >0, that usually means the space is wide open)
00383 //  or -1 if there is no loop.
00384 static bool CheckLoop(const gCycle *a, const gCycle *b,
00385                       REAL bDist, int bSide, int dir,
00386                       tArray<const gCycle*>& closedIn, int& winding,
00387                       REAL aEndDist = 0, int aEndSides = 3, int aEndDir = 1 )
00388 {
00389     tASSERT(0<= bSide && 1 >= bSide);
00390     tASSERT(0<= dir && 1 >= dir);
00391 
00392     int tries = 10;       // so long until we give up
00393     int ends  = 0;
00394 
00395     bool bClosedIn    = false;
00396 
00397     const gCycle *run = b;      // we run along this cycle's wall
00398     int end           = dir;    // and move towards this end its wall wall
00399     int side          = bSide;  // we are on this side of the cycle
00400     REAL dist         = bDist;  // and are at this distance.
00401     winding           = 0;      // the winding number we collected
00402 
00403     int turn     = a->Grid()->WindingNumber();
00404     int halfTurn = turn >> 1;
00405 
00406 
00407 #ifdef DEBUG
00408     //  con << "\n";
00409 #endif
00410 
00411     while(tries-- > 0 && run &&
00412             !(run == a &&
00413               end == aEndDir &&
00414               aEndSides & (1 << side) &&
00415               (end > 0 ? dist >= aEndDist : dist <= aEndDist ) ) )
00416     {
00417 #ifdef DEBUG
00418         //      con << "end = " << end << ", side = " << side << ", dist = " << dist
00419         //        << ", winding = " << winding << "\n";
00420 #endif
00421         if (end > 0)
00422         {
00423 
00424             // find the last connection
00425             gCycleMemoryEntry* last = run->memory.Latest(side);
00426             if (!last || last->max[side].dist <= dist + TOL)
00427             {
00428                 // no interference. We can move directly around the cylce
00429                 // and close it in.
00430 #ifdef DEBUG
00431                 //            con << "Turning around...\n";
00432 #endif
00433 
00434                 winding += halfTurn *
00435                            ( side > 0 ? -1 : 1);
00436 
00437                 end  = 0;
00438                 side = 1-side;
00439                 closedIn[closedIn.Len()] = run;
00440                 dist = run->GetDistance();
00441 
00442                 // detect early loop
00443                 if (run == b)
00444                     if (bClosedIn)
00445                     {
00446                         winding = 0;
00447                         return false;
00448                     }
00449                     else
00450                         bClosedIn = true;
00451             }
00452             else
00453             {
00454 #ifdef DEBUG
00455                 //            con << "Crossing...\n";
00456 #endif
00457 
00458                 // find the first connection
00459                 gCycleMemoryEntry* first = run->memory.Earliest(side);
00460                 if (first && first->min[side].dist >= dist + TOL)
00461                 {
00462                     // we cross the connection:
00463                     winding += first->min[side].winding;
00464                     run      = first->cycle;
00465                     end      = (side == first->min[side].otherSide) ? 1 : 0;
00466 
00467                     //            if (end == 0)
00468                     // we need to turn around to follow
00469                     winding += halfTurn * (side > 0 ? 1 : -1);
00470 
00471                     dist     = first->min[side].otherDist;
00472                     side     = first->min[side].otherSide;
00473                 }
00474                 else
00475                 {
00476                     winding = 0;
00477                     return false;
00478                 }
00479             }
00480         }
00481         else // dir = -1, we move towards the end
00482         {
00483             // find the first connection
00484             gCycleMemoryEntry* first = run->memory.Earliest(side);
00485             if (!first || first->min[side].dist >= dist - TOL)
00486             {
00487 #ifdef DEBUG
00488                 //            con << "Turning around...\n";
00489 #endif
00490 
00491 
00492                 // no interference. We can move directly around the cylce's end.
00493                 winding += halfTurn * ( side > 0 ? 1 : -1);
00494 
00495                 end  = 1;
00496                 side = 1-side;
00497                 ends++;
00498                 dist = -2 * TOL;
00499             }
00500             else
00501             {
00502 #ifdef DEBUG
00503                 //            con << "Crossing...\n";
00504 #endif
00505 
00506 
00507                 // find the latest connection
00508                 gCycleMemoryEntry* last = run->memory.Latest(side);
00509                 if (last && last->max[side].dist <= dist - TOL)
00510                 {
00511                     // we cross the connection:
00512                     winding += last->max[side].winding;
00513 
00514                     // we need to turn around to start:
00515                     winding += halfTurn * (side > 0 ? -1 : 1);
00516 
00517                     run      = last->cycle;
00518                     end      = (side == last->max[side].otherSide) ? 0 : 1;
00519 
00520                     //            if (end == 1)
00521                     // we need to turn around to follow
00522                     //            winding -= halfTurn * (side > 0 ? 1 : -1);
00523 
00524                     dist     = last->max[side].otherDist;
00525                     side     = last->max[side].otherSide;
00526                 }
00527                 else
00528                     // uh oh. we are already closed in. No chance...
00529                 {
00530                     winding = 0;
00531                     return false;
00532                 }
00533             }
00534         }
00535     }
00536 
00537 #ifdef DEBUG
00538     //      con << "end = " << end << ", side = " << side << ", dist = " << dist
00539     //    << ", winding = " << winding << "\n\n";
00540 #endif
00541 
00542     if (tries >= 0)
00543     {
00544         return true;
00545     }
00546     else
00547     {
00548         winding = 0;
00549         return false;
00550     }
00551 }
00552 
00553 
00554 // see if the given Cycle is trapped currently
00555 static bool IsTrapped(const gCycle *trapped, const gCycle *other)
00556 {
00557     tArray<const gCycle*> closedIn;
00558     int winding = 0;
00559     if (CheckLoop(trapped, trapped, trapped->GetDistance(), 1, 0, closedIn, winding, trapped->GetDistance() - 1))
00560     {
00561         if (winding + 2 < 0)
00562         {
00563             // see if the other cylce is trapped with him
00564             for (int i = closedIn.Len()-1; i>=0; i--)
00565                 if (other == closedIn(i))
00566                     return false;  // we can get him!
00567 
00568             // no. trapped is trapped allone.
00569             return true;
00570         }
00571     }
00572 
00573     return false;
00574 }
00575 
00576 
00577 
00578 
00579 // data about a loop
00580 class gLoopData{
00581 public:
00582     bool loop;                       // is there a loop?
00583     int  winding;                    // in what direction does it go?
00584     tArray<const gCycle*>closedIn;   // which cycles are closed in?
00585 
00586     gLoopData():loop(false), winding(0){}
00587     void AddCycle(const gCycle* c){closedIn[closedIn.Len()] = c;}
00588 };
00589 
00590 // hit data
00591 class gHitData{
00592 public:
00593     const eHalfEdge*  edge;      // edge we hit
00594     gSensorWallType   wallType;  // type of the wall hit
00595     int               lr;        // does the wall go left or right?
00596     REAL              distance;  // distance to the wall
00597 
00598     // additional info if the wall that got hit is a cycle wall
00599     gCycle         *otherCycle;    // the cylce that the hit wall belongs to
00600     REAL            driveDistance; // the distance it had travelled when it was at the place we hit
00601     int             windingNumber; // the winding number at the place hit
00602 
00603     gHitData():edge(NULL), wallType(gSENSOR_NONE), otherCycle(NULL){}
00604 
00605     bool Hit() const {return edge;}
00606 
00607     void AddHit(const eCoord& origin, const eCoord& dir, const gSensor& sensor, int winding)
00608     {
00609         if (!sensor.ehit)
00610             return; // no hit, nothing to do
00611 
00612         /*
00613         REAL otherDist = eCoord::F(*sensor.ehit->Point(), dir)
00614                          - eCoord::F(origin, dir);
00615 
00616         {
00617             REAL otherDist2 = eCoord::F(*sensor.ehit->Other()->Point(), dir)
00618                               - eCoord::F(origin, dir);
00619             if (otherDist2 < otherDist)
00620                 otherDist = otherDist2;
00621         }
00622         */
00623         REAL otherDist = eCoord::F( dir, sensor.before_hit - origin );
00624 
00625         if (otherDist < EPS)
00626             return; // the new hit is a wall we apparently drive along; nothing to do
00627 
00628         if (Hit() && distance < otherDist)
00629             return; // the hit that is already stored is more relevant. Ignore the new hit.
00630 
00631         // copy the relevant data
00632         edge     = sensor.ehit;
00633         wallType = sensor.type;
00634         lr       = sensor.lr;
00635         distance = otherDist;
00636 
00637         REAL alpha = edge->Ratio(sensor.before_hit);
00638 
00639         // get the extra information from the wall we hit:
00640         gPlayerWall *w = dynamic_cast<gPlayerWall*>(edge->GetWall());
00641         if (!w)
00642         {
00643             alpha = 1-alpha;
00644             w = dynamic_cast<gPlayerWall*>(edge->Other()->GetWall());
00645         }
00646 
00647         if (w)
00648         {
00649             otherCycle     = w->Cycle();
00650             driveDistance  = w->Pos(alpha);
00651             windingNumber  = w->WindingNumber() - winding;
00652         }
00653     }
00654 };
00655 
00656 // special sensor that scans a broader area (not just a raycast)
00657 class gAISensor{
00658 public:
00659     // the raw data we collect:
00660 
00661     const gCycle* cycle; // the cycle that sent out this sensor
00662 
00663     bool     hit;       // whether a dangerous spot is hit
00664 
00665     gHitData front;      // what happens in our front
00666     gHitData sides[2];   // and on our sides
00667 
00668     // what we make of it:
00669     gLoopData frontLoop[2];   // does the front wall we hit cause us to be trapped if we turn left or right?
00670     gLoopData sideLoop[2][2]; // do the two side walls cause us to be trapped if we turn/drive straight on?
00671 
00672     REAL distance;            // distance to the closest wall
00673 
00674     bool Hit() const
00675     {
00676         return hit;
00677     }
00678 
00679     void DetectLoop(const gHitData& hit, gLoopData loopData[2])
00680     {
00681         // avoid loops:
00682         gCycle *other = hit.otherCycle;
00683 
00684         if (other)
00685         {
00686             //    if (other!= Object())
00687             {
00688                 REAL    dist  = hit.driveDistance;
00689                 int otherSide = hit.lr < 0 ? 0 : 1;
00690                 for (int i=1; i>=0; i--)
00691                 {
00692                     loopData[i].closedIn.SetLen(0);
00693                     loopData[i].loop = false;
00694 
00695                     int lr   = i+i-1;
00696                     int dir  = hit.lr * lr > 0 ? 1 : 0;
00697                     int winding = 0;
00698                     bool loop = CheckLoop(cycle, other,
00699                                           dist, otherSide, dir,
00700                                           loopData[i].closedIn, winding);
00701 
00702                     if (loop)
00703                     {
00704                         // complete the winding calculation: the target winding
00705                         // is the one of this cycle:
00706                         winding += cycle->WindingNumber();
00707                         // and the source is the winding of the wall we hit
00708                         winding -= hit.windingNumber;
00709 
00710                         //                    if (dir == 0)
00711                         //                      winding -= lr * (Object()->Grid()->WindingNumber() >> 1);
00712 
00713                         winding += lr;
00714 
00715 #ifdef DEBUG_X
00716                         if (winding != 4 && winding != -4)
00717                         {
00718                             gRandomController noRandom;
00719                             if (winding != 0)
00720                                 //                      st_Breakpoint();
00721 
00722                                 winding = 0;
00723                             CheckLoop(cycle, other,
00724                                       dist, otherSide, dir,
00725                                       loopData[i].closedIn, winding);
00726 
00727                             winding += cycle->WindingNumber();
00728                             winding -= hit.windingNumber;
00729                             winding += lr;
00730                         }
00731 #endif
00732 
00733 #ifdef DEBUG
00734                         //                    con << "winding = " << winding << " ,direction = " << lr << "\n";
00735 #endif
00736                         // if the winding continues the direction we would turn in,
00737                         // we're trapped.
00738                         if (winding * lr > 0)
00739                             loopData[i].loop = true;
00740 
00741                         loopData[i].winding = winding;
00742                     }
00743                 }
00744             }
00745         }
00746     }
00747 
00748     gAISensor(const gCycle* c,
00749               const eCoord& start, const eCoord& dir,
00750               REAL sideScan, // the amout of space we should scan left and right
00751               REAL frontScan, // the total front scanning range
00752               REAL corridorScan, // the corridor scanning range
00753               int winding        // direction relative to the cycle's driving direction
00754              )
00755             :cycle(c), distance(frontScan*2)
00756     {
00757         gAIPlayer* ai = dynamic_cast<gAIPlayer*>(c->Player());
00758         tASSERT(ai);
00759         gAICharacter* character = ai->Character();
00760         tASSERT(character);
00761 
00762         hit = false;
00763 
00764         eDebugLine::SetTimeout(.5);
00765 
00766         gCycle* cycle = const_cast<gCycle*>( this->cycle );
00767 
00768         // detect straight ahead
00769         gSensor ahead(cycle, start, dir);
00770 
00771         int count = 0;
00772 
00773         do{
00774             ahead.detect(frontScan);
00775             front.AddHit(start, dir, ahead, winding);
00776             if (front.Hit())
00777             {
00778                 hit = true;
00779                 distance = front.distance;
00780                 if (character->properties[AI_LOOP] > 3 + fabsf(winding) * 3)
00781                     DetectLoop(front, frontLoop);
00782             }
00783         } while (!front.Hit() && count++ < character->properties[AI_RANGE]);
00784 
00785         // adapt the corridor distance so the corridor is not looked for too far away
00786         if (distance*.99f < corridorScan)
00787             corridorScan = distance * .99f;
00788         corridorScan -= sideScan * .02;
00789         if (corridorScan < 0.1f)
00790             corridorScan = 0.1f;
00791 
00792         if (Random() * 10 < character->properties[AI_TUNNEL])
00793         {
00794             // check the sides
00795             eCoord lookTunnel = start + dir * corridorScan;
00796 
00797             int i;
00798 
00799             for (i = 1; i>=0; i--)
00800             {
00801                 int lr       = i+i-1;
00802                 eCoord lrDir = dir.Turn(eCoord(.01f, -lr));
00803 
00804                 gSensor side(cycle, start, lrDir);
00805                 side.detect(sideScan*1.01f);
00806                 REAL thisSideScan = side.hit*.99f;
00807 
00808                 gSensor tunnel(cycle, lookTunnel, lrDir);
00809                 tunnel.detect(thisSideScan);
00810                 sides[i].AddHit(start, dir, tunnel, winding + lr);
00811 
00812                 gSensor parallel(cycle, start + lrDir * thisSideScan, dir);
00813                 parallel.detect(corridorScan);
00814                 sides[i].AddHit(start, dir, parallel, winding);
00815 
00816                 if (sides[i].Hit())
00817                 {
00818                     if (character->properties[AI_LOOP] > 6 + fabsf(winding) * 3)
00819                         DetectLoop(sides[i], sideLoop[i]);
00820 
00821                     if (sideLoop[i][1-i].loop ||
00822                             (character->properties[AI_TUNNEL] >= 10 &&
00823                              sides[i].otherCycle &&
00824                              sides[i].otherCycle->Team() != cycle->Team() &&
00825                              sides[i].lr * (i+i-1) < 0 &&
00826                              sides[i].otherCycle->GetDistance() < sides[i].driveDistance + sides[i].otherCycle->Speed() * 20)
00827                        )
00828                     {
00829                         if (sides[i].distance < distance)
00830                             distance = sides[i].distance;
00831 
00832                         hit = true;
00833                     }
00834                 }
00835 
00836 #ifdef DEBUG_X
00837                 {
00838                     gRandomController noRandom;
00839                     gSensor ahead(cycle, start, dir);
00840                     ahead.detect(frontScan);
00841 
00842                     gSensor side(cycle, start, lrDir);
00843                     side.detect(sideScan*1.01f);
00844                     REAL thisSideScan = side.hit*.99f;
00845 
00846                     gSensor tunnel(cycle, lookTunnel, lrDir);
00847                     tunnel.detect(thisSideScan);
00848                     //    sides[i].AddHit(start, dir, tunnel, winding + lr);
00849 
00850                     gSensor parallel(cycle, start + lrDir * thisSideScan, dir);
00851                     parallel.detect(corridorScan);
00852                     // sides[i].AddHit(start, dir, parallel, winding);
00853                 }
00854 #endif
00855 
00856             }
00857         }
00858         if (sides[1].otherCycle == sides[0].otherCycle && sides[0].otherCycle)
00859             hit = true;
00860 
00861 #ifdef DEBUGLINE
00862         if (Hit())
00863         {
00864             eDebugLine::SetColor  (1, 1, 1);
00865             eDebugLine::SetTimeout(1);
00866             eDebugLine::Draw(start, .5, start + dir*distance, .5);
00867 
00868             eDebugLine::SetColor  (1, .5, 1);
00869             eDebugLine::Draw(start + dir*distance, .5, start + dir*distance, 1.5);
00870         }
00871 #endif
00872 
00873         eDebugLine::SetTimeout(0);
00874 
00875     }
00876 
00877 };
00878 
00879 
00880 
00881 
00882 
00883 
00884 
00885 
00886 gCycleMemoryEntry* gCycleMemory::Latest (int side)  const
00887 {
00888     side = (side > 0 ? 1 : 0);
00889     gCycleMemoryEntry* ret = NULL;
00890     for (int i=memory.Len()-1; i>=0; i--)
00891     {
00892         gCycleMemoryEntry* m = memory(i);
00893         if ((!ret || (m->max[side].dist > ret->max[side].dist)
00894                 && bool( m->cycle ) && m->cycle->Alive() ))
00895             ret = memory(i);
00896     }
00897 
00898     return ret;
00899 }
00900 
00901 gCycleMemoryEntry* gCycleMemory::Earliest (int side)  const
00902 {
00903     side = (side > 0 ? 1 : 0);
00904     gCycleMemoryEntry* ret = NULL;
00905     for (int i=memory.Len()-1; i>=0; i--)
00906     {
00907         gCycleMemoryEntry* m = memory(i);
00908         if ((!ret || (m->min[side].dist < ret->min[side].dist)
00909                 && bool( m->cycle ) && m->cycle->Alive()))
00910             ret = memory(i);
00911     }
00912     return ret;
00913 }
00914 
00915 
00916 gCycleMemoryEntry* gCycleMemory::Remember(const gCycle *cycle)
00917 {
00918     for (int i=memory.Len()-1; i>=0; i--)
00919         if (memory(i)->cycle == cycle)
00920             return memory(i);
00921 
00922     return tNEW(gCycleMemoryEntry)(this, cycle);
00923 }
00924 
00925 gCycleMemory::gCycleMemory()
00926 {
00927 }
00928 
00929 
00930 gCycleMemory::~gCycleMemory()
00931 {
00932     Clear();
00933 }
00934 
00935 void gCycleMemory::Clear()
00936 {
00937     for (int i = memory.Len()-1; i>=0; i--)
00938         delete memory(i);
00939 }
00940 
00941 gCycleMemoryEntry* gCycleMemory::operator()(int i) const
00942 {
00943     tASSERT(0 <= i && i < Len());
00944     return memory(i);
00945 }
00946 
00947 
00948 // deeper analysis functions:
00949 
00950 // helper function for gAIPlayer::CycleBlocksWay()
00951 static void CycleBlocksWayHelper(const gCycle *a, const gCycle *b,
00952                                  int aDir, int bDir, REAL aDist, REAL bDist, int winding)
00953 {
00954     gCycleMemoryEntry* aEntry = (const_cast<gCycleMemory&>(a->memory)).Remember(b);
00955 
00956     if (aDist > aEntry->max[aDir].dist)
00957     {
00958         aEntry->max[aDir].dist      = aDist;
00959         aEntry->max[aDir].otherSide = bDir;
00960         aEntry->max[aDir].otherDist = bDist;
00961         aEntry->max[aDir].winding   = winding;
00962     }
00963 
00964     if (aDist < aEntry->min[aDir].dist)
00965     {
00966         aEntry->min[aDir].dist      = aDist;
00967         aEntry->min[aDir].otherSide = bDir;
00968         aEntry->min[aDir].otherDist = bDist;
00969         aEntry->min[aDir].winding   = winding;
00970     }
00971 }
00972 
00973 
00974 
00975 
00976 // called whenever cylce a drives close to the wall of cylce b.
00977 // directions: aDir tells whether the wall is to the left (-1) or right(1)
00978 // of a
00979 // bDir tells the direction the wall of b is going (-1: to the left, 1:...)
00980 // bDist is the distance of b's wall to its start.
00981 void gAIPlayer::CycleBlocksWay(const gCycleMovement *aa, const gCycleMovement *bb,
00982                                int aDir, int bDir, REAL bDist, int winding)
00983 {
00984 
00985 
00986     tASSERT(aa && bb);
00987     gCycle * a = dynamic_cast< gCycle * >( const_cast< gCycleMovement * > ( aa ) );
00988     gCycle * b = dynamic_cast< gCycle * >( const_cast< gCycleMovement * > ( bb ) );
00989 
00990     REAL aDist = a->GetDistance();
00991     aDir = (aDir > 0 ? 1 : 0);
00992     bDir = (bDir > 0 ? 1 : 0);
00993 
00994     int w = winding + aDir * 2 + bDir * 2;
00995     while (w < 0)
00996         w+=400;
00997     if (w % 4 != 2)
00998         return;
00999 
01000     CycleBlocksWayHelper(a,b,aDir,bDir,aDist,bDist,  winding);
01001     CycleBlocksWayHelper(b,a,bDir,aDir,bDist,aDist, -winding);
01002 
01003     // what to do if cylce a tries to trace cylce b?
01004     if (a->Team() != b->Team())
01005     {
01006         gAIPlayer* ai = dynamic_cast<gAIPlayer*>(b->Player());
01007         if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 5)
01008             if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 &&
01009                     ai->lastChangeAttempt < se_GameTime() - 5 )
01010             {
01011                 REAL behind = b->GetDistance() - bDist;
01012                 if (a->Speed() > b->Speed() * 1.2f && behind < (a->Speed() - b->Speed()) * 10)
01013                 { // a is faster. Try to escape.
01014                     ai->SetTraceSide(aDir > 0 ? 1 : -1);
01015                     ai->SwitchToState(AI_TRACE, 10);
01016                     ai->target = const_cast< gCycle * >( a );
01017                 }
01018                 else// if (a->Speed() < b->Speed() * 1.1f)
01019                 { // b is faster. Attack.
01020                     ai->SetTraceSide(aDir > 0 ? -1 : 1);
01021                     ai->SwitchToState(AI_TRACE, 10 + behind / ( a->Speed() + b->Speed() ) );
01022                     ai->target = const_cast< gCycle * >( a );
01023                 }
01024             }
01025 
01026         // what to do if the AI player traces his opponent by accident? Trace On!
01027         ai = dynamic_cast<gAIPlayer*>(a->Player());
01028         if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 0)
01029             if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 &&
01030                     ai->lastChangeAttempt < se_GameTime() - 5 )
01031             {
01032                 REAL behind = b->GetDistance() - bDist;
01033                 ai->SetTraceSide(aDir > 0 ? 1 : -1);
01034                 ai->SwitchToState(AI_TRACE, 10 + 4 * behind / a->Speed());
01035                 ai->target = const_cast< gCycle * >( b );
01036             }
01037     }
01038 }
01039 
01040 // called whenever a cylce blocks the rim wall.
01041 void gAIPlayer::CycleBlocksRim(const gCycleMovement *a, int aDir)
01042 {
01043 }
01044 
01045 // called whenever a hole is ripped in a's wall at distance aDist.
01046 void gAIPlayer::BreakWall(const gCycleMovement *a, REAL aDist)
01047 {}
01048 
01049 
01050 
01051 
01052 
01053 
01054 
01055 
01056 //#define DEBUG
01057 
01058 // which eWall is detected?
01059 
01060 #define MAXAI_COLOR 13
01061 
01062 static int current_ai;
01063 static REAL rgb_ai[MAXAI_COLOR][3]={
01064                                        {1,.2,.2},
01065                                        {.2,1,.2},
01066                                        {.2,.2,1},
01067                                        {1,1,.2},
01068                                        {1,.2,1},
01069                                        {.2,1,1},
01070                                        {1,.6,.2},
01071                                        {1,.2,.6},
01072                                        {.6,.2,1},
01073                                        {.2,.6,1},
01074                                        {1,1,1},
01075                                        {.2,.2,.2},
01076                                        {.5,.5,.5}
01077                                    };
01078 
01079 static nNOInitialisator<gAIPlayer> gAIPlayer_init(330,"gAIPlayer");
01080 
01081 nDescriptor &gAIPlayer::CreatorDescriptor() const{
01082     return gAIPlayer_init;
01083 }
01084 
01085 gAIPlayer::gAIPlayer(nMessage &m) :
01086         ePlayerNetID(m),
01087         character(NULL),
01088         //      target(NULL),
01089         lastPath(se_GameTime()-100),
01090         lastTime(se_GameTime()),
01091         nextTime(0),
01092         concentration(1),
01093         log(NULL)
01094 {
01095 }
01096 
01097 
01098 gAIPlayer::gAIPlayer():
01099         simpleAI_(NULL),
01100         character(NULL),
01101         //      target(NULL),
01102         lastPath(se_GameTime()-100),
01103         lastTime(se_GameTime()),
01104         nextTime(0),
01105         concentration(1),
01106         log(NULL)
01107 {
01108     character = NULL;
01109     ClearTarget();
01110     traceSide = 1;
01111     freeSide  = 0;
01112     log       = NULL;
01113 
01114     // find a good color
01115 
01116     current_ai=(current_ai+1) % MAXAI_COLOR;
01117     int take_ai=current_ai;
01118     int try_ai=current_ai;
01119 
01120     REAL maxmindist=-10000;
01121 
01122     for(int i=MAXAI_COLOR-1;i>=0;i--){
01123         REAL mindist=4;
01124         REAL score=0;
01125         for (int j=se_PlayerNetIDs.Len()-1;j>=-1;j--){
01126             REAL R, G, B; // the color we want to avoid
01127 
01128             if (j >= 0)
01129             {
01130                 ePlayerNetID *p=se_PlayerNetIDs(j);
01131                 if (p && p != this)
01132                 {
01133                     R = p->r/15.0;
01134                     G = p->g/15.0;
01135                     B = p->b/15.0;
01136                 }
01137                 else
01138                     continue;
01139             }
01140             else // last case: j = -1. Test against floor color
01141             {
01142                 se_FloorColor(R, G, B);
01143             }
01144             REAL dist=
01145                 fabs(R - rgb_ai[try_ai][0])+
01146                 fabs(G - rgb_ai[try_ai][1])+
01147                 fabs(B - rgb_ai[try_ai][2]);
01148 
01149             score+=exp(-dist*dist*4);
01150 
01151             if (dist<mindist){
01152                 mindist=dist;
01153                 /*         con << c->r << ":" << rgb_ai[try_ai][0] << '\t'
01154                            << c->g << ":" << rgb_ai[try_ai][1] << '\t'
01155                            << c->b << ":" << rgb_ai[try_ai][2] << '\t' << dist << '\n'; */
01156             }
01157         }
01158         //con << "md=" << mindist << "\n\n";
01159         if (mindist>2)
01160             mindist=2;
01161 
01162         mindist=-score;
01163 
01164         if (mindist>maxmindist){
01165             maxmindist=mindist;
01166             take_ai=try_ai;
01167         }
01168 
01169 
01170 
01171         try_ai = ((try_ai+1) % MAXAI_COLOR);
01172     }
01173 
01174 
01175     r = static_cast<int>(rgb_ai[take_ai][0] * 15);
01176     g = static_cast<int>(rgb_ai[take_ai][1] * 15);
01177     b = static_cast<int>(rgb_ai[take_ai][2] * 15);
01178 
01179 
01180     ping = 0;
01181     pingCharity = 300;
01182 
01183     NewObject();
01184 }
01185 
01186 void gAIPlayer::ConfigureAIs()  // ai configuration menu
01187 {
01188 
01189 }
01190 
01191 
01192 // make sure this many AI players are in the game
01193 void gAIPlayer::SetNumberOfAIs(int num, int minPlayers, int iq, int tries)
01194 {
01195     // balance the human teams with AI players
01196     gAITeam::BalanceWithAIs();
01197 
01198     // remove AI players that got kicked out of their team
01199     for (int i = se_PlayerNetIDs.Len()-1; i>=0; i--)
01200     {
01201         // count the active AIs
01202         ePlayerNetID *p = se_PlayerNetIDs(i);
01203         gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
01204         if ( ai && !bool( ai->NextTeam() ) )
01205         {
01206             ai->RemoveFromGame();
01207 
01208 #ifdef DEBUG
01209             if ( ai->GetRefcount() > 1 )
01210             {
01211                 st_Breakpoint();
01212             }
01213 #endif
01214 
01215             sg_AIReferences.Remove( ai );
01216         }
01217     }
01218 
01219     int count = 0;
01220 
01221     bool iqperfect = false;
01222 
01223     // repeat until we run out of tries or the total amount of AIs is correct
01224     do
01225     {
01226         count = 0;
01227 
01228         int i;
01229         gAIPlayer* worstIQ = NULL; // worst fitting AI player that is in the game
01230 
01231         //              static tArray<bool> inGame(gAICharacter::s_Characters.Len());
01232         //              for (i = gAICharacter::s_Characters.Len()-1; i>=0; i--)
01233         //                      inGame(i) = false;
01234 
01235         for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
01236         {
01237             // count the active AIs
01238             ePlayerNetID *p = se_PlayerNetIDs(i);
01239             gAIPlayer  *ai  = dynamic_cast<gAIPlayer*>(p);
01240             if (ai && ai->NextTeam() == sg_AITeam )
01241             {
01242                 //                              int index = ((int)ai->character - (int)&gAICharacter::s_Characters(0))/sizeof(gAICharacter);
01243                 //                              inGame(index) = true;
01244 
01245                 if (!worstIQ || !worstIQ->character || fabs(worstIQ->character->iq - iq) < fabs(ai->character->iq - iq) )
01246                     worstIQ = ai;
01247 
01248                 count++;
01249             }
01250         }
01251 
01252         gAICharacter* bestIQ = BestIQ( iq );
01253 
01254         // count = numberOfAIs to add / remove(numberofAIs - requestedNumAIs)
01255         count -= num;
01256 
01257         // pcount = numberPlayers above minPlayers
01258         int pcount = - minPlayers;
01259         // check if more AIs are required (because there are less than minPlayers players)
01260         for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
01261         {
01262             ePlayerNetID *p = se_PlayerNetIDs(i);
01263             if ( !p->IsSpectating() )
01264                 ++pcount;
01265         }
01266 
01267         if (pcount < count)
01268             count = pcount;
01269 
01270         iqperfect = true;
01271         if (bestIQ && worstIQ && worstIQ->character )
01272             iqperfect = (fabs(bestIQ->iq - iq) > fabs(worstIQ->character->iq - iq) * .9f);
01273 
01274 
01275         // count complete. Do something!
01276         if (worstIQ && (count > 0 || (count >= 0 && !iqperfect)))
01277         {
01278             // too many AIs. Delete the one with the least fitting intelligence.
01279             worstIQ->RemoveFromGame();
01280             //                  worstIQ->ReleaseOwnership();
01281 
01282 #ifdef DEBUG
01283             if ( worstIQ->GetRefcount() > 1 )
01284             {
01285                 st_Breakpoint();
01286             }
01287 #endif
01288 
01289             sg_AIReferences.Remove( worstIQ );
01290             count--;
01291 
01292             // remove empty AI team
01293             if ( 0 == AITeam()->NumPlayers() )
01294             {
01295                 ClearAITeam();
01296             }
01297         }
01298         if (bestIQ && count < 0)
01299         {
01300             // too litte AIs. Create one.
01301             gAIPlayer *ai = tNEW(gAIPlayer)();
01302             ai->SetName( bestIQ->name );
01303             ai->character = bestIQ;
01304 
01305             sg_AIReferences.Add( ai );
01306 
01307             ai->SetTeam ( AITeam() );
01308             ai->UpdateTeam();
01309 
01310             /*
01311             {
01312                 tOutput mess;
01313                 tColoredString printname;
01314                 printname << *(ePlayerNetID*)ai << tColoredString::ColorString(.5,1,.5);
01315 
01316                 mess.SetTemplateParameter(1, printname);
01317                 mess << "$player_entered_game";
01318 
01319                 sn_ConsoleOut( mess );
01320             }
01321             */
01322 
01323             count++;
01324         }
01325 
01326     }
01327     while ((count != 0 ||
01328             !iqperfect) &&
01329             tries-- != 0);
01330 
01331 }
01332 
01333 
01334 
01335 // Possible state changes:
01336 // Every state -> Survive for 20 seconds if the victim is dead or can be assumed dead soon, or if the situation gets too dangerous
01337 
01338 // Survive -> CloseCombat if survival gets too boring
01339 
01340 // Trace -> Closecombat
01341 // Path  -> Closecombat  if the victim gets in view
01342 
01343 // Survive -> Trace if an enemy wall is hit
01344 // Path    -> Trace
01345 
01346 // CloseCombat -> Path if the vicim gets out of view
01347 
01348 
01349 void gAIPlayer::SetTraceSide(int side)
01350 {
01351     REAL time = se_GameTime();
01352     REAL ts   = time - lastChangeAttempt + 1;
01353     lastChangeAttempt = time;
01354 
01355     lazySideChange += ts * side;
01356     if (lazySideChange * traceSide <= 0)
01357     {
01358         // state change!
01359         traceSide = lazySideChange > 0 ? 1 : -1;
01360         lazySideChange = 10 * traceSide;
01361     }
01362 
01363     if (lazySideChange > 10)
01364         lazySideChange = 10;
01365     if (lazySideChange < -10)
01366         lazySideChange = -10;
01367 }
01368 
01369 // state change:
01370 void gAIPlayer::SwitchToState(gAI_STATE nextState, REAL minTime)
01371 {
01372     int thisAbility = 10 - character->properties[AI_STATE_TRACE];
01373     switch (state)
01374     {
01375     case AI_TRACE:
01376         thisAbility = character->properties[AI_STATE_TRACE];
01377         break;
01378     case AI_CLOSECOMBAT:
01379         thisAbility = character->properties[AI_STATE_CLOSECOMBAT];
01380         break;
01381     case AI_PATH:
01382         thisAbility = character->properties[AI_STATE_PATH];
01383         break;
01384     case AI_SURVIVE:
01385         break;
01386     };
01387 
01388     int nextAbility = 10;
01389     switch (nextState)
01390     {
01391     case AI_TRACE:
01392         nextAbility = character->properties[AI_STATE_TRACE];
01393         break;
01394     case AI_CLOSECOMBAT:
01395         nextAbility = character->properties[AI_STATE_CLOSECOMBAT];
01396         break;
01397     case AI_PATH:
01398         nextAbility = character->properties[AI_STATE_PATH];
01399         break;
01400     case AI_SURVIVE:
01401         break;
01402     };
01403 
01404 
01405     if (nextAbility > thisAbility && Random() * 10 > nextAbility)
01406         return;
01407 
01408 #ifdef DEBUG
01409     if (state != nextState)
01410         con << "Switching to state " << nextState << "\n";
01411 #endif
01412 
01413     state           = nextState;
01414     nextStateChange = se_GameTime() + minTime;
01415 }
01416 
01417 // state update functions:
01418 void gAIPlayer::ThinkSurvive(  ThinkData & data )
01419 {
01420     if (!character)
01421     {
01422         st_Breakpoint();
01423         return;
01424     }
01425 
01426     REAL random = 0;
01427     // do nothing much. Rely on the emergency program.
01428     /*
01429       random=10*(Random()/float(1));
01430       if (random < .2)
01431       EmergencySurvive(front, left, right, -1, 1);
01432       else if (random > 9.8)
01433       EmergencySurvive(front, left, right, -1, -1);
01434       else
01435       if (front.front.wallType == gSENSOR_RIM && front.distance < 10)
01436       st_Breakpoint();
01437 
01438 
01439     */
01440 
01441     if (data.left.front.wallType == gSENSOR_RIM)
01442         EmergencySurvive( data, 1);
01443     else if (data.right.front.wallType == gSENSOR_RIM)
01444         EmergencySurvive( data, -1);
01445     else
01446         EmergencySurvive( data );
01447 
01448 
01449 
01450     if (nextStateChange > se_GameTime())
01451     {
01452         data.thinkAgain = .5f;
01453         return;
01454     }
01455 
01456     // switch from Survival to close combat if surviving is too boring
01457     random=10*Random();
01458     if (random < 5)
01459     {
01460         // find a new victim:
01461         eCoord enemypos=eCoord(1000,100);
01462 
01463         const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
01464         gCycle *secondbest = NULL;
01465 
01466         // find the closest enemy
01467         for (int i=gameObjects.Len()-1;i>=0;i--){
01468             gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
01469 
01470             if (other && other->Team()!=Object()->Team() &&
01471                     !IsTrapped(other, Object())){
01472                 // then, enemy is realy an enemy
01473                 eCoord otherpos=other->Position()-Object()->Position();
01474                 if (otherpos.NormSquared()<enemypos.NormSquared()){
01475                     // check if the path is clear
01476                     gSensor p(Object(),Object()->Position(),otherpos);
01477                     p.detect(REAL(.98));
01478                     secondbest = dynamic_cast<gCycle *>(other);
01479                     if (p.hit>=.98){
01480                         enemypos = otherpos;
01481                         target = secondbest;
01482                     }
01483                 }
01484             }
01485         }
01486 
01487         if (!target)
01488             target = secondbest;
01489 
01490         if (target)
01491             SwitchToState(AI_CLOSECOMBAT, 1);
01492     }
01493 
01494     data.thinkAgain = 1;
01495 }
01496 
01497 void gAIPlayer::ThinkTrace( ThinkData & data )
01498 {
01499     gAISensor const & front = data.front;
01500     gAISensor const & left = data.left;
01501     gAISensor const & right = data.right;
01502 
01503     bool inverse = front.Hit() && front.distance < Object()->Speed() * Delay();
01504 
01505     if (left.front.wallType == gSENSOR_RIM)
01506         SetTraceSide(1);
01507 
01508     if (right.front.wallType == gSENSOR_RIM)
01509         SetTraceSide(-1);
01510 
01511     bool success = EmergencySurvive(data, 0, traceSide * ( inverse ? -1 : 1));
01512 
01513     REAL & nextTurn = data.thinkAgain;
01514     nextTurn = 100;
01515     if (left.front.edge)
01516     {
01517         REAL a = eCoord::F(Object()->Direction(), *left.front.edge->Point() - Object()->Position());
01518         REAL b = eCoord::F(Object()->Direction(), *left.front.edge->Other()->Point() - Object()->Position());
01519 
01520         if (a < b)
01521             a = b;
01522         if ( a > 0 )
01523             nextTurn = a;
01524     }
01525 
01526     if (right.front.edge)
01527     {
01528         REAL a = eCoord::F(Object()->Direction(), *right.front.edge->Point() - Object()->Position());
01529         REAL b = eCoord::F(Object()->Direction(), *right.front.edge->Other()->Point() - Object()->Position());
01530 
01531         if (a < b)
01532             a = b;
01533         if ( a > 0 && a < nextTurn || !left.front.edge)
01534             nextTurn = a;
01535     }
01536 
01537     nextTurn/= Object()->Speed() * .98f;
01538 
01539     REAL delay = Delay() * 1.5f;
01540     if ((!Object()->CanMakeTurn(1) || !Object()->CanMakeTurn(-1) || success) && nextTurn > delay)
01541         nextTurn = delay;
01542 
01543     if (nextTurn > .3f)
01544         nextTurn = .3f;
01545 
01546     if (nextStateChange > se_GameTime())
01547         return;
01548 
01549     // find a new victim:
01550     eCoord enemypos=eCoord(1000,100);
01551 
01552     const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
01553     gCycle *secondbest = NULL;
01554 
01555     // find the closest enemy
01556     for (int i=gameObjects.Len()-1;i>=0;i--){
01557         gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
01558 
01559         if (other && other->Team()!=Object()->Team() &&
01560                 !IsTrapped(other, Object())){
01561             // then, enemy is realy an enemy
01562             eCoord otherpos=other->Position()-Object()->Position();
01563             if (otherpos.NormSquared()<enemypos.NormSquared()){
01564                 // check if the path is clear
01565                 gSensor p(Object(),Object()->Position(),otherpos);
01566                 p.detect(REAL(.98));
01567                 secondbest = dynamic_cast<gCycle *>(other);
01568 
01569                 if (!target)
01570                     enemypos = otherpos;
01571 
01572                 if (p.hit>=.98){
01573                     enemypos = otherpos;
01574                     target = secondbest;
01575                 }
01576             }
01577         }
01578     }
01579 
01580     eCoord relpos=enemypos.Turn(Object()->Direction().Conj()).Turn(0,1);
01581 
01582 
01583     if (!target)
01584         target = secondbest;
01585     else
01586         SwitchToState(AI_CLOSECOMBAT, 1);
01587 
01588     if (target)
01589         SetTraceSide((relpos.x  > 0 ? 10 : -10) *
01590                      (target->Speed() > Object()->Speed() ? -1 : 1));
01591 
01592     nextStateChange = se_GameTime() + 10;
01593 
01594     //  SwitchToState(AI_SURVIVE, 1);
01595     return;
01596 }
01597 
01598 
01599 void gAIPlayer::ThinkPath( ThinkData & data )
01600 {
01601     int lr = 0;
01602     REAL mindist = 10;
01603 
01604     eCoord dir = Object()->Direction();
01605     // REAL fs=front.distance;
01606     REAL ls=data.left.distance;
01607     REAL rs=data.right.distance;
01608 
01609 
01610     if (!target->CurrentFace() || IsTrapped(target, Object()))
01611     {
01612         SwitchToState(AI_SURVIVE, 1);
01613         EmergencySurvive( data );
01614 
01615         data.thinkAgain = 4;
01616         return;
01617     }
01618 
01619     eCoord tDir = target->Position() - Object()->Position();
01620 
01621     if ( nextStateChange < se_GameTime() )
01622     {
01623         gSensor p(Object(),Object()->Position(), tDir);
01624         p.detect(REAL(.9999999));
01625         if (p.hit >=  .9999999)  // free line of sight to victim. Switch to close combat.
01626         {
01627             SwitchToState(AI_CLOSECOMBAT, 5);
01628             EmergencySurvive( data );
01629 
01630             return;
01631         }
01632     }
01633 
01634 
01635 
01636     // find a new path if the one we got is outdated:
01637     if (lastPath < se_GameTime() - 10)
01638         if (target->CurrentFace())
01639         {
01640             Object()->FindCurrentFace();
01641             eHalfEdge::FindPath(Object()->Position(), Object()->CurrentFace(),
01642                                 target->Position(), target->CurrentFace(),
01643                                 Object(),
01644                                 path);
01645             lastPath = se_GameTime();
01646         }
01647 
01648     if (!path.Valid())
01649     {
01650         data.thinkAgain = 1;
01651         return;
01652     }
01653 
01654     // find the most advanced path point that is in our viewing range:
01655 
01656     for (int z = 10; z>=0; z--)
01657         path.Proceed();
01658 
01659     bool goon   = path.Proceed();
01660     bool nogood = false;
01661 
01662     do
01663     {
01664         if (goon)
01665             goon = path.GoBack();
01666         else
01667             goon = true;
01668 
01669         eCoord pos   = path.CurrentPosition() + path.CurrentOffset() * 0.1f;
01670         eCoord opos  = Object()->Position();
01671         eCoord odir  = pos - opos;
01672 
01673         eCoord intermediate = opos + dir * eCoord::F(odir, dir);
01674 
01675         gSensor p(Object(), opos, intermediate - opos);
01676         p.detect(1.1f);
01677         nogood = (p.hit <= .999999999 || eCoord::F(path.CurrentOffset(), odir) < 0);
01678 
01679         if (!nogood)
01680         {
01681             gSensor p(Object(), intermediate, pos - intermediate);
01682             p.detect(1);
01683             nogood = (p.hit <= .99999999 || eCoord::F(path.CurrentOffset(), odir) < 0);
01684         }
01685 
01686     }
01687     while (goon && nogood);
01688 
01689     if (goon)
01690     {
01691         // now we have found our next goal. Try to get there.
01692         eCoord pos    = Object()->Position();
01693         eCoord target = path.CurrentPosition();
01694 
01695         // look how far ahead the target is:
01696         REAL ahead = eCoord::F(target - pos, dir)
01697                      + eCoord::F(path.CurrentOffset(), dir);
01698 
01699         if ( ahead > 0)
01700         {         // it is still before us. just wait a while.
01701             mindist = ahead;
01702         }
01703         else
01704         { // we have passed it. Make a turn towards it.
01705             REAL side = (target - pos) * dir;
01706 
01707             if ( !((side > 0 && ls < 3) || (side < 0 && rs < 3))
01708                     && (fabs(side) > 3 || ahead < -10) )
01709             {
01710 #ifdef DEBUG
01711                 con << "Following path...\n";
01712 #endif
01713                 lr += (side > 0 ? 1 : -1);
01714             }
01715         }
01716     }
01717     else // nogood
01718     {
01719         lastPath -= 1;
01720         SwitchToState(AI_SURVIVE);
01721     }
01722 
01723     EmergencySurvive( data, 1, -lr );
01724 
01725     REAL d = sqrt(tDir.NormSquared()) * .2f;
01726     if (d < mindist)
01727         mindist = d;
01728 
01729     data.thinkAgain = mindist / Object()->Speed();
01730     if (data.thinkAgain > .4)
01731         data.thinkAgain *= .7;
01732 }
01733 
01734 
01735 void gAIPlayer::ThinkCloseCombat( ThinkData & data )
01736 {
01737     int lr=0;
01738 
01739     REAL nextThought = 0;
01740 
01741     const gAISensor* sides[2];
01742     sides[0] = &data.left;
01743     sides[1] = &data.right;
01744 
01745     eCoord dir = Object()->Direction();
01746     REAL fs=data.front.distance;
01747     //  REAL ls=left.hit;
01748     //  REAL rs=right.hit;
01749 
01750     if ( bool( target ) && !IsTrapped(target, Object()) && nextStateChange < se_GameTime() )
01751     {
01752         gSensor p(Object(),Object()->Position(),target->Position() - Object()->Position());
01753         p.detect(REAL(1));
01754         if (p.hit <=  .999999)  // no free line of sight to victim. Switch to path mode.
01755         {
01756             SwitchToState(AI_PATH, 5);
01757             EmergencySurvive( data );
01758             return;
01759         }
01760     }
01761 
01762     REAL ed = 0;
01763 
01764     const REAL fear=REAL(.01);
01765     const REAL caution=.001;
01766     const REAL evasive=100;
01767     const REAL attack=100;
01768     const REAL seek=REAL(1);
01769     const REAL trap=REAL(.01);
01770     const REAL ffar=20;
01771     //    const REAL close=1000;
01772 
01773     REAL random=10*Random()*Random();
01774 
01775     if ( bool( target ) && target->Alive()){
01776 
01777         eCoord enemypos=target->Position()-Object()->Position();
01778         eCoord enemydir=target->Direction();
01779         REAL enemyspeed=target->Speed();
01780 
01781         ed=REAL(fabs(enemypos.x)+fabs(enemypos.y));
01782         ed/=enemyspeed;
01783 
01784         // transform coordinates relative to us:
01785         enemypos=enemypos.Turn(dir.Conj()).Turn(0,1);
01786         enemydir=enemydir.Turn(dir.Conj()).Turn(0,1);
01787 
01788         // now we are at the center of the coordinate system facing
01789         // in direction (0,1).
01790 
01791         // rules are symmetrical: exploit that.
01792         int side=1;
01793         if (enemypos.x<0){
01794             side*=-1;
01795             enemypos.x*=-1;
01796             enemydir.x*=-1;
01797 
01798             sides[1] = &data.left;
01799             sides[0] = &data.right;
01800             //      Swap(ls,rs);
01801         }
01802         // now we can even assume the enemy is on our right side.
01803 
01804         // consider his ping and our reaction time
01805 #define REACTION .2
01806 
01807 
01808         //REAL enemyspeed=target->speed;
01809         REAL ourspeed=Object()->Speed();
01810 
01811         REAL enemydist=target->Lag()*enemyspeed;
01812 
01813         // redo the prediction
01814 #ifndef DEDICATED
01815         if (sn_GetNetState()==nCLIENT && !sr_predictObjects)
01816 #endif
01817             enemypos=enemypos-enemydir*enemydist;
01818         enemydist+=2*REACTION *enemyspeed;
01819 
01820         REAL ourdist=REACTION*ourspeed;;
01821 
01822 
01823         // now we consider the worst case: we drive straight on,
01824         enemypos.y-=ourdist;
01825         // while the enemy cuts us: he goes in front of us
01826         REAL forward=-enemypos.y+.01;
01827         if (forward<0) // no need to go to much ahead
01828             forward=0;
01829         if (forward>enemydist)
01830             forward=enemydist;
01831 
01832         enemypos.y+=forward;
01833         enemydist-=forward;
01834 
01835         // and then he turns left.
01836         enemypos.x-=enemydist;
01837 
01838         if (enemypos.y*enemyspeed>enemypos.x*ourspeed){ // he is right ahead of us.
01839             if (random<fear){ // evade him
01840 #ifdef DEBUG
01841                 con << "fear!\n";
01842 #endif
01843                 lr+=side;
01844 
01845                 nextThought += 1;
01846             }
01847             if (enemypos.y<=ffar &&
01848                     ((enemydir.x<0 && random<evasive) ||
01849                      (enemydir.y>0 && random<caution) ||
01850                      (enemydir.y<0 && random<attack))){
01851 #ifdef DEBUG
01852                 con << "caution!\n";
01853 #endif
01854                 lr+=side;
01855 
01856                 nextThought += 1;
01857 
01858                 if (enemyspeed > ourspeed)
01859                 {
01860                     SetTraceSide(-side);
01861                     SwitchToState(AI_TRACE, 10);
01862                 }
01863             }
01864         }
01865         else if (enemypos.y*ourspeed<-enemypos.x*enemyspeed){
01866             /*
01867               // good attack position
01868               if (enemypos.x<rs && rs < range*.99){
01869               #ifdef DEBUG
01870               con << "BOX!\n";
01871               #endif
01872               turn+=10;
01873               lr-=side;
01874               }
01875 
01876             */
01877 
01878             REAL canCutIfDriveOn = enemypos.x*ourspeed - fs * (enemyspeed - ourspeed);
01879             canCutIfDriveOn -= enemypos.y*enemyspeed;
01880 
01881             REAL canCutIfAttack  = - sides[1]->distance * enemyspeed
01882                                    - (sides[1]->distance - enemypos.x -enemypos.y*ourspeed) * ourspeed;
01883 
01884             if (random<attack && (!(data.front.Hit() && data.front.distance < 20) || canCutIfAttack > canCutIfDriveOn)){
01885 #ifdef DEBUG
01886                 con << "attack!\n";
01887 #endif
01888                 lr-=side;
01889 
01890                 nextThought += 1;
01891             }
01892         }
01893         else if(enemypos.x>ffar*4){
01894             if(random<seek){
01895 #ifdef DEBUG
01896                 con << "seek!\n";
01897 #endif
01898                 lr-=side;
01899 
01900                 nextThought += 1;
01901             }
01902         }
01903         else if (enemypos.x<ffar*2 && fabs(enemypos.y)<ffar){
01904             if(random<trap){
01905 #ifdef DEBUG
01906                 con << "trap!\n";
01907 #endif
01908                 lr+=side;
01909 
01910                 nextThought += 1;
01911             }
01912         }
01913     }
01914 
01915     if (!EmergencySurvive(data, 1, -lr))
01916         nextThought = 0;
01917 
01918     data.thinkAgain = ed/2 + nextThought;
01919 }
01920 
01921 
01922 /*
01923 static void PretendFrontHit(const gAISensor& f, const gAISensor &corridor,
01924                             const eCoord& origin, const eCoord& direction)
01925 {
01926   gAISensor& front = (gAISensor&)(f);
01927 
01928   // transfer the easy data
01929   front.ehit = corridor.ehit;
01930   front.lr   = corridor.lr;
01931   front.type = corridor.type;
01932 
01933   REAL a = eCoord::F(*corridor.ehit->Point()          - origin, direction);
01934   REAL b = eCoord::F(*corridor.ehit->Other()->Point() - origin, direction);
01935 
01936   if (front.hit > a)
01937     front.hit = a;
01938   if (front.hit > b)
01939     front.hit = b;
01940 
01941   front.before_hit = origin + direction * front.hit * .99f;
01942 }
01943 */
01944 
01945 
01946 #define DANGERLEVELS 4
01947 #define LOOPLEVEL   0
01948 #define SPACELEVEL  1
01949 #define TRAPLEVEL   2
01950 #define COLIDELEVEL 2
01951 #define TEAMLEVEL   3
01952 
01953 
01954 class gAILogEntry{
01955 public:
01956     int sideDanger[DANGERLEVELS][2];
01957     int frontDanger[DANGERLEVELS];
01958     int turn;
01959     int tries;
01960     REAL time;
01961 };
01962 
01963 #define ENTRIES 10
01964 
01965 class gAILog{
01966 public:
01967     gAILogEntry entries[ENTRIES+1];
01968     int         current;
01969     int         del;
01970 
01971     gAILog():current(0), del(0){}
01972 
01973     void DeleteEntry()
01974     {
01975         del = 1;
01976         if (current > 0)
01977             current--;
01978     }
01979 
01980     gAILogEntry& NextEntry()
01981     {
01982         del = 0;
01983 
01984         if (current >= ENTRIES)
01985         {
01986             for (int i=1; i<ENTRIES; i++)
01987                 entries[i-1] = entries[i];
01988         }
01989         else
01990             current++;
01991 
01992         gAILogEntry& ret = entries[current-1];
01993         ret.time = se_GameTime();
01994         return ret;
01995     }
01996 
01997     void Print()
01998     {
01999 #ifdef DEBUG
02000         con << "Log:\n";
02001         for (int i = current + del - 1; i>=0; i--)
02002         {
02003             for (int j=0; j < DANGERLEVELS; j++)
02004             {
02005                 con << entries[i].sideDanger[j][0] << ' ';
02006                 con << entries[i].frontDanger[j]   << ' ';
02007                 con << entries[i].sideDanger[j][1] << "    ";
02008             }
02009             con << entries[i].turn << ", " << entries[i].tries << "\n";
02010         }
02011 #ifndef DEDICATED
02012         //              se_PauseGameTimer(true);
02013 #endif
02014 #endif
02015     }
02016 };
02017 
02018 // emergency functions:
02019 bool gAIPlayer::EmergencySurvive( ThinkData & data, int enemyevade, int preferedSide)
02020 {
02021     if (!character)
02022     {
02023         st_Breakpoint();
02024         return false;
02025     }
02026 
02027     gAISensor const & front = data.front;
02028     gAISensor const & left = data.left;
02029     gAISensor const & right = data.right;
02030 
02031     if (!log)
02032         log = tNEW(gAILog);
02033 
02034 #ifdef DEBUG
02035     static int last = 0;
02036     if (log->current >= 4
02037             && log->entries[log->current-2].time > se_GameTime() - .2
02038             && log->entries[log->current-1].turn * last <= 0
02039             && log->entries[log->current-1].turn * log->entries[log->current-2].turn < 0
02040             //      && log->entries[log->current-3].turn * log->entries[log->current-2].turn < 0
02041             //      && log->entries[log->current-3].turn * log->entries[log->current-4].turn < 0
02042        )
02043     {
02044         log->Print();
02045         //      st_Breakpoint();
02046     }
02047     if ( log->current > 0 )
02048         last = log->entries[log->current-1].turn;
02049 #endif
02050 
02051     triesLeft = (triesLeft * character->properties[AI_EMERGENCY])/10;
02052 
02053     freeSide *= .95;
02054 
02055     int i, j;
02056 
02057     // don't do a thing if there may be a better way out of we drive on:
02058     if (triesLeft > 0 &&
02059             front.front.otherCycle &&
02060             front.front.otherCycle != Object() &&
02061             ((front.frontLoop[1].loop && front.front.otherCycle != left .front.otherCycle && left .front.otherCycle)||
02062              (front.frontLoop[0].loop && front.front.otherCycle != right.front.otherCycle && right.front.otherCycle ) )
02063        )
02064         return false;
02065 
02066     // get the delay between two turns
02067     REAL delay = Delay();
02068     REAL range = Object()->Speed() * delay;
02069 
02070     // nothing we can do if we cannot make a turn immediately
02071     if (!Object()->CanMakeTurn(1) || !Object()->CanMakeTurn(-1))
02072         return false;
02073 
02074     //  bool dontCheckForLoop[2] = { false, false };
02075 
02076 
02077     // look out if there is anything bad going on in one of the directions:
02078     // [signifficance: danger level of n: You'll be (as good as) dead in [10/n delay times] if you drive that way
02079     int sideDanger[DANGERLEVELS][2];
02080     int frontDanger[DANGERLEVELS];
02081     for(i = DANGERLEVELS-1; i>=0; i--)
02082     {
02083         sideDanger[i][0] = 0;
02084         sideDanger[i][1] = 0;
02085         frontDanger[i]   = 0;
02086     }
02087 
02088     bool canTrapEnemy = false;
02089 
02090     if (emergency)
02091     {
02092         frontDanger[SPACELEVEL] += 40;
02093     }
02094 
02095     const gAISensor* sides[2];
02096     sides[0] = &left;
02097     sides[1] = &right;
02098 
02099     // avoid loops:
02100 
02101     bool isTrapped = IsTrapped(Object(), NULL);
02102 
02103     /*
02104       if (front.front.wallType == gSENSOR_ENEMY)
02105       sideDanger[LOOPLEVEL][(1-front.front.lr*enemyevade) >> 1] += 5;
02106     */  
02107 
02108     if (!isTrapped)
02109         for (i = 1; i>=0; i--)
02110         {
02111             if (front.frontLoop[i].loop && front.distance < 5*sides[i]->distance)
02112             {
02113                 // if we would close ourself in, make the danger bigger
02114                 if (front.front.otherCycle == Object() && i+i-1 == front.front.lr)
02115                     sideDanger[LOOPLEVEL][i]+=40;
02116 
02117                 sideDanger[LOOPLEVEL][i]+=40;
02118                 for (j = front.frontLoop[i].closedIn.Len()-1; j>=0; j--)
02119                     if (front.frontLoop[i].closedIn(j) == target)
02120                         canTrapEnemy = true;
02121             }
02122 
02123             for (j = 1; j>=0; j--)
02124                 if (front.sideLoop[i][j].loop)
02125                     sideDanger[LOOPLEVEL][j]++;
02126 
02127             // if we would close ourselfs in by a zigzag in direction i,
02128             // but not by a u-turn and there is enough space for a u-turn,
02129             // do it.
02130             if (sides[i]->frontLoop[1-i].loop  &&
02131                     !sides[i]->frontLoop[i].loop)
02132             {
02133                 if (sides[i]->distance > range)
02134                 {
02135                     frontDanger[LOOPLEVEL]     += 20;
02136                     sideDanger[LOOPLEVEL][1-i] += 10;
02137                 }
02138                 else // try to make some room so we can evade:
02139                 {
02140                     frontDanger[LOOPLEVEL]     += 20;
02141 
02142                     sideDanger[LOOPLEVEL][i]   += 10;
02143                 }
02144             }
02145 
02146             // if we would close ourselves in by a U-Turn, don't do it.
02147             //  if (sides[i]->frontLoop[i].loop && sides[i].distance < range * 2)
02148             //    sideDanger[LOOPLEVEL][i] += 40;
02149         }
02150 
02151     // try to trap the enemy
02152     if (character->properties[AI_LOOP] >= 10 && canTrapEnemy && !emergency)
02153         return false;
02154 
02155 
02156     /*
02157       // avoid closing yourself or a teammate in.
02158       if (front.type == gSENSOR_SELF || front.type == gSENSOR_TEAMMATE)
02159       {
02160          if (front.lr > 0)
02161       sideDanger[][1] +=2;
02162          else
02163       sideDanger[][0] +=2;
02164       }
02165     */
02166 
02167 
02168 
02169     {
02170         if (front.Hit() &&
02171                 ( front.distance + range < sides[0]->distance ||
02172                   front.distance + range < sides[1]->distance) )
02173         {
02174             if ( front.front.wallType == gSENSOR_RIM)
02175                 frontDanger[SPACELEVEL] += static_cast<int>(100 * range * gArena::SizeMultiplier() / (front.distance + range * .1));
02176 
02177             frontDanger[SPACELEVEL] += static_cast<int>(5 * range / (front.distance + range *.2));
02178 
02179             if (front.distance < range)
02180                 frontDanger[SPACELEVEL] += static_cast<int>(20 * range / (front.distance + range *.2)) + 1;
02181         }
02182 
02183 
02184         // avoid close corners:
02185         for (i = 1; i>=0; i--)
02186         {
02187             if (sides[i]->Hit() && //sides[i]->distance < range * 3 &&
02188                     sides[i]->distance < front.distance + range)
02189             {
02190                 if ( sides[i]->front.wallType == gSENSOR_RIM)
02191                     sideDanger[SPACELEVEL][i] += static_cast<int>(150 * range * gArena::SizeMultiplier() / (sides[i]->distance + range * .1));
02192 
02193                 sideDanger[SPACELEVEL][i] += static_cast<int>
02194                                              (range * 5 / (sides[i]->distance + range * .1));
02195 
02196                 if (sides[i]->distance < range)
02197                     sideDanger[SPACELEVEL][i] += static_cast<int>
02198                                                  (range * 20 / (sides[i]->distance + range * .1));
02199             }
02200 
02201             // give us a chance to turn around:
02202             if (frontDanger[SPACELEVEL] * 2 < sideDanger[SPACELEVEL][i])
02203                 sideDanger[LOOPLEVEL][i-i] -= sideDanger[SPACELEVEL][i] * 2;
02204         }
02205     }
02206 
02207     // avoid close proximity to other cycles
02208     const gCycle* target = NULL;
02209     const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
02210     gCycle *secondbest = NULL;
02211     REAL closest = 1000000;
02212     eCoord dir     = Object()->Direction();
02213 
02214     // find the closest enemy
02215     for (i=gameObjects.Len()-1;i>=0;i--){
02216         gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
02217 
02218         if (other && other->Alive() && other != Object())
02219         {
02220             eCoord otherpos=other->Position()-Object()->Position();
02221             REAL otherNorm = otherpos.NormSquared();
02222 
02223             bool nothit = false;
02224             if (otherNorm < closest * 4)
02225             {
02226                 gSensor p(other, other->Position(), -otherpos);
02227                 p.detect(REAL(.9999));
02228                 gSensor q(Object(), Object()->Position(), otherpos);
02229                 q.detect(REAL(.9999));
02230 
02231                 nothit = p.hit>=.999 && q.hit >=.999;
02232             }
02233 
02234             if (other->Team() != Object()->Team())
02235             {
02236                 // then, enemy is realy an enemy
02237                 //      REAL s = Object()->Speed() * 50;
02238                 if (/* otherNorm < s*s && */ otherNorm < closest)
02239                 {
02240                     // check if the path is clear
02241                     secondbest = dynamic_cast<gCycle *>(other);
02242                     if (nothit){
02243                         closest = otherNorm;
02244                         target = secondbest;
02245                     }
02246                 }
02247             }
02248             else if (nothit)
02249             {
02250                 // he is a teammate. Avoid him.
02251 
02252                 eCoord friendpos=other->Position() - Object()->Position();
02253 
02254                 // transform coordinates relative to us:
02255                 friendpos=friendpos.Turn(dir.Conj()).Turn(0,1);
02256 
02257                 if (friendpos.y > fabs(friendpos.x) * 1.5f)
02258                     frontDanger[TEAMLEVEL] += 10;
02259                 if (friendpos.x * 2 > -friendpos.y)
02260                     sideDanger[TEAMLEVEL][1] += 10;
02261                 else if (-friendpos.x * 2 > -friendpos.y)
02262                     sideDanger[TEAMLEVEL][0] += 10;
02263             }
02264         }
02265     }
02266 
02267     //  if (!target)
02268     //target = secondbest;
02269 
02270     if (target && character->properties[AI_ENEMY] > 0)
02271     {
02272         bool sdanger = false;
02273         for (i = DANGERLEVELS-1; i>=0; i--)
02274             sdanger |= sideDanger[i][0] > 4 || sideDanger[i][1] > 4;
02275 
02276         eCoord enemypos=target->Position() - Object()->Position();
02277         eCoord enemydir=target->Direction();
02278         REAL enemyspeed=target->Speed();
02279 
02280 
02281         // transform coordinates relative to us:
02282         enemypos=enemypos.Turn(dir.Conj()).Turn(0,1);
02283         enemydir=enemydir.Turn(dir.Conj()).Turn(0,1);
02284 
02285         if (character->properties[AI_ENEMY] > 7)
02286         {
02287             // would he be able to trap us if we drive straight on?
02288             bool trap[2] = {false, false};
02289 
02290             if (!isTrapped)
02291                 for (i = 1; i>=0; i--)
02292                 {
02293                     // if the enemy comes racing towards us, check if he could
02294                     // close us in by touching our own line ON THE OPPOSITE side of i
02295                     tArray<const gCycle*> closedIn;
02296                     int winding = 0;
02297 
02298                     bool loop = CheckLoop(target, Object(),
02299                                           Object()->GetDistance() + 4 * TOL, i, 0,
02300                                           closedIn, winding);
02301 
02302                     winding -= Object()->WindingNumber();
02303                     winding += target->WindingNumber();
02304 
02305 
02306                     // yes! we shoult turn in direction 1-i to get the target
02307                     // to the other side.
02308                     if (loop)
02309                         if (winding * (i+i-1) < 0)
02310                         {
02311                             trap[i] = true;
02312                             REAL x = enemypos.x * (i+i-1);
02313                             REAL y = enemypos.y;
02314 
02315                             bool canAccelerateByTurning =
02316                                 ( sides[1-i]->Hit() &&
02317                                   sides[1-i]->distance < Object()->Speed() * delay * 5 &&
02318                                   sides[i-i]->distance > Object()->Speed() * delay &&
02319                                   !sides[i-i]->frontLoop[i].loop) ;
02320 
02321                             bool ohShit = target->Speed() > Object()->Speed() + sqrt(closest);
02322 
02323                             if (ohShit)
02324                             {
02325                                 SetTraceSide(-(i+i-1));
02326                                 SwitchToState(AI_TRACE, 10);
02327                             }
02328 
02329                             bool turningIsFutile =
02330                                 front.front.otherCycle == Object() &&
02331                                 sides[1-i]->front.otherCycle == Object() &&
02332                                 front.distance < sides[1-1]->distance * 10 ;
02333 
02334                             if (
02335                                 x < 0 &&
02336                                 (
02337                                     x * Object()->Speed() < -y * target->Speed() + 1000 ||
02338                                     canAccelerateByTurning || ohShit
02339                                 )
02340                                 &&
02341                                 !turningIsFutile
02342                             )
02343                             {
02344                                 if (enemydir.y < -.2f && y < 0)
02345                                     SetTraceSide(-(i+i-1));
02346 
02347                                 frontDanger[TRAPLEVEL]    += 10;
02348                             }
02349 
02350                             if ( y > 0 || x < 0 || ohShit
02351                                     //                     ( y * Object()->Speed() > x * target->Speed()*.9 - 200 || enemyspeed.x * (i+i-1)
02352                                )
02353                                 sideDanger[TRAPLEVEL][i] += 20;
02354                             // sideDanger[TRAPLEVEL][i] ++;
02355                         }
02356                 }
02357         }
02358 
02359         if (character->properties[AI_ENEMY] > 0)
02360         {
02361             // imminent collision check
02362             REAL totalspeed = enemyspeed + Object()->Speed();
02363 
02364             if ((fabs(enemypos.y) < totalspeed * .3f && fabs(enemypos.x) < totalspeed * .3f))
02365             {
02366                 REAL diffSpeed  = -enemydir.y * enemyspeed + Object()->Speed();
02367                 if (diffSpeed > 0 && enemydir.y <= .2)
02368                 {
02369                     REAL enemyFront = enemypos.y / diffSpeed;
02370                     REAL enemySide  = fabs(enemypos.x) / diffSpeed;
02371                     if (enemyFront > 0 && enemyFront < .4 + enemySide && fabs(enemypos.y) > fabs(enemypos.x))
02372                     {
02373                         frontDanger[COLIDELEVEL] += 1 + int(4 / (enemyFront + .01));
02374                         //                    SwitchToState( AI_SURVIVE, enemyFront * 4 + 2 );
02375                     }
02376                 }
02377 
02378                 int side = enemypos.x > 0 ? 1 : 0;
02379 
02380                 // can we cut him instead of evade him?
02381                 if (Object()->Team() != target->Team() &&
02382                         ( ( enemydir.y <= -.2 &&
02383                             enemypos.y*target->Speed()*1.1 > fabs(enemypos.x) * Object()->Speed() ) ||
02384                           sideDanger[COLIDELEVEL][side] > 0))
02385                     sideDanger[COLIDELEVEL][1-side]+=5;
02386                 else if ( -(enemypos.y + .3f) * Object()->Speed() < fabs(enemypos.x) * target->Speed()*1.2)
02387                     sideDanger[COLIDELEVEL][side]+=10;
02388             }
02389         }
02390     }
02391 
02392     eDebugLine::SetTimeout(.5);
02393     eDebugLine::SetColor  (1, 0, 1);
02394     eCoord p = Object()->Position();
02395     eDebugLine::Draw(p, .5, p, 8.5);
02396     eDebugLine::SetTimeout(0);
02397 
02398 
02399 
02400     // determine the total danger levels by taking the max of the individual experts:
02401     int fDanger = 0;
02402     int sDanger[2] = { 0, 0 };
02403     for (i = 0; i<DANGERLEVELS; i++)
02404         // for (i = 1; i< 2; i++)
02405     {
02406         if (!fDanger || frontDanger[i] > fDanger + 2)
02407             fDanger = frontDanger[i];
02408 
02409         for (int j=1; j>=0; j--)
02410             if (!sDanger[j] || sideDanger[i][j] > sDanger[j] + 2)
02411                 sDanger[j] = sideDanger[i][j];
02412     }
02413 
02414     // nothing to do if we are not in immediate danger.
02415     if (!fDanger && !preferedSide)
02416         return false;
02417 
02418 
02419     // decide about your direction:
02420     int turn = 0;
02421 
02422     turn += sDanger[0];
02423     turn -= sDanger[1];
02424 
02425     if (!turn)
02426         turn = (int) freeSide;
02427 
02428     if (!turn && front.front.wallType != gSENSOR_RIM)
02429         turn = front.front.lr * enemyevade;
02430 
02431     if (!turn)
02432         turn = (sides[0]->distance > sides[1]->distance ? -1 : 1);
02433 
02434     if (!turn && log->current)
02435         turn = log->entries[log->current-1].turn;
02436 
02437 
02438 
02439     // switch to survival mode if we just trapped an enemy
02440     if (canTrapEnemy)
02441     {
02442 #ifdef DEBUG
02443         if ( !tRecorder::IsRunning() )
02444             Chat(tString( "Hehe! Got you!" ) );
02445 #endif
02446         SwitchToState(AI_TRACE, 10);
02447 
02448         if (turn)
02449             this->SetTraceSide(-turn);
02450     }
02451 
02452     gAILogEntry&e = log->NextEntry();
02453     e.turn = 0;
02454     e.tries = triesLeft;
02455     for (i = DANGERLEVELS-1; i>=0; i--)
02456     {
02457         e.frontDanger[i]   = frontDanger[i];
02458         e.sideDanger[i][0] = sideDanger[i][0];
02459         e.sideDanger[i][1] = sideDanger[i][1];
02460     }
02461 
02462 
02463     int side = 1;
02464     if (preferedSide < 0)
02465     {
02466         for (i = DANGERLEVELS-1; i>=0; i--)
02467         {
02468             int dSwap = sideDanger[i][0];
02469             sideDanger[i][0] = sideDanger[i][1];
02470             sideDanger[i][1] = dSwap;
02471         }
02472 
02473         int dSwap = sDanger[0];
02474         sDanger[0] = sDanger[1];
02475         sDanger[1] = dSwap;
02476 
02477         sides[1] = &left;
02478         sides[0] = &right;
02479         side     = -1;
02480         preferedSide = 1;
02481     }
02482 
02483 
02484     // no problem in the preferred direction. Just take it.
02485     if (preferedSide)
02486     {
02487         if( fDanger  * 3 >= sDanger[1] * 2 - 5 &&
02488                 sDanger[0] * 3 >= sDanger[1] * 2 - 5)
02489         {
02490             freeSide -= side*100;
02491             e.turn = side;
02492             data.turn = side;
02493             return true;
02494         }
02495 
02496         if (fDanger * 2 <= sDanger[0] * 3 + 3)
02497         {
02498             log->DeleteEntry();
02499             return false;
02500         }
02501     }
02502 
02503     // it is safer driving straight on
02504     if (fDanger <= sDanger[0] + 3 && fDanger <= sDanger[1] + 3 && fDanger < 20)
02505     {
02506         log->DeleteEntry();
02507         return false;
02508     }
02509 
02510 
02511     if (turn)
02512     {
02513         freeSide -= side*100;
02514         e.turn = turn;
02515         data.turn = turn;
02516     }
02517     else
02518         log->DeleteEntry();
02519 
02520     return turn;
02521 
02522     eDebugLine::SetTimeout(0);
02523 }
02524 
02525 
02526 void gAIPlayer::EmergencyTrace( ThinkData & data )
02527 {
02528     EmergencySurvive( data, -1, -traceSide );
02529 }
02530 
02531 
02532 void gAIPlayer::EmergencyPath( ThinkData & data )
02533 {
02534     EmergencySurvive( data );
02535 }
02536 
02537 void gAIPlayer::EmergencyCloseCombat( ThinkData & data )
02538 {
02539     EmergencySurvive( data );
02540 
02541     /*
02542       int dir = 0;
02543 
02544       if (target)
02545       {
02546          eCoord enemyPos = target->Position() - Object()->Position();
02547          eCoord dirRel   = Object()->Direction();
02548          if (enemyPos * dirRel < 0)
02549       dir --;
02550          else
02551       dir ++;
02552       }
02553 
02554 
02555       EmergencySurvive(front, left, right, 1, dir);
02556     */
02557 }
02558 
02559 
02560 
02561 
02562 void gAIPlayer::RightBeforeDeath(int triesLeft) // is called right before the vehicle gets destroyed.
02563 {
02564     if ( nCLIENT == sn_GetNetState() )
02565         return;
02566 
02567     if ( simpleAI_ )
02568         return;
02569 
02570     if (!character)
02571     {
02572         st_Breakpoint();
02573         return;
02574     }
02575 
02576     gRandomController random( randomizer_ );
02577 
02578     // think again immediately after this
02579     nextTime = lastTime;
02580 
02581 #ifdef DEBUG_X
02582     if (log && !Object()->Alive())
02583     {
02584         log->Print();
02585         //      st_Breakpoint();
02586         delete log;
02587         log = NULL;
02588     }
02589 #endif
02590 
02591     if (!Object()->Alive() || ( character && Random() * 10 > character->properties[AI_EMERGENCY] ) )
02592         return;
02593 
02594 
02595     // get the delay between two turns
02596     REAL delay = Delay();
02597 
02598     this->triesLeft = triesLeft;
02599     this->emergency = (triesLeft < 2);
02600 
02601     REAL speed=Object()->Speed();
02602     REAL range=speed;
02603     eCoord dir=Object()->Direction();
02604     REAL  side = speed*delay;
02605 
02606     gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
02607     gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
02608     gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
02609 
02610 
02611 
02612 
02613 #ifdef TESTSTATE  
02614     state = TESTSTATE;
02615     nextStateChange = se_GameTime() + 100;
02616 #else
02617     // switch to survival state if our victim died:
02618     if ((!target || !target->Alive()) && state != AI_TRACE)
02619         SwitchToState(AI_SURVIVE, 1);
02620 #endif
02621 
02622     ThinkData data( front, left, right);
02623     switch (state)
02624     {
02625     case AI_SURVIVE:
02626         EmergencySurvive(data);
02627         break;
02628     case AI_PATH:
02629         EmergencyPath(data);
02630         break;
02631     case AI_TRACE:
02632         EmergencyTrace(data);
02633         break;
02634     case AI_CLOSECOMBAT:
02635         EmergencyCloseCombat(data);
02636         break;
02637     }
02638     ActOnData( data );
02639 
02640 #ifdef DEBUG_X
02641     {
02642         gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
02643         gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
02644         gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
02645     }
02646 #endif
02647 }
02648 
02649 void gAIPlayer::NewObject()         // called when we control a new object
02650 {
02651     lastTime = 0;
02652     lastPath = 0;
02653     lastChangeAttempt = 0;
02654     lazySideChange = 0;
02655     path.Clear();
02656 
02657     if (character)
02658     {
02659         nextTime        = character->properties[AI_STARTSTRAIGHT] * gArena::SizeMultiplier()/gCycleMovement::SpeedMultiplier();
02660         nextStateChange = character->properties[AI_STATECHANGE];
02661         state           = (gAI_STATE)character->properties[AI_STARTSTATE];
02662     }
02663     else
02664     {
02665         nextTime        = 10;
02666         nextStateChange = 30;
02667         state           = AI_TRACE;
02668     }
02669 
02670     if (log)
02671         delete log;
02672     log = NULL;
02673 
02674     ClearTarget();
02675 }
02676 
02677 static gAISensor * sg_GetSensor( int currentDirectionNumber, gCycle const & object, int turn, REAL side, REAL range, REAL corridor, REAL & mindist )
02678 {
02679     // determine the current direction
02680     eGrid & grid = *object.Grid();
02681     eCoord origDir = grid.GetDirection( currentDirectionNumber );
02682 
02683     // determine sensors
02684     gAISensor * ret = 0;
02685 
02686     // turn direction
02687     int direction = currentDirectionNumber;
02688     int turns = 1;
02689     grid.Turn( direction, turn );
02690     eCoord dir = grid.GetDirection( direction );
02691     while ( !ret || ( turn * ( origDir * dir ) > .01 && currentDirectionNumber != direction ) )
02692     {
02693         // cast rays
02694         gAISensor * sensor = tNEW(gAISensor)(&object,object.Position(),dir, side, range, corridor*.5f, turn );
02695         if ( !ret || sensor->distance > ret->distance )
02696         {
02697             if ( ret )
02698             {
02699                 // calculate effective distance required to turn around in time
02700                 REAL dist = ret->distance - 1.2 * object.Speed() * Delay() * turns;
02701                 if ( mindist > dist )
02702                     mindist = dist;
02703             }
02704 
02705             delete ret;
02706             ret = sensor;
02707         }
02708         else
02709         {
02710             delete sensor;
02711         }
02712 
02713         // go on turning
02714         grid.Turn( direction, turn );
02715         dir = grid.GetDirection( direction );
02716         ++turns;
02717     }
02718 
02719     return ret;
02720 }
02721 
02722 REAL gAIPlayer::Think(){
02723     if ( !simpleAI_ )
02724     {
02725         gSimpleAIFactory * factory = gSimpleAIFactory::Get();
02726         if ( factory )
02727         {
02728             simpleAI_ = factory->Create( Object() );
02729         }
02730     }
02731 
02732     if ( simpleAI_ )
02733     {
02734         return simpleAI_->Think();
02735     }
02736 
02737     // get the delay between two turns
02738     REAL delay = Delay();
02739 
02740 #ifdef DEBUG_X  
02741     if (log && !Object()->Alive())
02742     {
02743         log->Print();
02744         st_Breakpoint();
02745         delete log;
02746         log = NULL;
02747     }
02748 #endif
02749 
02750     if (!Object()->Alive())
02751         return 100;
02752 
02753     emergency = false;
02754     //  return 1;
02755 
02756     // first, find close eWalls and evade them.
02757     REAL speed=Object()->Speed();
02758     REAL range=speed;
02759     eCoord dir=Object()->Direction();
02760     REAL side=speed*delay;
02761 
02762     REAL corridor = range;
02763     if (corridor < side * 2)
02764         corridor = side * 2;
02765 
02766     gAISensor front(Object(),Object()->Position(),dir, side * 2, range, corridor, 0);
02767 
02768 #ifdef DEBUG_X
02769     if (front.Hit())
02770     {
02771         gRandomController noRandom;
02772 
02773         if (front.distance < 1)
02774         {
02775             int x;
02776             x = 1;
02777         }
02778         gAISensor front(Object(),Object()->Position(),dir, side, range, corridor, 0);
02779     }
02780 #endif
02781 
02782     // get the sensors to the left and right with the most free space
02783     int currentDirectionNumber = Object()->Grid()->DirectionWinding( dir );
02784     REAL mindistLeft = 1E+30, mindistRight = 1E+30;
02785     std::auto_ptr< gAISensor > left  ( sg_GetSensor( currentDirectionNumber, *Object(), -1, side, range, corridor, mindistLeft ) );
02786     std::auto_ptr< gAISensor > right ( sg_GetSensor( currentDirectionNumber, *Object(), 1, side, range, corridor, mindistRight ) );
02787 
02788     // count intermediate walls to the left and right as if they were in front
02789     {
02790         REAL mindistFront = mindistLeft > mindistRight ? mindistLeft : mindistRight;
02791         if ( mindistFront < front.distance )
02792         {
02793             front.distance = mindistFront;
02794         }
02795     }
02796 
02797 #ifdef TESTSTATE  
02798     state = TESTSTATE;
02799     nextStateChange = se_GameTime() + 100;
02800 #else
02801     // switch to survival state if our victim died:
02802     if (state != AI_SURVIVE && state != AI_TRACE && (!target || !target->Alive()))
02803         SwitchToState(AI_SURVIVE, 1);
02804 #endif
02805 
02806     {
02807         eDebugLine::SetTimeout(.5);
02808         eDebugLine::SetColor  (0, 1, 0);
02809         eCoord p = Object()->Position();
02810         eDebugLine::Draw(p, .5, p, 5.5);
02811         eDebugLine::SetTimeout(0);
02812     }
02813 
02814     triesLeft = 10;
02815 
02816     REAL ret = 1;
02817 
02818     //not the best solution, but still better than segfault...
02819     if(left.get() != 0 && right.get() != 0) {
02820         ThinkData data( front, *left, *right);
02821         switch (state)
02822         {
02823         case AI_SURVIVE:
02824             ThinkSurvive(data);
02825             break;
02826         case AI_PATH:
02827             ThinkPath(data);
02828             break;
02829         case AI_TRACE:
02830             ThinkTrace(data);
02831             break;
02832         case AI_CLOSECOMBAT:
02833             ThinkCloseCombat(data);
02834             break;
02835         }
02836         ActOnData( data );
02837         ret = data.thinkAgain;
02838     }
02839 
02840 #ifdef DEBUG_X
02841     {
02842         gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0);
02843         gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1);
02844         gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1);
02845     }
02846 #endif
02847 
02848     REAL mindist = front.distance * front.distance * 8;
02849 
02850     const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
02851 
02852     // find the closest enemy
02853     for (int i=gameObjects.Len()-1;i>=0;i--){
02854         gCycle *other=dynamic_cast<gCycle *>(gameObjects(i));
02855 
02856         if (other && other != Object()){
02857             // then, enemy is realy an enemy
02858             eCoord otherpos=other->Position()-Object()->Position();
02859             REAL dist = otherpos.NormSquared();
02860 
02861             if (dist < mindist)
02862                 mindist = dist;
02863         }
02864     }
02865 
02866     mindist = sqrt(mindist) / (3 * Object()->Speed());
02867 
02868     if (ret > mindist)
02869         ret = mindist;
02870 
02871     return ret;
02872 }
02873 
02874 std::ostream & operator << ( std::ostream & s, gAIPlayer::ThinkDataBase const & data )
02875 {
02876     s << data.turn << " " << data.thinkAgain;
02877 
02878     return s;
02879 }
02880 
02881 std::istream & operator >> ( std::istream & s, gAIPlayer::ThinkDataBase & data )
02882 {
02883     s >> data.turn >> data.thinkAgain;
02884 
02885     return s;
02886 }
02887 
02888 static char const * section = "AI";
02889 
02890 void gAIPlayer::ActOnData( ThinkData & data )
02891 {
02892     // delegate
02893     ThinkDataBase & base = data;
02894     ActOnData( base );
02895 
02896     // sanitize next think time so it will be before we hit the next wall
02897     if ( Object()->Speed() > 0 && data.thinkAgain > 0 )
02898     {
02899         gSensor front( Object(), Object()->Position(), Object()->Direction() );
02900         front.detect( Object()->Speed() * data.thinkAgain * 1.5 );
02901         if ( front.ehit )
02902         {
02903             REAL thinkAgain = ( front.hit / Object()->Speed() ) * .8;
02904             if ( data.thinkAgain > thinkAgain )
02905                 data.thinkAgain = thinkAgain;
02906         }
02907     }
02908 }
02909 
02910 void gAIPlayer::ActOnData( ThinkDataBase & data )
02911 {
02912     // archive decision
02913     ThinkDataBase copy = data;
02914     if ( tRecorder::Playback( section, data ) )
02915     {
02916         if ( copy.turn != data.turn )
02917         {
02918             // AI made a different decision than recorded, better let a programmer have a look at it
02919             std::cout << "AI turn decision changed!\n";
02920             st_Breakpoint();
02921         }
02922 
02923         REAL difference =  fabs( copy.thinkAgain - data.thinkAgain );
02924         static REAL minReport = EPS;
02925         if ( difference > minReport )
02926         {
02927             minReport = difference * 2;
02928             std::cout << "AI timing decision changed by " << difference
02929             << " from " << data.thinkAgain << " to " << copy.thinkAgain <<"!\n";
02930             st_Breakpoint();
02931         }
02932     }
02933     tRecorder::Record( section, data );
02934 
02935     // execute turn
02936     if ( data.turn )
02937         Object()->Turn( data.turn );
02938 }
02939 
02940 const REAL relax=25;
02941 
02942 void gAIPlayer::Timestep(REAL time){
02943     if (!character)
02944     {
02945         st_Breakpoint();
02946         return;
02947     }
02948 
02949     // don't think if the object is not up to date
02950     if ( Object() && Object()->LastTime() < time - EPS )
02951         return;
02952 
02953     REAL ts=time-lastTime;
02954     lastTime=time;
02955 
02956     if (concentration < 0)
02957         concentration = 0;
02958 
02959     concentration += 4*(character->properties[AI_REACTION]+1) * ts/relax;
02960     concentration=concentration/(1+ts/relax);
02961 
02962     if (bool(Object()) && Object()->Alive() && nextTime<time){
02963         gRandomController random( randomizer_ );
02964 
02965         REAL nextthought=Think();
02966         //    if (nextthought>.9) nextthought=REAL(.9);
02967 
02968         if (nextthought<REAL(.6-concentration)) nextthought=REAL(.6-concentration);
02969 
02970         nextTime=nextTime+nextthought;
02971 
02972         //con << concentration << "\t" << nextthought << '\t' << ts << '\n';
02973 
02974         if(.1+4*nextthought<1)
02975             concentration*=REAL(.1+4*nextthought);
02976     }
02977 }
02978 
02979 
02980 void gAIPlayer::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
02981 {
02982     ePlayerNetID::Color( a_r, a_g, a_b );
02983 }
02984 
02985 gAIPlayer::~gAIPlayer()
02986 {
02987     target=NULL;
02988     ClearObject();
02989     tCHECK_DEST;
02990 
02991     delete log;
02992     log = NULL;
02993 }
02994 
02995 void gAIPlayer::ClearAll()
02996 {
02997     sg_AIReferences.ReleaseAll();
02998 
02999     // remove empty AI team
03000     if ( 0 == AITeam()->NumPlayers() )
03001     {
03002         ClearAITeam();
03003     }
03004 }
03005 /*
03006 void gAIPlayer::AddRef()
03007 {
03008         ePlayerNetID::AddRef();
03009 }
03010 
03011 void gAIPlayer::Release()
03012 {
03013         ePlayerNetID::Release();
03014 }
03015 */

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