1 #include "RAI.h"
2 #include "LegacyCpp/IGlobalAICallback.h"
3 #include "LegacyCpp/UnitDef.h"
4 #include "LegacyCpp/CommandQueue.h"
5 #include "LegacyCpp/MoveData.h"
6 #include "CUtils/Util.h"
7 #include "System/Util.h"
8 #include "System/SafeCStrings.h"
9 #include <stdio.h>
10 //#include <direct.h> // mkdir function (windows)
11 //#include <sys/stat.h> // mkdir function (linux)
12 #include <time.h> // time(NULL)
13
14 static GlobalResourceMap* GRMap = NULL;
15 static GlobalTerrainMap* GTMap = NULL;
16 static cLogFile* gl = NULL;
17 static int RAIs=0;
18
19 namespace std
20 {
_xlen()21 void _xlen(){}
22 }
23
UnitInfo(sRAIUnitDef * rUnitDef)24 UnitInfo::UnitInfo(sRAIUnitDef* rUnitDef)
25 {
26 udr=rUnitDef;
27 ud=rUnitDef->ud;
28
29 AIDisabled=false;
30 humanCommand=false;
31 unitBeingBuilt=true;
32 inCombat=false;
33 lastUnitIdleFrame=0;
34 lastUnitDamagedFrame=0;
35 enemyID=-1;
36 area=0;
37 E=0;
38 enemyEff=0;
39 udrBL=0;
40 RS=0;
41 BuildQ=0;
42 CloakUI=0;
43 OnOffUI=0;
44 SWeaponUI=0;
45 group=0;
46 UE = 0;
47 }
48
EnemyInfo()49 EnemyInfo::EnemyInfo()
50 {
51 inLOS=false;
52 inRadar=false;
53 baseThreatFrame=-1;
54 baseThreatID=-1;
55 ud=0;
56 udr=0;
57 RS=0;
58 posLocked=false;
59 }
60
cRAI()61 cRAI::cRAI()
62 {
63 DebugEnemyEnterLOS=0;
64 DebugEnemyLeaveLOS=0;
65 DebugEnemyEnterRadar=0;
66 DebugEnemyLeaveRadar=0;
67 DebugEnemyDestroyedLOS=0;
68 DebugEnemyDestroyedRadar=0;
69 DebugEnemyEnterLOSError=0;
70 DebugEnemyLeaveLOSError=0;
71 DebugEnemyEnterRadarError=0;
72 DebugEnemyLeaveRadarError=0;
73 eventSize = 0;
74 SWM=0;
75 cb=0;
76 frame=0;
77 memset(eventList, 0, EVENT_LIST_SIZE);
78 TM=0;
79 RM=0;
80 UM=0;
81 UDH=0;
82 CM=0;
83 B=0;
84 l=0;
85 }
86
~cRAI()87 cRAI::~cRAI()
88 {
89 *l<<"\n\nShutting Down ...";
90 if( RAIDEBUGGING )
91 {
92 *l<<"\n cRAI Debug:";
93 *l<<"\n clearing Units size="<<int(Units.size())<<": ";
94 *l<<"\n clearing Enemies size="<<Enemies.size()<<": ";
95 }
96
97 while( int(Units.size()) > 0 )
98 UnitDestroyed(Units.begin()->first,-1);
99 while( int(Enemies.size()) > 0 )
100 EnemyDestroyed(Enemies.begin()->first,-1);
101
102 if( RAIDEBUGGING )
103 {
104 int ERadar,ELOS;
105 *l<<"\n Enemys (Enter LOS - Errors) = "<<DebugEnemyEnterLOS<<" = "<<DebugEnemyEnterLOS+DebugEnemyEnterLOSError<<"-"<<DebugEnemyEnterLOSError;
106 *l<<"\n Enemys (Leave LOS + Destroyed in LOS - Errors) = "<<DebugEnemyLeaveLOS+DebugEnemyDestroyedLOS<<" = "<<DebugEnemyLeaveLOS+DebugEnemyLeaveLOSError<<"+"<<DebugEnemyDestroyedLOS<<"-"<<DebugEnemyLeaveLOSError;
107 *l<<"\n Enemies Remaining in LOS = "<<(ELOS=DebugEnemyEnterLOS-DebugEnemyLeaveLOS-DebugEnemyDestroyedLOS);
108 *l<<"\n Enemys (Enter Radar - Errors) = "<<DebugEnemyEnterRadar<<" = "<<DebugEnemyEnterRadar+DebugEnemyEnterRadarError<<"-"<<DebugEnemyEnterRadarError;
109 *l<<"\n Enemys (Leave Radar + Destroyed in Radar - Errors) = "<<DebugEnemyLeaveRadar+DebugEnemyDestroyedRadar<<" = "<<DebugEnemyLeaveRadar+DebugEnemyLeaveRadarError<<"+"<<DebugEnemyDestroyedRadar<<"-"<<DebugEnemyLeaveRadarError;
110 *l<<"\n Enemies Remaining in Radar = "<<(ERadar=DebugEnemyEnterRadar-DebugEnemyLeaveRadar-DebugEnemyDestroyedRadar);
111 *l<<"\n Enemies Remaining = "<<int(Enemies.size());
112 *l<<"\n Units Remaining = "<<UM->GroupSize;
113 *l<<"\n Groups Remaining = "<<UM->GroupSize;
114 if( UM->GroupSize != 0 || ELOS != 0 || ERadar != 0 )
115 *l<<"\n (ERROR)";
116 }
117
118 delete UM;
119 UM = NULL;
120 delete B;
121 B = NULL;
122 delete SWM;
123 SWM = NULL;
124 delete CM;
125 CM = NULL;
126 delete UDH;
127 UDH = NULL;
128
129 RAIs--;
130 if( RAIs == 0 )
131 {
132 *gl<<"\n Global RAI Shutting Down";
133 // double closingTimer = clock();
134 GlobalResourceMap* tmpRM = GRMap;
135 GRMap = NULL;
136 RM = NULL;
137 delete tmpRM;
138 tmpRM = NULL;
139 // *gl<<"\n Resource-Map Closing Time: "<<(clock()-closingTimer)/(double)CLOCKS_PER_SEC<<" seconds";
140 GlobalTerrainMap* tmpTM = GTMap;
141 GTMap = NULL;
142 TM = NULL;
143 delete tmpTM;
144 tmpTM = NULL;
145 *gl<<"\n Global RAI Shutdown Complete.";
146 delete gl;
147 gl = NULL;
148 }
149
150 *l<<"\nShutdown Complete.";
151 delete l;
152 l = NULL;
153 }
154
InitAI(IGlobalAICallback * callback,int team)155 void cRAI::InitAI(IGlobalAICallback* callback, int team)
156 {
157 cb = callback->GetAICallback();
158 if( GRMap == 0 )
159 ClearLogFiles();
160 l = new cLogFile(cb, GetLogFileSubPath(cb->GetMyTeam()), false);
161
162 /* string test = (char*)cb->GetMyTeam(); // Crashes Spring? Spring-Version(v0.76b1)
163 *l<<"cb->GetMyTeam()="<<test<<"\n";
164 */
165 if( GRMap == 0 )
166 {
167 ClearLogFiles();
168 gl = new cLogFile(cb, "log/RAIGlobal_LastGame.log", false);
169 *gl<<"Loading Global RAI...";
170 *gl<<"\n Mod = " << cb->GetModHumanName() << "(" << IntToString(cb->GetModHash(), "%x") << ")";
171 *gl<<"\n Map = " << cb->GetMapName() << "(" << IntToString(cb->GetMapHash(), "%x") << ")";
172 int seed = time(NULL);
173 srand(seed);
174 RAIs=0;
175 double loadingTimer = clock();
176 GTMap = new GlobalTerrainMap(cb,gl);
177 *gl<<"\n Terrain-Map Loading Time: "<<(clock()-loadingTimer)/(double)CLOCKS_PER_SEC<<" seconds";
178 loadingTimer = clock();
179 GRMap = new GlobalResourceMap(cb,gl,GTMap);
180 *gl<<"\n Resource-Map Loading Time: "<<(clock()-loadingTimer)/(double)CLOCKS_PER_SEC<<" seconds\n";
181 /*
182 loadingTimer = clock();
183 CMetalMap* KMM;
184 KMM = new CMetalMap(cb);
185 KMM->Init();
186 *l<<" KAI Metal-Map Loading Time: "<<(clock()-loadingTimer)/(double)CLOCKS_PER_SEC<<" seconds";
187 *l<<"\n KAI Metal-Sites Found: "<<KMM->NumSpotsFound;
188 delete KMM;
189 */
190 *gl<<"\nGlobal RAI Loading Complete.\n\n";
191 }
192 RM = GRMap;
193 TM = GTMap;
194 RAIs++;
195
196 *l<<"Loading ...";
197 *l<<"\n Team = "<<cb->GetMyTeam();
198 *l<<"\n Ally Team = "<<cb->GetMyAllyTeam();
199
200 *l<<"\nLoading cRAIUnitDefHandler ...";
201 UDH = new cRAIUnitDefHandler(cb,RM,TM,l);
202
203 UM = new cUnitManager(cb,this);
204 B = new cBuilder(cb,this);
205 UpdateEventAdd(3,cb->GetCurrentFrame()-1);
206 SWM = new cSWeaponManager(cb,this);
207 CM = new cCombatManager(cb,this);
208 *l<<"\nLoading Complete.\n";
209 /*
210 if( cb->GetUnitDef("arm_retaliator") != 0 ) // XTA Cheat Nuke Test
211 {
212 IAICheats* cheat=callback->GetCheatInterface();
213 float3 pos =*cb->GetStartPos();
214 float3 pos2 =*cb->GetStartPos();
215 float3 pos3 =*cb->GetStartPos();
216 if( pos.x < 8*cb->GetMapWidth() - pos.x )
217 {
218 pos.x = 1;
219 pos2.x = 200;
220 pos3.x = 1;
221 }
222 else
223 {
224 pos.x = 8*cb->GetMapWidth();
225 pos2.x = pos.x - 200;
226 pos3.x = pos.x;
227 }
228 if( pos.z < 8*cb->GetMapHeight() - pos.z )
229 {
230 pos.z = 1;
231 pos2.z = 1;
232 pos3.z = 200;
233 }
234 else
235 {
236 pos.z = 8*cb->GetMapHeight();
237 pos2.z = pos.z;
238 pos3.z = pos.z - 200;
239 }
240
241 pos = cb->ClosestBuildSite(cb->GetUnitDef("arm_retaliator"),pos,1500,0);
242 cheat->CreateUnit("arm_retaliator",pos);
243 cheat->CreateUnit("arm_moho_metal_maker",cb->ClosestBuildSite(cb->GetUnitDef("arm_moho_metal_maker"),pos,1500,3));
244 cheat->CreateUnit("arm_fusion_reactor",cb->ClosestBuildSite(cb->GetUnitDef("arm_fusion_reactor"),pos,1500,3));
245 cheat->CreateUnit("arm_energy_storage",cb->ClosestBuildSite(cb->GetUnitDef("arm_energy_storage"),pos,1500,3));
246 cheat->CreateUnit("arm_annihilator",cb->ClosestBuildSite(cb->GetUnitDef("arm_annihilator"),pos,1500,10));
247 cheat->CreateUnit("arm_annihilator",cb->ClosestBuildSite(cb->GetUnitDef("arm_annihilator"),pos,1500,10));
248 cheat->CreateUnit("arm_annihilator",cb->ClosestBuildSite(cb->GetUnitDef("arm_annihilator"),pos,1500,10));
249 }
250 */
251 }
252
UnitCreated(int unit,int builder)253 void cRAI::UnitCreated(int unit, int builder)
254 {
255 const UnitDef* ud=cb->GetUnitDef(unit);
256 if( RAIDEBUGGING ) { *l<<"\nUnitCreated("<<unit; *l<<")["+ud->humanName+"]"; }
257 Units.insert(iuPair(unit,UnitInfo(&UDH->UDR.find(ud->id)->second)));
258 UnitInfo* U = &Units.find(unit)->second;
259 float3 position = cb->GetUnitPos(unit);
260 U->area = GetCurrentMapArea(U->udr,position);
261
262 if( ud->speed == 0 )
263 {
264 for(map<int,UnitInfo*>::iterator i=UImmobile.begin(); i!=UImmobile.end(); ++i )
265 {
266 if( U->udr->WeaponGuardRange > 0 && i->second->udr->WeaponGuardRange == 0 && position.distance2D(cb->GetUnitPos(i->first)) < U->udr->WeaponGuardRange )
267 {
268 U->UDefending.insert(cRAI::iupPair(i->first,i->second));
269 i->second->UDefences.insert(cRAI::iupPair(unit,U));
270 }
271 else if( U->udr->WeaponGuardRange == 0 && i->second->udr->WeaponGuardRange > 0 && position.distance2D(cb->GetUnitPos(i->first)) < i->second->udr->WeaponGuardRange )
272 {
273 U->UDefences.insert(cRAI::iupPair(i->first,i->second));
274 i->second->UDefending.insert(cRAI::iupPair(unit,U));
275 }
276 }
277 UImmobile.insert(iupPair(unit,U));
278 }
279 else
280 UMobile.insert(iupPair(unit,U));
281
282 B->UnitCreated(unit,U);
283 B->BP->UResourceCreated(unit,U);
284 if( RAIDEBUGGING ) *l<<"#";
285 }
286
UnitFinished(int unit)287 void cRAI::UnitFinished(int unit)
288 {
289 if( RAIDEBUGGING ) *l<<"\nUnitFinished("<<unit<<")";
290 if( Units.find(unit) == Units.end() ) // Occurs if a player canceled a build order with more than one quaried and something still finished
291 UnitCreated(unit, -1);
292
293 UnitInfo* U = &Units.find(unit)->second;
294 U->unitBeingBuilt = false;
295 if( U->AIDisabled )
296 {
297 if( RAIDEBUGGING ) *l<<"#";
298 return;
299 }
300
301 B->UnitFinished(unit,U);
302 if( U->AIDisabled )
303 {
304 if( RAIDEBUGGING ) *l<<"#";
305 return;
306 }
307 B->PM->UnitFinished(unit,U);
308 SWM->UnitFinished(unit,U->udr);
309 UM->UnitFinished(unit,U);
310 if( U->ud->highTrajectoryType == 2 && rand()%4 == 0 )
311 { // Nothing too meanful, just a degree of randomness
312 Command c;
313 c.id = CMD_TRAJECTORY;
314 c.params.push_back(1);
315 cb->GiveOrder(unit,&c);
316 }
317 if( U->ud->speed == 0 )
318 UnitIdle(unit);
319
320 if( RAIDEBUGGING ) *l<<"#";
321 }
322
UnitDestroyed(int unit,int attacker)323 void cRAI::UnitDestroyed(int unit,int attacker)
324 {
325 if( RAIDEBUGGING ) *l<<"\nUnitDestroyed("<<unit<<","<<attacker<<")";
326 if( Units.find(unit) == Units.end() ) // Occurs if a player canceled a build order with more than one quaried and something was still being worked on
327 {
328 if( RAIDEBUGGING ) *l<<"#";
329 return;
330 }
331 UnitInfo *U = &Units.find(unit)->second;
332 if( U->UE != 0 )
333 UpdateEventRemove(U->UE);
334 if( !U->AIDisabled )
335 {
336 B->UnitDestroyed(unit,U);
337 if( !U->unitBeingBuilt )
338 {
339 B->PM->UnitDestroyed(unit,U);
340 SWM->UnitDestroyed(unit);
341 UM->UnitDestroyed(unit,U);
342 }
343 }
344 B->BP->UResourceDestroyed(unit,U);
345 if( U->ud->speed == 0 )
346 {
347 for(map<int,UnitInfo*>::iterator i=U->UDefending.begin(); i!=U->UDefending.end(); ++i )
348 i->second->UDefences.erase(unit);
349 for(map<int,UnitInfo*>::iterator i=U->UDefences.begin(); i!=U->UDefences.end(); ++i )
350 i->second->UDefending.erase(unit);
351 UImmobile.erase(unit);
352 }
353 else
354 UMobile.erase(unit);
355 Units.erase(unit);
356 if( RAIDEBUGGING ) *l<<"#";
357 }
358
EnemyEnterLOS(int enemy)359 void cRAI::EnemyEnterLOS(int enemy)
360 {
361 if( RAIDEBUGGING ) *l<<"\nEnemyEnterLOS("<<enemy<<")";
362 if( cb->GetUnitHealth(enemy) <= 0 ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
363 {
364 DebugEnemyEnterLOSError++;
365 *l<<"\nWARNING: EnemyEnterLOS("<<enemy<<"): enemy is either dead or not in LOS";
366 return;
367 }
368 DebugEnemyEnterLOS++;
369 if( Enemies.find(enemy) == Enemies.end() )
370 Enemies.insert(iePair(enemy,EnemyInfo()));
371 EnemyInfo *E = &Enemies.find(enemy)->second;
372 E->inLOS=true;
373 E->ud=cb->GetUnitDef(enemy);
374 E->udr=&UDH->UDR.find(E->ud->id)->second;
375 if( E->ud->speed == 0 )
376 {
377 E->position = cb->GetUnitPos(enemy);
378 E->posLocked = true;
379 }
380 else if( E->posLocked )
381 { // just in case the id is being reused
382 E->posLocked = false;
383 }
384
385 UM->EnemyEnterLOS(enemy,E);
386 B->BP->EResourceEnterLOS(enemy,E);
387 if( RAIDEBUGGING ) *l<<"#";
388 }
389
EnemyLeaveLOS(int enemy)390 void cRAI::EnemyLeaveLOS(int enemy)
391 {
392 if( RAIDEBUGGING ) *l<<"\nEnemyLeaveLOS("<<enemy<<")";
393 if( Enemies.find(enemy) == Enemies.end() ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
394 {
395 DebugEnemyLeaveLOSError++;
396 *l<<"\nWARNING: EnemyLeaveLOS("<<enemy<<"): unknown unit id";
397 return;
398 }
399 EnemyInfo *E = &Enemies.find(enemy)->second;
400 if( !E->inLOS ) // Work Around: Spring-Version(v0.76b1)
401 {
402 DebugEnemyLeaveLOSError++;
403 *l<<"\nWARNING: EnemyLeaveLOS("<<enemy<<"): not in LOS";
404 return;
405 }
406
407 DebugEnemyLeaveLOS++;
408 E->inLOS=false;
409 if( !E->inRadar )
410 {
411 if( !E->posLocked )
412 E->position = cb->GetUnitPos(enemy);
413 if( !TM->IsSectorValid(TM->GetSectorIndex(E->position)) )
414 EnemyRemove(enemy,E);
415 }
416 if( RAIDEBUGGING ) *l<<"#";
417 }
418
EnemyEnterRadar(int enemy)419 void cRAI::EnemyEnterRadar(int enemy)
420 {
421 if( RAIDEBUGGING ) *l<<"\nEnemyEnterRadar("<<enemy<<")";
422 if( cb->GetUnitPos(enemy).x <= 0 && cb->GetUnitPos(enemy).y <= 0 && cb->GetUnitPos(enemy).z <= 0 ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
423 {
424 DebugEnemyEnterRadarError++;
425 *l<<"\nWARNING: EnemyEnterRadar("<<enemy<<"): enemy position is invalid";
426 return;
427 }
428 DebugEnemyEnterRadar++;
429 if( Enemies.find(enemy) == Enemies.end() )
430 Enemies.insert(iePair(enemy,EnemyInfo()));
431 EnemyInfo *E = &Enemies.find(enemy)->second;
432 E->inRadar=true;
433 UM->EnemyEnterRadar(enemy,E);
434 if( RAIDEBUGGING ) *l<<"#";
435 }
436
EnemyLeaveRadar(int enemy)437 void cRAI::EnemyLeaveRadar(int enemy)
438 {
439 if( RAIDEBUGGING ) *l<<"\nEnemyLeaveRadar("<<enemy<<")";
440 if( Enemies.find(enemy) == Enemies.end() ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
441 {
442 DebugEnemyLeaveRadarError++;
443 *l<<"\nWARNING: EnemyLeaveRadar("<<enemy<<"): unknown unit id";
444 return;
445 }
446 EnemyInfo *E = &Enemies.find(enemy)->second;
447 if( !E->inRadar ) // Work Around: Spring-Version(v0.76b1)
448 {
449 DebugEnemyLeaveRadarError++;
450 *l<<"\nWARNING: EnemyLeaveRadar("<<enemy<<"): not in radar";
451 return;
452 }
453
454 DebugEnemyLeaveRadar++;
455 E->inRadar=false;
456 if( !E->inLOS )
457 {
458 if( !E->posLocked )
459 E->position = cb->GetUnitPos(enemy);
460 if( !TM->IsSectorValid(TM->GetSectorIndex(E->position)) )
461 EnemyRemove(enemy,E);
462 }
463 if( RAIDEBUGGING ) *l<<"#";
464 }
465
EnemyDestroyed(int enemy,int attacker)466 void cRAI::EnemyDestroyed(int enemy,int attacker)
467 {
468 if( RAIDEBUGGING ) *l<<"\nEnemyDestroyed("<<enemy<<","<<attacker<<")";
469 if( Enemies.find(enemy) == Enemies.end() ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
470 {
471 *l<<"\nWARNING: EnemyDestroyed("<<enemy<<","<<attacker<<"): unknown unit id";
472 return;
473 }
474
475 EnemyInfo *E = &Enemies.find(enemy)->second;
476 if( E->inLOS )
477 DebugEnemyDestroyedLOS++;
478 if( E->inRadar )
479 DebugEnemyDestroyedRadar++;
480 EnemyRemove(enemy,E);
481 if( RAIDEBUGGING ) *l<<"#";
482 }
483
EnemyRemove(int enemy,EnemyInfo * E)484 void cRAI::EnemyRemove(int enemy, EnemyInfo *E)
485 {
486 if( E->RS != 0 && E->RS->unitID == enemy )
487 {
488 E->RS->unitID=-1;
489 E->RS->unitUD=0;
490 E->RS->enemy = false;
491 }
492 if( E->baseThreatID != -1 )
493 EThreat.erase(enemy);
494 while( int(E->attackGroups.size()) > 0 )
495 UM->GroupRemoveEnemy(enemy,E,*E->attackGroups.begin());
496 Enemies.erase(enemy);
497 }
498
EnemyDamaged(int damaged,int attacker,float damage,float3 dir)499 void cRAI::EnemyDamaged(int damaged,int attacker,float damage,float3 dir)
500 {
501
502 }
503
UnitIdle(int unit)504 void cRAI::UnitIdle(int unit)
505 {
506 if( RAIDEBUGGING ) *l<<"\nUI("<<unit<<")";
507 if( Units.find(unit) == Units.end() ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
508 {
509 *l<<"\nWARNING: UnitIdle("<<unit<<"): unknown unit id";
510 return;
511 }
512 UnitInfo *U = &Units.find(unit)->second;
513 if( U->AIDisabled || cb->UnitBeingBuilt(unit) || cb->IsUnitParalyzed(unit) || int(cb->GetCurrentUnitCommands(unit)->size()) > 0 )
514 {
515 if( RAIDEBUGGING ) *l<<"#";
516 return;
517 }
518 U->humanCommand=false;
519 if( cb->GetCurrentFrame() <= U->lastUnitIdleFrame+15 ) // !! Occurs if enemy attack order fails/...possibly some other reason
520 {
521 UpdateEventAdd(1,cb->GetCurrentFrame()+15,unit,U);
522 if( RAIDEBUGGING ) *l<<"#";
523 return;
524 }
525 U->lastUnitIdleFrame=cb->GetCurrentFrame();
526 if( U->UE != 0 && U->UE->type == 1 )
527 UpdateEventRemove(U->UE);
528 if( U->inCombat )
529 {
530 CM->UnitIdle(unit,U);
531 if( RAIDEBUGGING ) *l<<"#";
532 return;
533 }
534 UM->UnitIdle(unit,U);
535 if( RAIDEBUGGING ) *l<<"#";
536 }
537
538
UnitDamaged(int unit,int attacker,float damage,float3 dir)539 void cRAI::UnitDamaged(int unit,int attacker,float damage,float3 dir)
540 {
541 if( RAIDEBUGGING ) *l<<"\nUnitDamaged("<<unit<<","<<attacker<<","<<damage<<",x"<<dir.x<<" z"<<dir.z<<" y"<<dir.y<<")";
542 if( cb->UnitBeingBuilt(unit) || cb->IsUnitParalyzed(unit) || cb->GetUnitHealth(unit) <= 0.0 )
543 {
544 if( RAIDEBUGGING ) *l<<"#";
545 return;
546 }
547
548 UnitInfo *U = &Units.find(unit)->second;
549 if( cb->GetCurrentFrame() <= U->lastUnitDamagedFrame+15 )
550 {
551 if( RAIDEBUGGING ) *l<<"#";
552 return;
553 }
554
555 U->lastUnitDamagedFrame=cb->GetCurrentFrame();
556
557 EnemyInfo *E=0;
558 if( Enemies.find(attacker) != Enemies.end() )
559 E = &Enemies.find(attacker)->second;
560 else
561 attacker = -1;
562
563 if( U->ud->speed==0 )
564 {
565 if( E != 0 )
566 {
567 E->baseThreatFrame=cb->GetCurrentFrame();
568 E->baseThreatID=unit;
569 if( EThreat.find(attacker) == EThreat.end() )
570 {
571 EThreat.insert(iepPair(attacker,E));
572 for( int i=0; i<UM->GroupSize; i++ )
573 if( int(UM->Group[i]->Enemies.size()) == 0 && !UM->Group[i]->Units.begin()->second->ud->canLoopbackAttack )
574 CM->GetClosestEnemy(cb->GetUnitPos(UM->Group[i]->Units.begin()->first),UM->Group[i]->Units.begin()->second);
575 }
576 }
577 ValidateUnitList(&U->UGuards);
578 for( map<int,UnitInfo*>::iterator i = U->UGuards.begin(); i != U->UGuards.end(); ++i )
579 {
580 if( int(i->second->URepair.size()) == 0 && !IsHumanControled(i->first,i->second) )
581 {
582 Command c;
583 c.id = CMD_REPAIR;
584 c.params.push_back(unit);
585 cb->GiveOrder(i->first, &c);
586 }
587 if( i->second->URepair.find(unit) == i->second->URepair.end() )
588 i->second->URepair.insert(iupPair(unit,U));
589 }
590 if( U->ud->canReclaim && attacker > -1 && U->udr->IsNano() && cb->GetUnitPos(unit).distance2D(cb->GetUnitPos(attacker)) < U->ud->buildDistance && !IsHumanControled(unit,U) )
591 {
592 if( cb->GetCurrentUnitCommands(unit)->size() == 0 || (cb->GetCurrentUnitCommands(unit)->front().id != CMD_REPAIR && cb->GetCurrentUnitCommands(unit)->front().id != CMD_RECLAIM) )
593 {
594 Command c;
595 c.id = CMD_RECLAIM;
596 c.params.push_back(attacker);
597 cb->GiveOrder(unit, &c);
598 }
599 }
600 }
601 if( U->AIDisabled || U->udrBL->task<=0 || U->ud->speed==0 )
602 {
603 if( RAIDEBUGGING ) *l<<"#";
604 return;
605 }
606
607 if( E != 0 && U->group != 0 && U->group->Enemies.find(attacker) == U->group->Enemies.end() )
608 {
609 if( E->baseThreatID != -1 || UM->ActiveAttackOrders() )
610 UM->GroupAddEnemy(attacker,E,U->group);
611 }
612
613 if( !IsHumanControled(unit,U) )
614 CM->UnitDamaged(unit,U,attacker,E,dir);
615
616 if( RAIDEBUGGING ) *l<<"#";
617 }
618
UnitMoveFailed(int unit)619 void cRAI::UnitMoveFailed(int unit)
620 {
621 if( RAIDEBUGGING ) *l<<"\nUnitMoveFailed("<<unit<<")";
622 if( UMobile.find(unit) == UMobile.end() ) // ! Work Around: Spring-Version(v0.72b1-0.76b1)
623 {
624 *l<<"\nWARNING: UnitMoveFailed("<<unit<<"): unknown unit id";
625 return;
626 }
627
628 UnitInfo *U = UMobile.find(unit)->second;
629 if( U->AIDisabled || cb->IsUnitParalyzed(unit) ||
630 B->UBuilderMoveFailed(unit,U) || UM->UnitMoveFailed(unit,U) ||
631 int(cb->GetCurrentUnitCommands(unit)->size()) > 0 )
632 {
633 if( RAIDEBUGGING ) *l<<"#";
634 return;
635 }
636
637 Command c;
638 c.id=CMD_WAIT;
639 //c.timeOut=cb->GetCurrentFrame()+120;
640 cb->GiveOrder(unit,&c);
641 UpdateEventAdd(1,cb->GetCurrentFrame()+90,unit,U);
642 if( RAIDEBUGGING ) *l<<"#";
643 }
644
HandleEvent(int msg,const void * data)645 int cRAI::HandleEvent(int msg,const void* data)
646 {
647 if( RAIDEBUGGING ) *l<<"\nHandleEvent("<<msg<<","<<"~"<<")";
648 switch (msg)
649 {
650 case AI_EVENT_UNITGIVEN:
651 case AI_EVENT_UNITCAPTURED:
652 {
653 const IGlobalAI::ChangeTeamEvent* cte = (const IGlobalAI::ChangeTeamEvent*) data;
654
655 const int myAllyTeamId = cb->GetMyAllyTeam();
656 const bool oldEnemy = !cb->IsAllied(myAllyTeamId, cb->GetTeamAllyTeam(cte->oldteam));
657 const bool newEnemy = !cb->IsAllied(myAllyTeamId, cb->GetTeamAllyTeam(cte->newteam));
658
659 if ( oldEnemy && !newEnemy ) {
660 {
661 if( Enemies.find(cte->unit) != Enemies.end() )
662 EnemyDestroyed(cte->unit,-1);
663 }
664 }
665 else if( !oldEnemy && newEnemy )
666 {
667 // unit changed from an ally to an enemy team
668 // we lost a friend! :(
669 EnemyCreated(cte->unit);
670 if (!cb->UnitBeingBuilt(cte->unit)) {
671 EnemyFinished(cte->unit);
672 }
673 }
674
675 if( cte->oldteam == cb->GetMyTeam() )
676 {
677 UnitDestroyed(cte->unit,-1);
678 }
679 else if( cte->newteam == cb->GetMyTeam() )
680 {
681 if( cb->GetUnitHealth(cte->unit) <= 0 ) // ! Work Around: Spring-Version(v0.74b1-0.75b2)
682 {
683 *l<<"\nERROR: HandleEvent(AI_EVENT_(UNITGIVEN|UNITCAPTURED)): given unit is dead or does not exist";
684 return 0;
685 }
686 UnitCreated(cte->unit, -1);
687 Units.find(cte->unit)->second.AIDisabled=false;
688 if( !cb->UnitBeingBuilt(cte->unit) )
689 {
690 UnitFinished(cte->unit);
691 UnitIdle(cte->unit);
692 }
693 }
694 }
695 break;
696 case AI_EVENT_PLAYER_COMMAND:
697 {
698 const IGlobalAI::PlayerCommandEvent* pce = (const IGlobalAI::PlayerCommandEvent*) data;
699 bool ImportantCommand=false;
700 if( pce->command.id < 0 )
701 ImportantCommand = true;
702 switch( pce->command.id )
703 {
704 case CMD_MOVE:
705 case CMD_PATROL:
706 case CMD_FIGHT:
707 case CMD_ATTACK:
708 case CMD_AREA_ATTACK:
709 case CMD_GUARD:
710 case CMD_REPAIR:
711 case CMD_LOAD_UNITS:
712 case CMD_UNLOAD_UNITS:
713 case CMD_UNLOAD_UNIT:
714 case CMD_RECLAIM:
715 case CMD_DGUN:
716 case CMD_RESTORE:
717 case CMD_RESURRECT:
718 case CMD_CAPTURE:
719 ImportantCommand = true;
720 }
721
722 for( int i=0; i<int(pce->units.size()); i++ )
723 {
724 if( Units.find(pce->units.at(i)) == Units.end() ) // ! Work Around: Spring-Version(v0.75b2)
725 {
726 *l<<"\nERROR: HandleEvent(AI_EVENT_PLAYER_COMMAND): unknown unit id="<<pce->units.at(i);
727 // pce->units.erase(pce->units.begin()+i);
728 // i--;
729 }
730 else if( ImportantCommand )
731 Units.find(pce->units.at(i))->second.humanCommand = true;
732 }
733 if( ImportantCommand )
734 {
735 B->HandleEvent(pce);
736 }
737 else if( pce->command.id == CMD_SELFD )
738 {
739 for( vector<int>::const_iterator i=pce->units.begin(); i!=pce->units.end(); ++i )
740 UnitDestroyed(*i,-1);
741 }
742 }
743 break;
744 }
745 if( RAIDEBUGGING ) *l<<"#";
746 return 0;
747 }
748
Update()749 void cRAI::Update()
750 {
751 frame=cb->GetCurrentFrame();
752 if(frame%FUPDATE_MINIMAL)
753 return;
754
755 if( RAIDEBUGGING ) *l<<"\nUpdate("<<frame<<")";
756
757 if(!(frame%FUPDATE_POWER))
758 { // Old Code, ensures a unit won't just go permanently idle, hopefully unnecessary in the future
759 ValidateAllUnits();
760 for(map<int,UnitInfo>::iterator iU=Units.begin(); iU!=Units.end(); ++iU)
761 if( !cb->UnitBeingBuilt(iU->first) && !iU->second.AIDisabled && iU->second.udrBL->task > 1 &&
762 frame > iU->second.lastUnitIdleFrame+FUPDATE_UNITS && iU->second.UE == 0 && cb->GetCurrentUnitCommands(iU->first)->size() == 0 )
763 {
764 // *l<<"\nWARNING: Unit was Idle Name="<<iU->second.ud->humanName<<"("<<iU->first<<")";
765 UnitIdle(iU->first);
766 }
767 }
768
769 while( eventSize > 0 && eventList[0]->frame <= frame )
770 {
771 switch( eventList[0]->type )
772 {
773 case 1: // Checks for idleness
774 // *l<<"\n(u1)";
775 if( ValidateUnit(eventList[0]->unitID) ) // if false, the event will be removed elsewhere
776 {
777 if( !IsHumanControled(eventList[0]->unitID,eventList[0]->unitI) )
778 {
779 // *l<<" Stopping "<<eventList[0]->unitI->ud->humanName<<"("<<eventList[0]->unitID<<")";
780 eventList[0]->unitI->lastUnitIdleFrame = -1;
781 if( eventList[0]->unitI->ud->speed == 0 )
782 {
783 int unit = eventList[0]->unitID;
784 UpdateEventRemove(eventList[0]);
785 UnitIdle(unit);
786 }
787 else
788 { // doesn't seem to work for factories Spring-Version(v0.76b1)
789 Command c;
790 c.id=CMD_STOP;
791 cb->GiveOrder(eventList[0]->unitID, &c);
792 UpdateEventRemove(eventList[0]);
793 }
794 }
795 else
796 {
797 eventList[0]->frame = frame;
798 UpdateEventReorderFirst();
799 }
800 }
801 break;
802 case 2: // Few Unit Monitoring
803 // *l<<"\n(u2)";
804 if( ValidateUnit(eventList[0]->unitID) ) // if false, the event will be removed elsewhere
805 {
806 if( !eventList[0]->unitI->inCombat &&
807 !IsHumanControled(eventList[0]->unitID,eventList[0]->unitI) &&
808 eventList[0]->unitI->BuildQ != 0 &&
809 cb->GetCurrentUnitCommands(eventList[0]->unitID)->front().id < 0 )
810 {
811 if( eventList[0]->lastPosition == 0 )
812 eventList[0]->lastPosition = new float3;
813 float3 position = cb->GetUnitPos(eventList[0]->unitID);
814 if( eventList[0]->unitI->BuildQ->creationID.size() > 0 )
815 {
816 eventList[0]->lastPosition->x = -1.0;
817 float3 conPosition = cb->GetUnitPos(eventList[0]->unitI->BuildQ->creationID.front());
818 if( abs(int(position.x-conPosition.x)) + 4.0 < 8.0*eventList[0]->unitI->ud->xsize/2.0 + 8.0*eventList[0]->unitI->BuildQ->creationUD->ud->xsize/2.0 &&
819 abs(int(position.z-conPosition.z)) + 4.0 < 8.0*eventList[0]->unitI->ud->zsize/2.0 + 8.0*eventList[0]->unitI->BuildQ->creationUD->ud->zsize/2.0 )
820 { // most likely, the commander built something on top of himself
821 Command c;
822 c.id = CMD_RECLAIM;
823 c.params.push_back(eventList[0]->unitI->BuildQ->creationID.front());
824 cb->GiveOrder(eventList[0]->unitID,&c);
825 if( B->BP->NeedResourceSite(eventList[0]->unitI->BuildQ->creationUD->ud) )
826 {
827 c.params.clear();
828 c.id = CMD_MOVE;
829 c.options = SHIFT_KEY;
830 float3 newPos;
831 if( position.x < conPosition.x )
832 newPos.x = (position.x - ((position.x-conPosition.x)/position.distance2D(conPosition))*(8.0*eventList[0]->unitI->ud->xsize/2.0 +8.0*eventList[0]->unitI->BuildQ->creationUD->ud->xsize/2.0) );
833 else
834 newPos.x = (position.x + ((position.x-conPosition.x)/position.distance2D(conPosition))*(8.0*eventList[0]->unitI->ud->xsize/2.0 +8.0*eventList[0]->unitI->BuildQ->creationUD->ud->xsize/2.0) );
835 if( position.z < conPosition.z )
836 newPos.z = (position.z - ((position.z-conPosition.z)/position.distance2D(conPosition))*(8.0*eventList[0]->unitI->ud->zsize/2.0 +8.0*eventList[0]->unitI->BuildQ->creationUD->ud->zsize/2.0) );
837 else
838 newPos.z = (position.z + ((position.z-conPosition.z)/position.distance2D(conPosition))*(8.0*eventList[0]->unitI->ud->zsize/2.0 +8.0*eventList[0]->unitI->BuildQ->creationUD->ud->zsize/2.0) );
839 CorrectPosition(newPos);
840 c.params.push_back(newPos.x);
841 c.params.push_back(newPos.y);
842 c.params.push_back(newPos.z);
843 cb->GiveOrder(eventList[0]->unitID,&c);
844 }
845 }
846 }
847 else if( position == *eventList[0]->lastPosition )
848 { // most likely, the commander is stuck at the starting point
849 // if( eventList[0]->unitI->area == 0 && TM->udMobileType.find(eventList[0]->unitI->ud->id)->second != 0 )
850 // {} // trapped forever
851 eventList[0]->unitI->lastUnitIdleFrame = -1;
852 Command c;
853 c.id = CMD_MOVE;
854 float f = (40.0+(rand()%401)/10.0);
855 float3 newPos = position;
856 if( rand()%2 == 0 )
857 newPos.x += f;
858 else
859 newPos.x -= f;
860 f = (40.0+(rand()%401)/10.0);
861 if( rand()%2 == 0 )
862 newPos.z += f;
863 else
864 newPos.z -= f;
865
866 CorrectPosition(newPos);
867 c.params.push_back(newPos.x);
868 c.params.push_back(newPos.y);
869 c.params.push_back(newPos.z);
870 cb->GiveOrder(eventList[0]->unitID,&c);
871 *eventList[0]->lastPosition = position;
872 }
873 else
874 *eventList[0]->lastPosition = position;
875 }
876 if( Units.size() >= 10 )
877 {
878 if( eventList[0]->lastPosition != 0 )
879 delete eventList[0]->lastPosition;
880 UpdateEventRemove(eventList[0]);
881 }
882 else
883 UpdateEventReorderFirst();
884 }
885 break;
886 case 3: // Initiatization
887 // *l<<"\n(u3)";
888 if( frame >= 210 || ( cb->GetMetalIncome()>0 && cb->GetMetalIncome()<0.9*cb->GetMetalStorage() ) || ( cb->GetEnergyIncome()>0 && cb->GetEnergyIncome()<0.9*cb->GetEnergyStorage() ) )
889 {
890 *l<<"\nInitiated=true Frame="<<frame<<" Metal-Income="<<cb->GetMetalIncome()<<" Energy-Income="<<cb->GetEnergyIncome()<<"\n";
891 UpdateEventRemove(eventList[0]);
892 B->UpdateUDRCost();
893 for(map<int,UnitInfo>::iterator i=Units.begin(); i!=Units.end(); ++i )
894 if( !i->second.AIDisabled )
895 {
896 if( Units.size() < 10 && i->second.ud->movedata != 0 )
897 UpdateEventAdd(2,frame+FUPDATE_MINIMAL,i->first,&i->second);
898 if( cb->GetCurrentUnitCommands(i->first)->size() == 0 )
899 UnitIdle(i->first);
900 }
901 B->bInitiated=true;
902 }
903 else
904 UpdateEventReorderFirst();
905 break;
906 default:
907 *l<<"(ERROR)";
908 UpdateEventRemove(eventList[0]);
909 break;
910 }
911 }
912
913 if(!(frame%FUPDATE_POWER))
914 {
915 B->PM->Update();
916 SWM->Update();
917 if(!(frame%FUPDATE_BUILDLIST))
918 B->UpdateUDRCost();
919 }
920 if( RAIDEBUGGING ) *l<<"#";
921 }
922
UpdateEventAdd(const int & eventType,const int & eventFrame,int uID,UnitInfo * uI)923 void cRAI::UpdateEventAdd(const int &eventType, const int &eventFrame, int uID, UnitInfo* uI)
924 {
925 if( eventSize == 10000 )
926 {
927 *l<<"\nERROR: Event Maximum Reached.";
928 return;
929 }
930
931 UpdateEvent* e = new UpdateEvent;
932 if( uI != 0 )
933 {
934 if( uI->UE != 0 ) // The unit already has an event
935 {
936 if( eventType >= uI->UE->type ) // more or equally important
937 UpdateEventRemove(uI->UE); // remove the old
938 else
939 {
940 delete e;
941 return;
942 }
943 }
944 uI->UE = e;
945 }
946
947 e->type = eventType;
948 e->frame = eventFrame;
949 e->unitID = uID;
950 e->unitI = uI;
951 e->lastPosition = 0;
952
953 for(e->index = eventSize; e->index>0; e->index-- )
954 if( e->frame < eventList[e->index-1]->frame )
955 {
956 eventList[e->index] = eventList[e->index-1];
957 eventList[e->index]->index = e->index;
958 }
959 else
960 break;
961
962 eventList[e->index] = e;
963 eventSize++;
964 }
965
UpdateEventReorderFirst()966 void cRAI::UpdateEventReorderFirst()
967 {
968 UpdateEvent* e = eventList[0];
969 e->frame += FUPDATE_MINIMAL;
970 while( e->index < eventSize-1 && eventList[e->index+1]->frame < e->frame )
971 {
972 eventList[e->index] = eventList[e->index+1];
973 eventList[e->index]->index = e->index;
974 e->index++;
975 }
976 eventList[e->index] = e;
977 }
978
UpdateEventRemove(UpdateEvent * e)979 void cRAI::UpdateEventRemove(UpdateEvent* e)
980 {
981 if( e->unitI != 0 )
982 e->unitI->UE = 0;
983 eventSize--;
984 while( e->index < eventSize )
985 {
986 eventList[e->index] = eventList[e->index+1];
987 eventList[e->index]->index = e->index;
988 e->index++;
989 }
990 delete e;
991 }
992
CorrectPosition(float3 & position)993 void cRAI::CorrectPosition(float3& position)
994 {
995 if( position.x < 1 )
996 position.x = 1;
997 else if( position.x > 8*cb->GetMapWidth()-2 )
998 position.x = 8*cb->GetMapWidth()-2;
999 if( position.z < 1 )
1000 position.z = 1;
1001 else if( position.z > 8*cb->GetMapHeight()-2 )
1002 position.z = 8*cb->GetMapHeight()-2;
1003 position.y = cb->GetElevation(position.x,position.z);
1004 }
1005
GetCurrentMapArea(sRAIUnitDef * udr,float3 & position)1006 TerrainMapArea* cRAI::GetCurrentMapArea(sRAIUnitDef* udr, float3& position)
1007 {
1008 if( udr->mobileType == 0 ) // flying units & buildings
1009 return 0;
1010
1011 // other mobile units & their factories
1012 int iS = TM->GetSectorIndex(position);
1013 if( !TM->IsSectorValid(iS) )
1014 {
1015 CorrectPosition(position);
1016 iS = TM->GetSectorIndex(position);
1017 }
1018 return udr->mobileType->sector[iS].area;
1019 }
1020
GetRandomPosition(TerrainMapArea * area)1021 float3 cRAI::GetRandomPosition(TerrainMapArea* area)
1022 {
1023 float3 Pos;
1024 if( area == 0 )
1025 {
1026 Pos.x=1.0 + rand()%7 + 8.0*(rand()%cb->GetMapWidth());
1027 Pos.z=1.0 + rand()%7 + 8.0*(rand()%cb->GetMapHeight());
1028 CorrectPosition(Pos);
1029 return Pos;
1030 }
1031
1032 vector<int> Temp;
1033 for( map<int,TerrainMapAreaSector*>::iterator iS=area->sector.begin(); iS!=area->sector.end(); ++iS )
1034 Temp.push_back(iS->first);
1035 int iS=Temp.at(rand()%int(Temp.size()));
1036 Pos.x=TM->sector[iS].position.x - TM->convertStoP/2-1.0 + rand()%(TM->convertStoP-1);
1037 Pos.z=TM->sector[iS].position.z - TM->convertStoP/2-1.0 + rand()%(TM->convertStoP-1);
1038 CorrectPosition(Pos);
1039 return Pos;
1040 }
1041
IsHumanControled(const int & unit,UnitInfo * U)1042 bool cRAI::IsHumanControled(const int& unit,UnitInfo* U)
1043 {
1044 if( int(cb->GetCurrentUnitCommands(unit)->size()) == 0 )
1045 return false;
1046 if( U->humanCommand )
1047 return true;
1048 return false;
1049 }
1050
ValidateUnit(const int & unitID)1051 bool cRAI::ValidateUnit(const int& unitID)
1052 {
1053 if( cb->GetUnitDef(unitID) == 0 ) // ! Work Around: Spring-Version(v0.74b1-0.75b2)
1054 {
1055 *l<<"\nERROR: ValidateUnit(): iU->first="<<unitID;
1056 UnitDestroyed(unitID,-1);
1057 return false;
1058 }
1059 return true;
1060 }
1061
ValidateUnitList(map<int,UnitInfo * > * UL)1062 bool cRAI::ValidateUnitList(map<int,UnitInfo*>* UL)
1063 {
1064 int ULsize = UL->size();
1065 for(map<int,UnitInfo*>::iterator iU=UL->begin(); iU!=UL->end(); ++iU)
1066 {
1067 if( !ValidateUnit(iU->first) )
1068 {
1069 // The iterator has becomes invalid at this point
1070 if( ULsize == 1 ) // if true then the list is now empty, and may have been deleted (UL is invalid)
1071 return false;
1072 else
1073 return ValidateUnitList(UL);
1074 }
1075 }
1076 return true;
1077 }
1078
ValidateAllUnits()1079 void cRAI::ValidateAllUnits()
1080 {
1081 for(map<int,UnitInfo>::iterator iU=Units.begin(); iU!=Units.end(); ++iU)
1082 {
1083 if( !ValidateUnit(iU->first) )
1084 {
1085 // The iterator has becomes invalid at this point
1086 ValidateAllUnits();
1087 return;
1088 }
1089 }
1090 }
1091
DebugDrawLine(float3 StartPos,float distance,int direction,float xposoffset,float zposoffset,float yposoffset,int lifetime,int arrow,float width,int group)1092 void cRAI::DebugDrawLine(float3 StartPos, float distance, int direction, float xposoffset, float zposoffset, float yposoffset, int lifetime, int arrow, float width, int group)
1093 {
1094 StartPos.x+=xposoffset;
1095 StartPos.z+=zposoffset;
1096 StartPos.y+=yposoffset;
1097 float3 EndPos=StartPos;
1098 switch( direction )
1099 {
1100 case 0:
1101 EndPos.x+=distance;
1102 break;
1103 case 1:
1104 EndPos.z+=distance;
1105 break;
1106 case 2:
1107 EndPos.x-=distance;
1108 break;
1109 case 3:
1110 EndPos.z-=distance;
1111 break;
1112 }
1113 cb->CreateLineFigure(StartPos, EndPos, width, arrow, lifetime, group);
1114 }
1115
DebugDrawShape(float3 centerPos,float lineLength,float width,int arrow,float yPosOffset,int lifeTime,int sides,int group)1116 void cRAI::DebugDrawShape(float3 centerPos, float lineLength, float width, int arrow, float yPosOffset, int lifeTime, int sides, int group)
1117 {
1118 DebugDrawLine(centerPos, lineLength, 0, -lineLength/2, lineLength/2, yPosOffset, lifeTime, arrow, width, group);
1119 DebugDrawLine(centerPos, lineLength, 1, -lineLength/2, -lineLength/2, yPosOffset, lifeTime, arrow, width, group);
1120 DebugDrawLine(centerPos, lineLength, 2, lineLength/2, -lineLength/2, yPosOffset, lifeTime, arrow, width, group);
1121 DebugDrawLine(centerPos, lineLength, 3, lineLength/2, lineLength/2, yPosOffset, lifeTime, arrow, width, group);
1122 }
1123
LocateFile(IAICallback * cb,const string & relFileName,string & absFileName,bool forWriting)1124 bool cRAI::LocateFile(IAICallback* cb, const string& relFileName, string& absFileName, bool forWriting)
1125 {
1126 int action = forWriting ? AIVAL_LOCATE_FILE_W : AIVAL_LOCATE_FILE_R;
1127
1128 char absFN[2048];
1129 STRCPY_T(absFN, sizeof(absFN), relFileName.c_str());
1130 const bool located = cb->GetValue(action, absFN);
1131
1132 if (located) {
1133 absFileName = absFN;
1134 } else {
1135 absFileName = "";
1136 }
1137
1138 return located;
1139 }
1140
IsFSGoodChar(const char c)1141 static bool IsFSGoodChar(const char c) {
1142
1143 if ((c >= '0') && (c <= '9')) {
1144 return true;
1145 } else if ((c >= 'a') && (c <= 'z')) {
1146 return true;
1147 } else if ((c >= 'A') && (c <= 'Z')) {
1148 return true;
1149 } else if ((c == '.') || (c == '_') || (c == '-')) {
1150 return true;
1151 }
1152
1153 return false;
1154 }
MakeFileSystemCompatible(const std::string & str)1155 std::string cRAI::MakeFileSystemCompatible(const std::string& str) {
1156
1157 std::string cleaned = str;
1158
1159 for (std::string::size_type i=0; i < cleaned.size(); i++) {
1160 if (!IsFSGoodChar(cleaned[i])) {
1161 cleaned[i] = '_';
1162 }
1163 }
1164
1165 return cleaned;
1166 }
1167
RemoveLogFile(string relFileName) const1168 void cRAI::RemoveLogFile(string relFileName) const {
1169
1170 string absFileName;
1171 if (cRAI::LocateFile(cb, relFileName, absFileName, true)) {
1172 remove(absFileName.c_str());
1173 }
1174 }
1175
GetLogFileSubPath(int teamId) const1176 std::string cRAI::GetLogFileSubPath(int teamId) const {
1177
1178 static const size_t logFileSubPath_sizeMax = 64;
1179 char logFileSubPath[logFileSubPath_sizeMax];
1180 SNPRINTF(logFileSubPath, logFileSubPath_sizeMax, "log/RAI%i_LastGame.log", teamId);
1181 return std::string(logFileSubPath);
1182 }
1183
ClearLogFiles()1184 void cRAI::ClearLogFiles()
1185 {
1186 for( int t=0; t < 255; t++ )
1187 {
1188 RemoveLogFile(GetLogFileSubPath(t));
1189 }
1190
1191 RemoveLogFile("log/RAIGlobal_LastGame.log");
1192 RemoveLogFile("log/TerrainMapDebug.log");
1193 // RemoveLogFile("log/PathfinderDebug.log");
1194 // RemoveLogFile("log/PathFinderAPNDebug.log");
1195 // RemoveLogFile("log/PathFinderNPNDebug.log");
1196 // RemoveLogFile("log/Prerequisite.log");
1197 // RemoveLogFile("log/Debug.log");
1198 }
1199