00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00067
00068 #endif
00069
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
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
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
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
00197 }
00198
00199 gAITeam::gAITeam()
00200 {
00201
00202 }
00203
00204 static nNOInitialisator<gAITeam> gAITeam_init(331,"gAITeam");
00205
00206 nDescriptor &gAITeam::CreatorDescriptor() const
00207 {
00208 return gAITeam_init;
00209 }
00210
00211
00212 void gAITeam::BalanceWithAIs(bool balanceWithAIs)
00213 {
00214
00215 EnforceConstraints();
00216
00217 int numTeams = 0, numTeamsWithPlayers = 0;
00218
00219
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
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
00252 if ( !balanceWithAIs && t->NumHumanPlayers() > 0 )
00253 wishAIs = 0;
00254
00255
00256 if ( numTeamsWithPlayers >= minTeams && t->NumHumanPlayers() == 0 )
00257 wishAIs = 0;
00258
00259
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
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
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
00318 nNetObject::ClearAllDeleted();
00319 }
00320
00321
00322 bool gAITeam::PlayerMayJoin(const ePlayerNetID* player) const
00323 {
00324 return !player->IsHuman();
00325 }
00326
00327
00328
00329 class gCycleTouchEvent{
00330 public:
00331 REAL dist;
00332 REAL otherDist;
00333 int otherSide;
00334 int winding;
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
00367 gCycleTouchEvent max[2];
00368 gCycleTouchEvent min[2];
00369 };
00370
00371 #define TOL 4
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
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;
00393 int ends = 0;
00394
00395 bool bClosedIn = false;
00396
00397 const gCycle *run = b;
00398 int end = dir;
00399 int side = bSide;
00400 REAL dist = bDist;
00401 winding = 0;
00402
00403 int turn = a->Grid()->WindingNumber();
00404 int halfTurn = turn >> 1;
00405
00406
00407 #ifdef DEBUG
00408
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
00419
00420 #endif
00421 if (end > 0)
00422 {
00423
00424
00425 gCycleMemoryEntry* last = run->memory.Latest(side);
00426 if (!last || last->max[side].dist <= dist + TOL)
00427 {
00428
00429
00430 #ifdef DEBUG
00431
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
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
00456 #endif
00457
00458
00459 gCycleMemoryEntry* first = run->memory.Earliest(side);
00460 if (first && first->min[side].dist >= dist + TOL)
00461 {
00462
00463 winding += first->min[side].winding;
00464 run = first->cycle;
00465 end = (side == first->min[side].otherSide) ? 1 : 0;
00466
00467
00468
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
00482 {
00483
00484 gCycleMemoryEntry* first = run->memory.Earliest(side);
00485 if (!first || first->min[side].dist >= dist - TOL)
00486 {
00487 #ifdef DEBUG
00488
00489 #endif
00490
00491
00492
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
00504 #endif
00505
00506
00507
00508 gCycleMemoryEntry* last = run->memory.Latest(side);
00509 if (last && last->max[side].dist <= dist - TOL)
00510 {
00511
00512 winding += last->max[side].winding;
00513
00514
00515 winding += halfTurn * (side > 0 ? -1 : 1);
00516
00517 run = last->cycle;
00518 end = (side == last->max[side].otherSide) ? 0 : 1;
00519
00520
00521
00522
00523
00524 dist = last->max[side].otherDist;
00525 side = last->max[side].otherSide;
00526 }
00527 else
00528
00529 {
00530 winding = 0;
00531 return false;
00532 }
00533 }
00534 }
00535 }
00536
00537 #ifdef DEBUG
00538
00539
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
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
00564 for (int i = closedIn.Len()-1; i>=0; i--)
00565 if (other == closedIn(i))
00566 return false;
00567
00568
00569 return true;
00570 }
00571 }
00572
00573 return false;
00574 }
00575
00576
00577
00578
00579
00580 class gLoopData{
00581 public:
00582 bool loop;
00583 int winding;
00584 tArray<const gCycle*>closedIn;
00585
00586 gLoopData():loop(false), winding(0){}
00587 void AddCycle(const gCycle* c){closedIn[closedIn.Len()] = c;}
00588 };
00589
00590
00591 class gHitData{
00592 public:
00593 const eHalfEdge* edge;
00594 gSensorWallType wallType;
00595 int lr;
00596 REAL distance;
00597
00598
00599 gCycle *otherCycle;
00600 REAL driveDistance;
00601 int windingNumber;
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;
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623 REAL otherDist = eCoord::F( dir, sensor.before_hit - origin );
00624
00625 if (otherDist < EPS)
00626 return;
00627
00628 if (Hit() && distance < otherDist)
00629 return;
00630
00631
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
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
00657 class gAISensor{
00658 public:
00659
00660
00661 const gCycle* cycle;
00662
00663 bool hit;
00664
00665 gHitData front;
00666 gHitData sides[2];
00667
00668
00669 gLoopData frontLoop[2];
00670 gLoopData sideLoop[2][2];
00671
00672 REAL distance;
00673
00674 bool Hit() const
00675 {
00676 return hit;
00677 }
00678
00679 void DetectLoop(const gHitData& hit, gLoopData loopData[2])
00680 {
00681
00682 gCycle *other = hit.otherCycle;
00683
00684 if (other)
00685 {
00686
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
00705
00706 winding += cycle->WindingNumber();
00707
00708 winding -= hit.windingNumber;
00709
00710
00711
00712
00713 winding += lr;
00714
00715 #ifdef DEBUG_X
00716 if (winding != 4 && winding != -4)
00717 {
00718 gRandomController noRandom;
00719 if (winding != 0)
00720
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
00735 #endif
00736
00737
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,
00751 REAL frontScan,
00752 REAL corridorScan,
00753 int winding
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
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
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
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
00849
00850 gSensor parallel(cycle, start + lrDir * thisSideScan, dir);
00851 parallel.detect(corridorScan);
00852
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
00949
00950
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
00977
00978
00979
00980
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
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 {
01014 ai->SetTraceSide(aDir > 0 ? 1 : -1);
01015 ai->SwitchToState(AI_TRACE, 10);
01016 ai->target = const_cast< gCycle * >( a );
01017 }
01018 else
01019 {
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
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
01041 void gAIPlayer::CycleBlocksRim(const gCycleMovement *a, int aDir)
01042 {
01043 }
01044
01045
01046 void gAIPlayer::BreakWall(const gCycleMovement *a, REAL aDist)
01047 {}
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
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
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
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
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;
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
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
01154
01155
01156 }
01157 }
01158
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()
01187 {
01188
01189 }
01190
01191
01192
01193 void gAIPlayer::SetNumberOfAIs(int num, int minPlayers, int iq, int tries)
01194 {
01195
01196 gAITeam::BalanceWithAIs();
01197
01198
01199 for (int i = se_PlayerNetIDs.Len()-1; i>=0; i--)
01200 {
01201
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
01224 do
01225 {
01226 count = 0;
01227
01228 int i;
01229 gAIPlayer* worstIQ = NULL;
01230
01231
01232
01233
01234
01235 for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
01236 {
01237
01238 ePlayerNetID *p = se_PlayerNetIDs(i);
01239 gAIPlayer *ai = dynamic_cast<gAIPlayer*>(p);
01240 if (ai && ai->NextTeam() == sg_AITeam )
01241 {
01242
01243
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
01255 count -= num;
01256
01257
01258 int pcount = - minPlayers;
01259
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
01276 if (worstIQ && (count > 0 || (count >= 0 && !iqperfect)))
01277 {
01278
01279 worstIQ->RemoveFromGame();
01280
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
01293 if ( 0 == AITeam()->NumPlayers() )
01294 {
01295 ClearAITeam();
01296 }
01297 }
01298 if (bestIQ && count < 0)
01299 {
01300
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
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323 count++;
01324 }
01325
01326 }
01327 while ((count != 0 ||
01328 !iqperfect) &&
01329 tries-- != 0);
01330
01331 }
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
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
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
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
01418 void gAIPlayer::ThinkSurvive( ThinkData & data )
01419 {
01420 if (!character)
01421 {
01422 st_Breakpoint();
01423 return;
01424 }
01425
01426 REAL random = 0;
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
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
01457 random=10*Random();
01458 if (random < 5)
01459 {
01460
01461 eCoord enemypos=eCoord(1000,100);
01462
01463 const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
01464 gCycle *secondbest = NULL;
01465
01466
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
01473 eCoord otherpos=other->Position()-Object()->Position();
01474 if (otherpos.NormSquared()<enemypos.NormSquared()){
01475
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
01550 eCoord enemypos=eCoord(1000,100);
01551
01552 const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects();
01553 gCycle *secondbest = NULL;
01554
01555
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
01562 eCoord otherpos=other->Position()-Object()->Position();
01563 if (otherpos.NormSquared()<enemypos.NormSquared()){
01564
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
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
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)
01626 {
01627 SwitchToState(AI_CLOSECOMBAT, 5);
01628 EmergencySurvive( data );
01629
01630 return;
01631 }
01632 }
01633
01634
01635
01636
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
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
01692 eCoord pos = Object()->Position();
01693 eCoord target = path.CurrentPosition();
01694
01695
01696 REAL ahead = eCoord::F(target - pos, dir)
01697 + eCoord::F(path.CurrentOffset(), dir);
01698
01699 if ( ahead > 0)
01700 {
01701 mindist = ahead;
01702 }
01703 else
01704 {
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
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
01748
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)
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
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
01785 enemypos=enemypos.Turn(dir.Conj()).Turn(0,1);
01786 enemydir=enemydir.Turn(dir.Conj()).Turn(0,1);
01787
01788
01789
01790
01791
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
01801 }
01802
01803
01804
01805 #define REACTION .2
01806
01807
01808
01809 REAL ourspeed=Object()->Speed();
01810
01811 REAL enemydist=target->Lag()*enemyspeed;
01812
01813
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
01824 enemypos.y-=ourdist;
01825
01826 REAL forward=-enemypos.y+.01;
01827 if (forward<0)
01828 forward=0;
01829 if (forward>enemydist)
01830 forward=enemydist;
01831
01832 enemypos.y+=forward;
01833 enemydist-=forward;
01834
01835
01836 enemypos.x-=enemydist;
01837
01838 if (enemypos.y*enemyspeed>enemypos.x*ourspeed){
01839 if (random<fear){
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
01868
01869
01870
01871
01872
01873
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
01924
01925
01926
01927
01928
01929
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939
01940
01941
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
02013 #endif
02014 #endif
02015 }
02016 };
02017
02018
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
02041
02042 )
02043 {
02044 log->Print();
02045
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
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
02067 REAL delay = Delay();
02068 REAL range = Object()->Speed() * delay;
02069
02070
02071 if (!Object()->CanMakeTurn(1) || !Object()->CanMakeTurn(-1))
02072 return false;
02073
02074
02075
02076
02077
02078
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
02100
02101 bool isTrapped = IsTrapped(Object(), NULL);
02102
02103
02104
02105
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
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
02128
02129
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
02139 {
02140 frontDanger[LOOPLEVEL] += 20;
02141
02142 sideDanger[LOOPLEVEL][i] += 10;
02143 }
02144 }
02145
02146
02147
02148
02149 }
02150
02151
02152 if (character->properties[AI_LOOP] >= 10 && canTrapEnemy && !emergency)
02153 return false;
02154
02155
02156
02157
02158
02159
02160
02161
02162
02163
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
02185 for (i = 1; i>=0; i--)
02186 {
02187 if (sides[i]->Hit() &&
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
02202 if (frontDanger[SPACELEVEL] * 2 < sideDanger[SPACELEVEL][i])
02203 sideDanger[LOOPLEVEL][i-i] -= sideDanger[SPACELEVEL][i] * 2;
02204 }
02205 }
02206
02207
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
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
02237
02238 if ( otherNorm < closest)
02239 {
02240
02241 secondbest = dynamic_cast<gCycle *>(other);
02242 if (nothit){
02243 closest = otherNorm;
02244 target = secondbest;
02245 }
02246 }
02247 }
02248 else if (nothit)
02249 {
02250
02251
02252 eCoord friendpos=other->Position() - Object()->Position();
02253
02254
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
02268
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
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
02288 bool trap[2] = {false, false};
02289
02290 if (!isTrapped)
02291 for (i = 1; i>=0; i--)
02292 {
02293
02294
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
02307
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
02352 )
02353 sideDanger[TRAPLEVEL][i] += 20;
02354
02355 }
02356 }
02357 }
02358
02359 if (character->properties[AI_ENEMY] > 0)
02360 {
02361
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
02375 }
02376 }
02377
02378 int side = enemypos.x > 0 ? 1 : 0;
02379
02380
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
02401 int fDanger = 0;
02402 int sDanger[2] = { 0, 0 };
02403 for (i = 0; i<DANGERLEVELS; i++)
02404
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
02415 if (!fDanger && !preferedSide)
02416 return false;
02417
02418
02419
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
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
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
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
02543
02544
02545
02546
02547
02548
02549
02550
02551
02552
02553
02554
02555
02556
02557 }
02558
02559
02560
02561
02562 void gAIPlayer::RightBeforeDeath(int triesLeft)
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
02579 nextTime = lastTime;
02580
02581 #ifdef DEBUG_X
02582 if (log && !Object()->Alive())
02583 {
02584 log->Print();
02585
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
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
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()
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
02680 eGrid & grid = *object.Grid();
02681 eCoord origDir = grid.GetDirection( currentDirectionNumber );
02682
02683
02684 gAISensor * ret = 0;
02685
02686
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
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
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
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
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
02755
02756
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
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
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
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
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
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
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
02893 ThinkDataBase & base = data;
02894 ActOnData( base );
02895
02896
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
02913 ThinkDataBase copy = data;
02914 if ( tRecorder::Playback( section, data ) )
02915 {
02916 if ( copy.turn != data.turn )
02917 {
02918
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
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
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
02967
02968 if (nextthought<REAL(.6-concentration)) nextthought=REAL(.6-concentration);
02969
02970 nextTime=nextTime+nextthought;
02971
02972
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
03000 if ( 0 == AITeam()->NumPlayers() )
03001 {
03002 ClearAITeam();
03003 }
03004 }
03005
03006
03007
03008
03009
03010
03011
03012
03013
03014
03015