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