1 // -------------------------------------------------------------------------
2 // AAI
3 //
4 // A skirmish AI for the Spring engine.
5 // Copyright Alexander Seizinger
6 //
7 // Released under GPL license: see LICENSE.html for more information.
8 // -------------------------------------------------------------------------
9 
10 #include "AAIMap.h"
11 #include "AAI.h"
12 #include "AAIBuildTable.h"
13 #include "AAIBrain.h"
14 #include "AAIConfig.h"
15 #include "AAISector.h"
16 
17 #include "System/Util.h"
18 #include "LegacyCpp/UnitDef.h"
19 using namespace springLegacyAI;
20 
21 #define MAP_CACHE_PATH "cache/"
22 
23 int AAIMap::xSize;
24 int AAIMap::ySize;
25 int AAIMap::xMapSize;
26 int AAIMap::yMapSize;
27 int AAIMap::losMapRes;
28 int AAIMap::xLOSMapSize;
29 int AAIMap::yLOSMapSize;
30 int AAIMap::xDefMapSize;
31 int AAIMap::yDefMapSize;
32 int AAIMap::xContMapSize;
33 int AAIMap::yContMapSize;
34 int AAIMap::xSectors;
35 int AAIMap::ySectors;
36 int AAIMap::xSectorSize;
37 int AAIMap::ySectorSize;
38 int AAIMap::xSectorSizeMap;
39 int AAIMap::ySectorSizeMap;
40 
41 list<AAIMetalSpot>  AAIMap::metal_spots;
42 
43 int AAIMap::land_metal_spots;
44 int AAIMap::water_metal_spots;
45 
46 float AAIMap::land_ratio;
47 float AAIMap::flat_land_ratio;
48 float AAIMap::water_ratio;
49 
50 bool AAIMap::metalMap;
51 MapType AAIMap::map_type;
52 
53 vector< vector<int> > AAIMap::team_sector_map;
54 vector<int> AAIMap::buildmap;
55 vector<int> AAIMap::blockmap;
56 vector<float> AAIMap::plateau_map;
57 vector<int> AAIMap::continent_map;
58 
59 vector<AAIContinent> AAIMap::continents;
60 int AAIMap::land_continents;
61 int AAIMap::water_continents;
62 int AAIMap::avg_land_continent_size;
63 int AAIMap::avg_water_continent_size;
64 int AAIMap::max_land_continent_size;
65 int AAIMap::max_water_continent_size;
66 int AAIMap::min_land_continent_size;
67 int AAIMap::min_water_continent_size;
68 list<UnitCategory> AAIMap::map_categories;
69 list<int> AAIMap::map_categories_id;
70 
AAIMap(AAI * ai)71 AAIMap::AAIMap(AAI *ai)
72 {
73 	this->ai = ai;
74 	initialized = false;
75 }
76 
~AAIMap(void)77 AAIMap::~AAIMap(void)
78 {
79 	// delete common data only if last aai instace has gone
80 	if(ai->GetInstances() == 0)
81 	{
82 		Learn();
83 
84 		const std::string mapLearn_filename = LocateMapLearnFile();
85 
86 		// save map data
87 		FILE *save_file = fopen(mapLearn_filename.c_str(), "w+");
88 
89 		fprintf(save_file, "%s \n", MAP_LEARN_VERSION);
90 
91 		for(int y = 0; y < ySectors; y++)
92 		{
93 			for(int x = 0; x < xSectors; x++)
94 			{
95 				// save sector data
96 				fprintf(save_file, "%f %f %f", sector[x][y].flat_ratio, sector[x][y].water_ratio, sector[x][y].importance_this_game);
97 				// save combat data
98 				for(size_t cat = 0; cat < ai->Getbt()->assault_categories.size(); ++cat)
99 					fprintf(save_file, "%f %f ", sector[x][y].attacked_by_this_game[cat], sector[x][y].combats_this_game[cat]);
100 			}
101 
102 			fprintf(save_file, "\n");
103 		}
104 
105 		fclose(save_file);
106 
107 		buildmap.clear();
108 		blockmap.clear();
109 		plateau_map.clear();
110 		continent_map.clear();
111 	}
112 
113 	defence_map.clear();
114 	air_defence_map.clear();
115 	submarine_defence_map.clear();
116 
117 	scout_map.clear();
118 	last_updated_map.clear();
119 
120 	sector_in_los.clear();
121 	sector_in_los_with_enemies.clear();
122 
123 	units_in_los.clear();
124 	enemy_combat_units_spotted.clear();
125 }
126 
Init()127 void AAIMap::Init()
128 {
129 	// all static vars are only initialized by the first aai instance
130 	if(ai->GetInstances() == 1)
131 	{
132 		// get size
133 		xMapSize = ai->Getcb()->GetMapWidth();
134 		yMapSize = ai->Getcb()->GetMapHeight();
135 
136 		xSize = xMapSize * SQUARE_SIZE;
137 		ySize = yMapSize * SQUARE_SIZE;
138 
139 		losMapRes = ai->Getcb()->GetLosMapResolution();
140 		xLOSMapSize = xMapSize / losMapRes;
141 		yLOSMapSize = yMapSize / losMapRes;
142 
143 		xDefMapSize = xMapSize / 4;
144 		yDefMapSize = yMapSize / 4;
145 
146 		xContMapSize = xMapSize / 4;
147 		yContMapSize = yMapSize / 4;
148 
149 		// calculate number of sectors
150 		xSectors = floor(0.5f + ((float) xMapSize)/cfg->SECTOR_SIZE);
151 		ySectors = floor(0.5f + ((float) yMapSize)/cfg->SECTOR_SIZE);
152 
153 		// calculate effective sector size
154 		xSectorSizeMap = floor( ((float) xMapSize) / ((float) xSectors) );
155 		ySectorSizeMap = floor( ((float) yMapSize) / ((float) ySectors) );
156 
157 		xSectorSize = 8 * xSectorSizeMap;
158 		ySectorSize = 8 * ySectorSizeMap;
159 
160 		buildmap.resize(xMapSize*yMapSize, 0);
161 		blockmap.resize(xMapSize*yMapSize, 0);
162 		continent_map.resize(xContMapSize*yContMapSize, -1);
163 		plateau_map.resize(xContMapSize*yContMapSize, 0);
164 
165 		// create map that stores which aai player has occupied which sector (visible to all aai players)
166 		team_sector_map.resize(xSectors);
167 
168 		for(int x = 0; x < xSectors; ++x)
169 			team_sector_map[x].resize(ySectors, -1);
170 
171 		ReadContinentFile();
172 
173 		ReadMapCacheFile();
174 	}
175 
176 	// create field of sectors
177 	sector.resize(xSectors);
178 
179 	for(int x = 0; x < xSectors; ++x)
180 		sector[x].resize(ySectors);
181 
182 	for(int j = 0; j < ySectors; ++j)
183 	{
184 		for(int i = 0; i < xSectors; ++i)
185 			// provide ai callback to sectors & set coordinates of the sectors
186 			sector[i][j].Init(ai, i, j, xSectorSize*i, xSectorSize*(i+1), ySectorSize * j, ySectorSize * (j+1));
187 	}
188 
189 	// add metalspots to their sectors
190 	int k, l;
191 	for(list<AAIMetalSpot>::iterator spot = metal_spots.begin(); spot != metal_spots.end(); ++spot)
192 	{
193 		k = spot->pos.x/xSectorSize;
194 		l = spot->pos.z/ySectorSize;
195 
196 		if(k < xSectors && l < ySectors)
197 			sector[k][l].AddMetalSpot(&(*spot));
198 	}
199 
200 	ReadMapLearnFile(true);
201 
202 	// for scouting
203 	scout_map.resize(xLOSMapSize*yLOSMapSize, 0);
204 	last_updated_map.resize(xLOSMapSize*yLOSMapSize, 0);
205 
206 	sector_in_los.resize( (xSectors+1) * (ySectors+1) );
207 	sector_in_los_with_enemies.resize( (xSectors+1) * (ySectors+1) );
208 
209 	units_in_los.resize(cfg->MAX_UNITS, 0);
210 
211 	enemy_combat_units_spotted.resize(AAIBuildTable::ass_categories, 0);
212 
213 	// create defence
214 	defence_map.resize(xDefMapSize*yDefMapSize, 0);
215 	air_defence_map.resize(xDefMapSize*yDefMapSize, 0);
216 	submarine_defence_map.resize(xDefMapSize*yDefMapSize, 0);
217 
218 	initialized = true;
219 
220 	// for log file
221 	ai->Log("Map: %s\n",ai->Getcb()->GetMapName());
222 	ai->Log("Maptype: %s\n", GetMapTypeTextString(map_type));
223 	ai->Log("Mapsize is %i x %i\n", ai->Getcb()->GetMapWidth(),ai->Getcb()->GetMapHeight());
224 	ai->Log("%i sectors in x direction\n", xSectors);
225 	ai->Log("%i sectors in y direction\n", ySectors);
226 	ai->Log("x-sectorsize is %i (Map %i)\n", xSectorSize, xSectorSizeMap);
227 	ai->Log("y-sectorsize is %i (Map %i)\n", ySectorSize, ySectorSizeMap);
228 	ai->Log( _STPF_ " metal spots found (%i are on land, %i under water) \n \n", metal_spots.size(), land_metal_spots, water_metal_spots);
229 	ai->Log( _STPF_ " continents found on map\n", continents.size());
230 	ai->Log("%i land and %i water continents\n", land_continents, water_continents);
231 	ai->Log("Average land continent size is %i\n", avg_land_continent_size);
232 	ai->Log("Average water continent size is %i\n", avg_water_continent_size);
233 
234 	//debug
235 	/*float3 my_pos;
236 	for(int x = 0; x < xMapSize; x+=2)
237 	{
238 		for(int y = 0; y < yMapSize; y+=2)
239 		{
240 			if(buildmap[x + y*xMapSize] == 1 || buildmap[x + y*xMapSize] == 5)
241 			{
242 				my_pos.x = x * 8;
243 				my_pos.z = y * 8;
244 				my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
245 				ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 8000, ai->Getcb()->GetMyAllyTeam(), true, true);
246 			}
247 		}
248 	}*/
249 }
250 
ReadMapCacheFile()251 void AAIMap::ReadMapCacheFile()
252 {
253 	// try to read cache file
254 	bool loaded = false;
255 
256 	const size_t buffer_sizeMax = 512;
257 	char buffer[buffer_sizeMax];
258 
259 	const std::string mapCache_filename = LocateMapCacheFile();
260 
261 	FILE *file;
262 
263 	if((file = fopen(mapCache_filename.c_str(), "r")) != NULL)
264 	{
265 		// check if correct version
266 		fscanf(file, "%s ", buffer);
267 
268 		if(strcmp(buffer, MAP_CACHE_VERSION))
269 		{
270 			ai->LogConsole("Mapcache out of date - creating new one");
271 			fclose(file);
272 		}
273 		else
274 		{
275 			int temp;
276 			float temp_float;
277 
278 			// load if its a metal map
279 			fscanf(file, "%i ", &temp);
280 			metalMap = (bool)temp;
281 
282 			// load map type
283 			fscanf(file, "%s ", buffer);
284 
285 			if(!strcmp(buffer, "LAND_MAP"))
286 				map_type = LAND_MAP;
287 			else if(!strcmp(buffer, "LAND_WATER_MAP"))
288 				map_type = LAND_WATER_MAP;
289 			else if(!strcmp(buffer, "WATER_MAP"))
290 				map_type = WATER_MAP;
291 			else
292 				map_type = UNKNOWN_MAP;
293 
294 			ai->LogConsole("%s detected", GetMapTypeTextString(map_type));
295 
296 			// load water ratio
297 			fscanf(file, "%f ", &water_ratio);
298 
299 			// load buildmap
300 			for(int i = 0; i < xMapSize*yMapSize; ++i)
301 			{
302 				fscanf(file, "%i ", &temp);
303 				buildmap[i] = temp;
304 			}
305 
306 			// load plateau map
307 			for(int i = 0; i < xMapSize*yMapSize/16; ++i)
308 			{
309 				fscanf(file, "%f ", &temp_float);
310 				plateau_map[i] = temp_float;
311 			}
312 
313 			// load mex spots
314 			AAIMetalSpot spot;
315 			fscanf(file, "%i ", &temp);
316 
317 			for(int i = 0; i < temp; ++i)
318 			{
319 				fscanf(file, "%f %f %f %f ", &(spot.pos.x), &(spot.pos.y), &(spot.pos.z), &(spot.amount));
320 				spot.occupied = false;
321 				metal_spots.push_back(spot);
322 			}
323 
324 			fscanf(file, "%i %i ", &land_metal_spots, &water_metal_spots);
325 
326 			fclose(file);
327 
328 			ai->Log("Map cache file successfully loaded\n");
329 
330 			loaded = true;
331 		}
332 	}
333 
334 	if(!loaded)  // create new map data
335 	{
336 		// look for metalspots
337 		SearchMetalSpots();
338 
339 		CalculateWaterRatio();
340 
341 		// detect cliffs/water and create plateau map
342 		AnalyseMap();
343 
344 		DetectMapType();
345 
346 		//////////////////////////////////////////////////////////////////////////////////////////////////////
347 		// save mod independent map data
348 		const std::string mapCache_filename = LocateMapCacheFile();
349 
350 		file = fopen(mapCache_filename.c_str(), "w+");
351 
352 		fprintf(file, "%s\n", MAP_CACHE_VERSION);
353 
354 		// save if its a metal map
355 		fprintf(file, "%i\n", (int)metalMap);
356 
357 		const char *temp_buffer = GetMapTypeString(map_type);
358 
359 		// save map type
360 		fprintf(file, "%s\n", temp_buffer);
361 
362 		// save water ratio
363 		fprintf(file, "%f\n", water_ratio);
364 
365 		// save buildmap
366 		for(int i = 0; i < xMapSize*yMapSize; ++i)
367 			fprintf(file, "%i ", buildmap[i]);
368 
369 		fprintf(file, "\n");
370 
371 		// save plateau map
372 		for(int i = 0; i < xMapSize*yMapSize/16; ++i)
373 			fprintf(file, "%f ", plateau_map[i]);
374 
375 		// save mex spots
376 		land_metal_spots = 0;
377 		water_metal_spots = 0;
378 
379 		fprintf(file, "\n" _STPF_ " \n", metal_spots.size());
380 
381 		for(list<AAIMetalSpot>::iterator spot = metal_spots.begin(); spot != metal_spots.end(); ++spot)
382 		{
383 			fprintf(file, "%f %f %f %f \n", spot->pos.x, spot->pos.y, spot->pos.z, spot->amount);
384 
385 			if(spot->pos.y >= 0)
386 				++land_metal_spots;
387 			else
388 				++water_metal_spots;
389 		}
390 
391 		fprintf(file, "%i %i\n", land_metal_spots, water_metal_spots);
392 
393 		fclose(file);
394 
395 		ai->Log("New map cache-file created\n");
396 	}
397 
398 
399 	// determine important unit categories on this map
400 	if(cfg->AIR_ONLY_MOD)
401 	{
402 		map_categories.push_back(GROUND_ASSAULT);
403 		map_categories.push_back(AIR_ASSAULT);
404 		map_categories.push_back(HOVER_ASSAULT);
405 		map_categories.push_back(SEA_ASSAULT);
406 
407 		map_categories_id.push_back(0);
408 		map_categories_id.push_back(1);
409 		map_categories_id.push_back(2);
410 		map_categories_id.push_back(3);
411 	}
412 	else
413 	{
414 		if(map_type == LAND_MAP)
415 		{
416 			map_categories.push_back(GROUND_ASSAULT);
417 			map_categories.push_back(AIR_ASSAULT);
418 			map_categories.push_back(HOVER_ASSAULT);
419 
420 			map_categories_id.push_back(0);
421 			map_categories_id.push_back(1);
422 			map_categories_id.push_back(2);
423 		}
424 		else if(map_type == LAND_WATER_MAP)
425 		{
426 			map_categories.push_back(GROUND_ASSAULT);
427 			map_categories.push_back(AIR_ASSAULT);
428 			map_categories.push_back(HOVER_ASSAULT);
429 			map_categories.push_back(SEA_ASSAULT);
430 			map_categories.push_back(SUBMARINE_ASSAULT);
431 
432 			map_categories_id.push_back(0);
433 			map_categories_id.push_back(1);
434 			map_categories_id.push_back(2);
435 			map_categories_id.push_back(3);
436 			map_categories_id.push_back(4);
437 		}
438 		else if(map_type == WATER_MAP)
439 		{
440 			map_categories.push_back(AIR_ASSAULT);
441 			map_categories.push_back(HOVER_ASSAULT);
442 			map_categories.push_back(SEA_ASSAULT);
443 			map_categories.push_back(SUBMARINE_ASSAULT);
444 
445 			map_categories_id.push_back(1);
446 			map_categories_id.push_back(2);
447 			map_categories_id.push_back(3);
448 			map_categories_id.push_back(4);
449 		}
450 		else
451 		{
452 			map_categories.push_back(AIR_ASSAULT);
453 
454 			map_categories_id.push_back(1);
455 		}
456 	}
457 }
458 
459 
460 
ReadContinentFile()461 void AAIMap::ReadContinentFile()
462 {
463 	const std::string filename = cfg->GetFileName(cfg->getUniqueName(true, true, true, true), MAP_CACHE_PATH, "_continent.dat", true);
464 	FILE* file = fopen(filename.c_str(), "r");
465 
466 	if(file != NULL)
467 	{
468 		char buffer[4096];
469 		// check if correct version
470 		fscanf(file, "%s ", buffer);
471 
472 		if(strcmp(buffer, CONTINENT_DATA_VERSION))
473 		{
474 			ai->LogConsole("Continent cache out of date - creating new one");
475 			fclose(file);
476 		}
477 		else
478 		{
479 			int temp, temp2;
480 
481 			// load continent map
482 			for(int j = 0; j < yContMapSize; ++j)
483 			{
484 				for(int i = 0; i < xContMapSize; ++i)
485 				{
486 					fscanf(file, "%i ", &temp);
487 					continent_map[j * xContMapSize + i] = temp;
488 				}
489 			}
490 
491 			// load continents
492 			fscanf(file, "%i ", &temp);
493 
494 			continents.resize(temp);
495 
496 			for(int i = 0; i < temp; ++i)
497 			{
498 				fscanf(file, "%i %i ", &continents[i].size, &temp2);
499 
500 				continents[i].water = (bool) temp2;
501 				continents[i].id = i;
502 			}
503 
504 			// load statistical data
505 			fscanf(file, "%i %i %i %i %i %i %i %i", &land_continents, &water_continents, &avg_land_continent_size, &avg_water_continent_size,
506 																			&max_land_continent_size, &max_water_continent_size,
507 																			&min_land_continent_size, &min_water_continent_size);
508 
509 			fclose(file);
510 
511 			ai->Log("Continent cache file successfully loaded\n");
512 
513 			return;
514 		}
515 	}
516 
517 
518 	// loading has not been succesful -> create new continent maps
519 
520 	// create continent/movement map
521 	CalculateContinentMaps();
522 
523 	//////////////////////////////////////////////////////////////////////////////////////////////////////
524 	// save movement maps
525 	const std::string movementfile = cfg->GetFileName(cfg->getUniqueName(true, false, true, false), MAP_CACHE_PATH, "_movement.dat", true);
526 	file = fopen(movementfile.c_str(), "w+");
527 
528 	fprintf(file, "%s\n",  CONTINENT_DATA_VERSION);
529 
530 	// save continent map
531 	for(int j = 0; j < yContMapSize; ++j)
532 	{
533 		for(int i = 0; i < xContMapSize; ++i)
534 			fprintf(file, "%i ", continent_map[j * xContMapSize + i]);
535 
536 		fprintf(file, "\n");
537 	}
538 
539 	// save continents
540 	fprintf(file, "\n" _STPF_ " \n", continents.size());
541 
542 	for(size_t c = 0; c < continents.size(); ++c)
543 		fprintf(file, "%i %i \n", continents[c].size, (int)continents[c].water);
544 
545 	// save statistical data
546 	fprintf(file, "%i %i %i %i %i %i %i %i\n", land_continents, water_continents, avg_land_continent_size, avg_water_continent_size,
547 																	max_land_continent_size, max_water_continent_size,
548 																	min_land_continent_size, min_water_continent_size);
549 
550 	fclose(file);
551 }
552 
LocateMapLearnFile() const553 std::string AAIMap::LocateMapLearnFile() const
554 {
555 	return cfg->GetFileName(cfg->getUniqueName(true, true, true, true), MAP_LEARN_PATH, "_maplearn.dat", true);
556 }
557 
LocateMapCacheFile() const558 std::string AAIMap::LocateMapCacheFile() const
559 {
560 	return cfg->GetFileName(cfg->getUniqueName(false, false, true, true), MAP_LEARN_PATH, "_mapcache.dat", true);
561 }
562 
ReadMapLearnFile(bool auto_set)563 void AAIMap::ReadMapLearnFile(bool auto_set)
564 {
565 	const std::string mapLearn_filename = LocateMapLearnFile();
566 
567 	const size_t buffer_sizeMax = 2048;
568 	char buffer[buffer_sizeMax];
569 
570 	// open learning files
571 	FILE *load_file = fopen(mapLearn_filename.c_str(), "r");
572 
573 	// check if correct map file version
574 	if(load_file)
575 	{
576 		fscanf(load_file, "%s", buffer);
577 
578 		// file version out of date
579 		if(strcmp(buffer, MAP_LEARN_VERSION))
580 		{
581 			ai->LogConsole("Map learning file version out of date, creating new one");
582 			fclose(load_file);
583 			load_file = 0;
584 		}
585 	}
586 
587 	// load sector data from file or init with default values
588 	if(load_file)
589 	{
590 		for(int j = 0; j < ySectors; ++j)
591 		{
592 			for(int i = 0; i < xSectors; ++i)
593 			{
594 				// load sector data
595 				fscanf(load_file, "%f %f %f", &sector[i][j].flat_ratio, &sector[i][j].water_ratio, &sector[i][j].importance_learned);
596 
597 				// set movement types that may enter this sector
598 				// always: MOVE_TYPE_AIR, MOVE_TYPE_AMPHIB, MOVE_TYPE_HOVER;
599 				sector[i][j].allowed_movement_types = 22;
600 
601 				if(sector[i][j].water_ratio < 0.3)
602 					sector[i][j].allowed_movement_types |= MOVE_TYPE_GROUND;
603 				else if(sector[i][j].water_ratio < 0.7)
604 				{
605 					sector[i][j].allowed_movement_types |= MOVE_TYPE_GROUND;
606 					sector[i][j].allowed_movement_types |= MOVE_TYPE_SEA;
607 				}
608 				else
609 					sector[i][j].allowed_movement_types |= MOVE_TYPE_SEA;
610 
611 				if(sector[i][j].importance_learned <= 1)
612 					sector[i][j].importance_learned += (rand()%5)/20.0;
613 
614 				// load combat data
615 				for(size_t cat = 0; cat < ai->Getbt()->assault_categories.size(); cat++)
616 					fscanf(load_file, "%f %f ", &sector[i][j].attacked_by_learned[cat], &sector[i][j].combats_learned[cat]);
617 
618 				if(auto_set)
619 				{
620 					sector[i][j].importance_this_game = sector[i][j].importance_learned;
621 
622 					for(size_t cat = 0; cat < ai->Getbt()->assault_categories.size(); ++cat)
623 					{
624 						sector[i][j].attacked_by_this_game[cat] = sector[i][j].attacked_by_learned[cat];
625 						sector[i][j].combats_this_game[cat] = sector[i][j].combats_learned[cat];
626 					}
627 				}
628 			}
629 		}
630 	}
631 	else
632 	{
633 		for(int j = 0; j < ySectors; ++j)
634 		{
635 			for(int i = 0; i < xSectors; ++i)
636 			{
637 				sector[i][j].importance_learned = 1 + (rand()%5)/20.0;
638 				sector[i][j].flat_ratio = sector[i][j].GetFlatRatio();
639 				sector[i][j].water_ratio = sector[i][j].GetWaterRatio();
640 
641 				// set movement types that may enter this sector
642 				// always: MOVE_TYPE_AIR, MOVE_TYPE_AMPHIB, MOVE_TYPE_HOVER;
643 				sector[i][j].allowed_movement_types = 22;
644 
645 				if(sector[i][j].water_ratio < 0.3)
646 					sector[i][j].allowed_movement_types |= MOVE_TYPE_GROUND;
647 				else if(sector[i][j].water_ratio < 0.7)
648 				{
649 					sector[i][j].allowed_movement_types |= MOVE_TYPE_GROUND;
650 					sector[i][j].allowed_movement_types |= MOVE_TYPE_SEA;
651 				}
652 				else
653 					sector[i][j].allowed_movement_types |= MOVE_TYPE_SEA;
654 
655 				if(auto_set)
656 				{
657 					sector[i][j].importance_this_game = sector[i][j].importance_learned;
658 
659 					for(size_t cat = 0; cat < ai->Getbt()->assault_categories.size(); ++cat)
660 					{
661 						// init with higher values in the center of the map
662 						sector[i][j].attacked_by_learned[cat] = 2 * sector[i][j].GetEdgeDistance();
663 
664 						sector[i][j].attacked_by_this_game[cat] = sector[i][j].attacked_by_learned[cat];
665 						sector[i][j].combats_this_game[cat] = sector[i][j].combats_learned[cat];
666 					}
667 				}
668 			}
669 		}
670 	}
671 
672 	// determine land/water ratio of total map
673 	flat_land_ratio = 0;
674 	water_ratio = 0;
675 
676 	for(int j = 0; j < ySectors; ++j)
677 	{
678 		for(int i = 0; i < xSectors; ++i)
679 		{
680 			flat_land_ratio += sector[i][j].flat_ratio;
681 			water_ratio += sector[i][j].water_ratio;
682 		}
683 	}
684 
685 	flat_land_ratio /= (float)(xSectors * ySectors);
686 	water_ratio /= (float)(xSectors * ySectors);
687 	land_ratio = 1.0f - water_ratio;
688 
689 	if(load_file)
690 		fclose(load_file);
691 	else
692 		ai->LogConsole("New map-learning file created");
693 }
694 
Learn()695 void AAIMap::Learn()
696 {
697 	AAISector *sector;
698 
699 	for(int y = 0; y < ySectors; ++y)
700 	{
701 		for(int x = 0; x < xSectors; ++x)
702 		{
703 			sector = &this->sector[x][y];
704 
705 			sector->importance_this_game = 0.93f * (sector->importance_this_game + 3.0f * sector->importance_learned)/4.0f;
706 
707 			if(sector->importance_this_game < 1)
708 				sector->importance_this_game = 1;
709 
710 			for(size_t cat = 0; cat < ai->Getbt()->assault_categories.size(); ++cat)
711 			{
712 				sector->attacked_by_this_game[cat] = 0.90f * (sector->attacked_by_this_game[cat] + 3.0f * sector->attacked_by_learned[cat])/4.0f;
713 
714 				sector->combats_this_game[cat] = 0.90f * (sector->combats_this_game[cat] + 3.0f * sector->combats_learned[cat])/4.0f;
715 			}
716 		}
717 	}
718 }
719 
720 // converts unit positions to cell coordinates
Pos2BuildMapPos(float3 * pos,const UnitDef * def)721 void AAIMap::Pos2BuildMapPos(float3 *pos, const UnitDef* def)
722 {
723 	// get cell index of middlepoint
724 	pos->x = (int) (pos->x/SQUARE_SIZE);
725 	pos->z = (int) (pos->z/SQUARE_SIZE);
726 
727 	// shift to the leftmost uppermost cell
728 	pos->x -= def->xsize/2;
729 	pos->z -= def->zsize/2;
730 
731 	// check if pos is still in that map, otherwise retun 0
732 	if(pos->x < 0 && pos->z < 0)
733 		pos->x = pos->z = 0;
734 }
735 
BuildMapPos2Pos(float3 * pos,const UnitDef * def)736 void AAIMap::BuildMapPos2Pos(float3 *pos, const UnitDef *def)
737 {
738 	// shift to middlepoint
739 	pos->x += def->xsize/2;
740 	pos->z += def->zsize/2;
741 
742 	// back to unit coordinates
743 	pos->x *= SQUARE_SIZE;
744 	pos->z *= SQUARE_SIZE;
745 }
746 
Pos2FinalBuildPos(float3 * pos,const UnitDef * def)747 void AAIMap::Pos2FinalBuildPos(float3 *pos, const UnitDef* def)
748 {
749 	if(def->xsize&2) // check if xsize is a multiple of 4
750 		pos->x=floor((pos->x)/(SQUARE_SIZE*2))*SQUARE_SIZE*2+8;
751 	else
752 		pos->x=floor((pos->x+8)/(SQUARE_SIZE*2))*SQUARE_SIZE*2;
753 
754 	if(def->zsize&2)
755 		pos->z=floor((pos->z)/(SQUARE_SIZE*2))*SQUARE_SIZE*2+8;
756 	else
757 		pos->z=floor((pos->z+8)/(SQUARE_SIZE*2))*SQUARE_SIZE*2;
758 }
759 
SetBuildMap(int xPos,int yPos,int xSize,int ySize,int value,int ignore_value)760 bool AAIMap::SetBuildMap(int xPos, int yPos, int xSize, int ySize, int value, int ignore_value)
761 {
762 	//float3 my_pos;
763 
764 	if(xPos+xSize <= xMapSize && yPos+ySize <= yMapSize)
765 	{
766 		for(int x = xPos; x < xSize+xPos; x++)
767 		{
768 			for(int y = yPos; y < ySize+yPos; y++)
769 			{
770 				if(buildmap[x+y*xMapSize] != ignore_value)
771 				{
772 					buildmap[x+y*xMapSize] = value;
773 
774 					// debug
775 					/*if(x%2 == 0 && y%2 == 0)
776 					{
777 						my_pos.x = x * 8;
778 						my_pos.z = y * 8;
779 						my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
780 						ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 1500, ai->Getcb()->GetMyAllyTeam(), true, true);
781 					}*/
782 				}
783 			}
784 		}
785 		return true;
786 	}
787 	return false;
788 }
789 
GetBuildSiteInRect(const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd,bool water)790 float3 AAIMap::GetBuildSiteInRect(const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd, bool water)
791 {
792 	float3 pos;
793 
794 	// get required cell-size of the building
795 	int xSize, ySize, xPos, yPos;
796 	GetSize(def, &xSize, &ySize);
797 
798 	// check rect
799 	for(yPos = yStart; yPos < yEnd; yPos += 2)
800 	{
801 		for(xPos = xStart; xPos < xEnd; xPos += 2)
802 		{
803 			// check if buildmap allows construction
804 			if(CanBuildAt(xPos, yPos, xSize, ySize, water))
805 			{
806 				if(ai->Getbt()->IsFactory(def->id))
807 					yPos += 8;
808 
809 				pos.x = xPos;
810 				pos.z = yPos;
811 
812 				// buildmap allows construction, now check if otherwise blocked
813 				BuildMapPos2Pos(&pos, def);
814 				Pos2FinalBuildPos(&pos, def);
815 
816 				if(ai->Getcb()->CanBuildAt(def, pos))
817 				{
818 					int x = pos.x/xSectorSize;
819 					int y = pos.z/ySectorSize;
820 
821 					if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
822 						return pos;
823 				}
824 			}
825 		}
826 	}
827 
828 	return ZeroVector;
829 }
830 
GetRadarArtyBuildsite(const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd,float range,bool water)831 float3 AAIMap::GetRadarArtyBuildsite(const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd, float range, bool water)
832 {
833 	float3 pos;
834 	float3 best_pos = ZeroVector;
835 
836 	float my_rating;
837 	float best_rating = -10000.0f;
838 
839 	// convert range from unit coordinates to build map coordinates
840 	range /= 8.0f;
841 
842 	// get required cell-size of the building
843 	int xSize, ySize, xPos, yPos;
844 	GetSize(def, &xSize, &ySize);
845 
846 	// go through rect
847 	for(yPos = yStart; yPos < yEnd; yPos += 2)
848 	{
849 		for(xPos = xStart; xPos < xEnd; xPos += 2)
850 		{
851 			if(CanBuildAt(xPos, yPos, xSize, ySize, water))
852 			{
853 				if(water)
854 					my_rating = 1.0f + 0.01f * (float)(rand()%100) - range / (float)(1 + GetEdgeDistance(xPos, yPos));
855 				else
856 					my_rating = 0.01f * (float)(rand()%50) + plateau_map[xPos + yPos * xSize] - range / (float)(1 + GetEdgeDistance(xPos, yPos));
857 
858 				if(my_rating > best_rating)
859 				{
860 					pos.x = xPos;
861 					pos.z = yPos;
862 
863 					// buildmap allows construction, now check if otherwise blocked
864 					BuildMapPos2Pos(&pos, def);
865 					Pos2FinalBuildPos(&pos, def);
866 
867 					if(ai->Getcb()->CanBuildAt(def, pos))
868 					{
869 						best_pos = pos;
870 						best_rating = my_rating;
871 					}
872 				}
873 			}
874 		}
875 	}
876 
877 	return best_pos;
878 }
879 
GetHighestBuildsite(const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd)880 float3 AAIMap::GetHighestBuildsite(const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd)
881 {
882 	float3 best_pos = ZeroVector, pos;
883 
884 	// get required cell-size of the building
885 	int xSize, ySize, xPos, yPos, x, y;
886 	GetSize(def, &xSize, &ySize);
887 
888 	// go through rect
889 	for(xPos = xStart; xPos < xEnd; xPos += 2)
890 	{
891 		for(yPos = yStart; yPos < yEnd; yPos += 2)
892 		{
893 			if(CanBuildAt(xPos, yPos, xSize, ySize))
894 			{
895 				pos.x = xPos;
896 				pos.z = yPos;
897 
898 				// buildmap allows construction, now check if otherwise blocked
899 				BuildMapPos2Pos(&pos, def);
900 				Pos2FinalBuildPos(&pos, def);
901 
902 				if(ai->Getcb()->CanBuildAt(def, pos))
903 				{
904 					x = pos.x/xSectorSize;
905 					y = pos.z/ySectorSize;
906 
907 					if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
908 					{
909 						pos.y = ai->Getcb()->GetElevation(pos.x, pos.z);
910 
911 						if(pos.y > best_pos.y)
912 							best_pos = pos;
913 					}
914 				}
915 			}
916 		}
917 	}
918 
919 	return best_pos;
920 }
921 
GetCenterBuildsite(const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd,bool water)922 float3 AAIMap::GetCenterBuildsite(const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd, bool water)
923 {
924 	float3 pos, temp_pos;
925 	bool vStop = false, hStop = false;
926 	int vCenter = yStart + (yEnd-yStart)/2;
927 	int hCenter = xStart + (xEnd-xStart)/2;
928 	int hIterator = 1, vIterator = 1;
929 
930 	// get required cell-size of the building
931 	int xSize, ySize;
932 	GetSize(def, &xSize, &ySize);
933 
934 	// check rect
935 	while(!vStop || !hStop)
936 	{
937 
938 		pos.z = vCenter - vIterator;
939 		pos.x = hCenter - hIterator;
940 
941 		if(!vStop)
942 		{
943 			while(pos.x < hCenter+hIterator)
944 			{
945 				// check if buildmap allows construction
946 				if(CanBuildAt(pos.x, pos.z, xSize, ySize, water))
947 				{
948 					temp_pos.x = pos.x;
949 					temp_pos.y = 0;
950 					temp_pos.z = pos.z;
951 
952 					if(ai->Getbt()->IsFactory(def->id))
953 						temp_pos.z += 8;
954 
955 					// buildmap allows construction, now check if otherwise blocked
956 					BuildMapPos2Pos(&temp_pos, def);
957 					Pos2FinalBuildPos(&temp_pos, def);
958 
959 					if(ai->Getcb()->CanBuildAt(def, temp_pos))
960 					{
961 						int	x = temp_pos.x/xSectorSize;
962 						int	y = temp_pos.z/ySectorSize;
963 
964 						if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
965 							return temp_pos;
966 					}
967 
968 				}
969 				else if(CanBuildAt(pos.x, pos.z + 2 * vIterator, xSize, ySize, water))
970 				{
971 					temp_pos.x = pos.x;
972 					temp_pos.y = 0;
973 					temp_pos.z = pos.z + 2 * vIterator;
974 
975 					if(ai->Getbt()->IsFactory(def->id))
976 						temp_pos.z += 8;
977 
978 					// buildmap allows construction, now check if otherwise blocked
979 					BuildMapPos2Pos(&temp_pos, def);
980 					Pos2FinalBuildPos(&temp_pos, def);
981 
982 					if(ai->Getcb()->CanBuildAt(def, temp_pos))
983 					{
984 						int x = temp_pos.x/xSectorSize;
985 						int y = temp_pos.z/ySectorSize;
986 
987 						if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
988 							return temp_pos;
989 					}
990 				}
991 
992 				pos.x += 2;
993 			}
994 		}
995 
996 		if (!hStop)
997 		{
998 			hIterator += 2;
999 
1000 			if (hCenter - hIterator < xStart || hCenter + hIterator > xEnd)
1001 			{
1002 				hStop = true;
1003 				hIterator -= 2;
1004 			}
1005 		}
1006 
1007 		if(!hStop)
1008 		{
1009 			while(pos.z < vCenter+vIterator)
1010 			{
1011 				// check if buildmap allows construction
1012 				if(CanBuildAt(pos.x, pos.z, xSize, ySize, water))
1013 				{
1014 					temp_pos.x = pos.x;
1015 					temp_pos.y = 0;
1016 					temp_pos.z = pos.z;
1017 
1018 					if(ai->Getbt()->IsFactory(def->id))
1019 						temp_pos.z += 8;
1020 
1021 					// buildmap allows construction, now check if otherwise blocked
1022 					BuildMapPos2Pos(&temp_pos, def);
1023 					Pos2FinalBuildPos(&temp_pos, def);
1024 
1025 					if(ai->Getcb()->CanBuildAt(def, temp_pos))
1026 					{
1027 						int x = temp_pos.x/xSectorSize;
1028 						int y = temp_pos.z/ySectorSize;
1029 
1030 						if(x < xSectors || x  >= 0 || y < ySectors || y >= 0)
1031 							return temp_pos;
1032 					}
1033 				}
1034 				else if(CanBuildAt(pos.x + 2 * hIterator, pos.z, xSize, ySize, water))
1035 				{
1036 					temp_pos.x = pos.x + 2 * hIterator;
1037 					temp_pos.y = 0;
1038 					temp_pos.z = pos.z;
1039 
1040 					if(ai->Getbt()->IsFactory(def->id))
1041 						temp_pos.z += 8;
1042 
1043 					// buildmap allows construction, now check if otherwise blocked
1044 					BuildMapPos2Pos(&temp_pos, def);
1045 					Pos2FinalBuildPos(&temp_pos, def);
1046 
1047 					if(ai->Getcb()->CanBuildAt(def, temp_pos))
1048 					{
1049 						int x = temp_pos.x/xSectorSize;
1050 						int y = temp_pos.z/ySectorSize;
1051 
1052 						if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
1053 							return temp_pos;
1054 					}
1055 				}
1056 
1057 				pos.z += 2;
1058 			}
1059 		}
1060 
1061 		vIterator += 2;
1062 
1063 		if(vCenter - vIterator < yStart || vCenter + vIterator > yEnd)
1064 			vStop = true;
1065 	}
1066 
1067 	return ZeroVector;
1068 }
1069 
GetRandomBuildsite(const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd,int tries,bool water)1070 float3 AAIMap::GetRandomBuildsite(const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd, int tries, bool water)
1071 {
1072 	float3 pos;
1073 
1074 	// get required cell-size of the building
1075 	int xSize, ySize;
1076 	GetSize(def, &xSize, &ySize);
1077 
1078 	for(int i = 0; i < tries; i++)
1079 	{
1080 
1081 		// get random pos within rectangle
1082 		if(xEnd - xStart - xSize < 1)
1083 			pos.x = xStart;
1084 		else
1085 			pos.x = xStart + rand()%(xEnd - xStart - xSize);
1086 
1087 		if(yEnd - yStart - ySize < 1)
1088 			pos.z = yStart;
1089 		else
1090 			pos.z = yStart + rand()%(yEnd - yStart - ySize);
1091 
1092 		// check if buildmap allows construction
1093 		if(CanBuildAt(pos.x, pos.z, xSize, ySize, water))
1094 		{
1095 			if(ai->Getbt()->IsFactory(def->id))
1096 				pos.z += 8;
1097 
1098 			// buildmap allows construction, now check if otherwise blocked
1099 			BuildMapPos2Pos(&pos, def);
1100 			Pos2FinalBuildPos(&pos, def);
1101 
1102 			if(ai->Getcb()->CanBuildAt(def, pos))
1103 			{
1104 				int x = pos.x/xSectorSize;
1105 				int y = pos.z/ySectorSize;
1106 
1107 				if(x < xSectors && x  >= 0 && y < ySectors && y >= 0)
1108 					return pos;
1109 			}
1110 		}
1111 	}
1112 
1113 	return ZeroVector;
1114 }
1115 
GetClosestBuildsite(const UnitDef * def,float3 pos,int max_distance,bool water)1116 float3 AAIMap::GetClosestBuildsite(const UnitDef *def, float3 pos, int max_distance, bool water)
1117 {
1118 	Pos2BuildMapPos(&pos, def);
1119 
1120 	int xStart = pos.x - max_distance;
1121 	int xEnd = pos.x + max_distance;
1122 	int yStart = pos.z - max_distance;
1123 	int yEnd = pos.z + max_distance;
1124 
1125 	if(xStart < 0)
1126 		xStart = 0;
1127 
1128 	if(xEnd >= xSectors * xSectorSizeMap)
1129 		xEnd = xSectors * xSectorSizeMap - 1;
1130 
1131 	if(yStart < 0)
1132 		yStart = 0;
1133 
1134 	if(yEnd >= ySectors * ySectorSizeMap)
1135 		yEnd = ySectors * ySectorSizeMap - 1;
1136 
1137 	return GetCenterBuildsite(def, xStart, xEnd, yStart, yEnd, water);
1138 }
1139 
CanBuildAt(int xPos,int yPos,int xSize,int ySize,bool water)1140 bool AAIMap::CanBuildAt(int xPos, int yPos, int xSize, int ySize, bool water)
1141 {
1142 	if(xPos+xSize <= xMapSize && yPos+ySize <= yMapSize)
1143 	{
1144 		// check if all squares the building needs are empty
1145 		for(int x = xPos; x < xSize+xPos; ++x)
1146 		{
1147 			for(int y = yPos; y < ySize+yPos; ++y)
1148 			{
1149 				// check if cell already blocked by something
1150 				if(!water && buildmap[x+y*xMapSize] != 0)
1151 					return false;
1152 				else if(water && buildmap[x+y*xMapSize] != 4)
1153 					return false;
1154 			}
1155 		}
1156 		return true;
1157 	}
1158 	else
1159 		return false;
1160 }
1161 
CheckRows(int xPos,int yPos,int xSize,int ySize,bool add,bool water)1162 void AAIMap::CheckRows(int xPos, int yPos, int xSize, int ySize, bool add, bool water)
1163 {
1164 	bool insert_space;
1165 	int cell;
1166 	int building;
1167 
1168 	if(water)
1169 		building = 5;
1170 	else
1171 		building = 1;
1172 
1173 	// check horizontal space
1174 	if(xPos+xSize+cfg->MAX_XROW <= xMapSize && xPos - cfg->MAX_XROW >= 0)
1175 	{
1176 		for(int y = yPos; y < yPos + ySize; ++y)
1177 		{
1178 			if(y >= yMapSize)
1179 			{
1180 				ai->Log("ERROR: y = %i index out of range when checking horizontal rows", y);
1181 				return;
1182 			}
1183 
1184 			// check to the right
1185 			insert_space = true;
1186 			for(int x = xPos+xSize; x < xPos+xSize+cfg->MAX_XROW; x++)
1187 			{
1188 				if(buildmap[x+y*xMapSize] != building)
1189 				{
1190 					insert_space = false;
1191 					break;
1192 				}
1193 			}
1194 
1195 			// check to the left
1196 			if(!insert_space)
1197 			{
1198 				insert_space = true;
1199 
1200 				for(int x = xPos-1; x >= xPos - cfg->MAX_XROW; x--)
1201 				{
1202 					if(buildmap[x+y*xMapSize] != building)
1203 					{
1204 						insert_space = false;
1205 						break;
1206 					}
1207 				}
1208 			}
1209 
1210 			if(insert_space)
1211 			{
1212 				// right side
1213 				cell = GetNextX(1, xPos+xSize, y, building);
1214 
1215 				if(cell != -1 && xPos+xSize+cfg->X_SPACE <= xMapSize)
1216 				{
1217 					BlockCells(cell, y, cfg->X_SPACE, 1, add, water);
1218 
1219 					//add blocking of the edges
1220 					if(y == yPos && (yPos - cfg->Y_SPACE) >= 0)
1221 						BlockCells(cell, yPos - cfg->Y_SPACE, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1222 					if(y == yPos + ySize - 1)
1223 						BlockCells(cell, yPos + ySize, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1224 				}
1225 
1226 				// left side
1227 				cell = GetNextX(0, xPos-1, y, building);
1228 
1229 				if(cell != -1 && cell-cfg->X_SPACE >= 0)
1230 				{
1231 					BlockCells(cell-cfg->X_SPACE, y, cfg->X_SPACE, 1, add, water);
1232 
1233 					// add diagonal blocks
1234 					if(y == yPos && (yPos - cfg->Y_SPACE) >= 0)
1235 							BlockCells(cell-cfg->X_SPACE, yPos - cfg->Y_SPACE, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1236 					if(y == yPos + ySize - 1)
1237 							BlockCells(cell-cfg->X_SPACE, yPos + ySize, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1238 
1239 				}
1240 			}
1241 		}
1242 	}
1243 
1244 	// check vertical space
1245 	if(yPos+ySize+cfg->MAX_YROW <= yMapSize && yPos - cfg->MAX_YROW >= 0)
1246 	{
1247 		for(int x = xPos; x < xPos + xSize; x++)
1248 		{
1249 			if(x >= xMapSize)
1250 			{
1251 				ai->Log("ERROR: x = %i index out of range when checking vertical rows", x);
1252 				return;
1253 			}
1254 
1255 			// check downwards
1256 			insert_space = true;
1257 			for(int y = yPos+ySize; y < yPos+ySize+cfg->MAX_YROW; ++y)
1258 			{
1259 				if(buildmap[x+y*xMapSize] != building)
1260 				{
1261 					insert_space = false;
1262 					break;
1263 				}
1264 			}
1265 
1266 			// check upwards
1267 			if(!insert_space)
1268 			{
1269 				insert_space = true;
1270 
1271 				for(int y = yPos-1; y >= yPos - cfg->MAX_YROW; --y)
1272 				{
1273 					if(buildmap[x+y*xMapSize] != building)
1274 					{
1275 						insert_space = false;
1276 						break;
1277 					}
1278 				}
1279 			}
1280 
1281 			if(insert_space)
1282 			{
1283 				// downwards
1284 				cell = GetNextY(1, x, yPos+ySize, building);
1285 
1286 				if(cell != -1 && yPos+ySize+cfg->Y_SPACE <= yMapSize)
1287 				{
1288 					BlockCells(x, cell, 1, cfg->Y_SPACE, add, water);
1289 
1290 					// add diagonal blocks
1291 					if(x == xPos && (xPos - cfg->X_SPACE) >= 0)
1292 						BlockCells(xPos-cfg->X_SPACE, cell, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1293 					if(x == xPos + xSize - 1)
1294 						BlockCells(xPos + xSize, cell, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1295 				}
1296 
1297 				// upwards
1298 				cell = GetNextY(0, x, yPos-1, building);
1299 
1300 				if(cell != -1 && cell-cfg->Y_SPACE >= 0)
1301 				{
1302 					BlockCells(x, cell-cfg->Y_SPACE, 1, cfg->Y_SPACE, add, water);
1303 
1304 					// add diagonal blocks
1305 					if(x == xPos && (xPos - cfg->X_SPACE) >= 0)
1306 						BlockCells(xPos-cfg->X_SPACE, cell-cfg->Y_SPACE, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1307 					if(x == xPos + xSize - 1)
1308 						BlockCells(xPos + xSize, cell-cfg->Y_SPACE, cfg->X_SPACE, cfg->Y_SPACE, add, water);
1309 				}
1310 			}
1311 		}
1312 	}
1313 }
1314 
BlockCells(int xPos,int yPos,int width,int height,bool block,bool water)1315 void AAIMap::BlockCells(int xPos, int yPos, int width, int height, bool block, bool water)
1316 {
1317 	// make sure to stay within map if too close to the edges
1318 	int xEnd = xPos + width;
1319 	int yEnd = yPos + height;
1320 
1321 	if(xEnd > xMapSize)
1322 		xEnd = xMapSize;
1323 
1324 	if(yEnd > yMapSize)
1325 		yEnd = yMapSize;
1326 
1327 	//float3 my_pos;
1328 	int empty, cell;
1329 
1330 	if(water)
1331 		empty = 4;
1332 	else
1333 		empty = 0;
1334 
1335 	if(block)	// block cells
1336 	{
1337 		for(int y = yPos; y < yEnd; ++y)
1338 		{
1339 			for(int x = xPos; x < xEnd; ++x)
1340 			{
1341 				cell = x + xMapSize*y;
1342 
1343 				// if no building ordered that cell to be blocked, update buildmap
1344 				// (only if space is not already occupied by a building)
1345 				if(blockmap[cell] == 0 && buildmap[cell] == empty)
1346 				{
1347 					buildmap[cell] = 2;
1348 
1349 					// debug
1350 					/*if(x%2 == 0 && y%2 == 0)
1351 					{
1352 						my_pos.x = x * 8;
1353 						my_pos.z = y * 8;
1354 						my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
1355 						ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 1500, ai->Getcb()->GetMyAllyTeam(), true, true);
1356 					}*/
1357 				}
1358 
1359 				++blockmap[cell];
1360 			}
1361 		}
1362 	}
1363 	else		// unblock cells
1364 	{
1365 		for(int y = yPos; y < yEnd; ++y)
1366 		{
1367 			for(int x = xPos; x < xEnd; ++x)
1368 			{
1369 				cell = x + xMapSize*y;
1370 
1371 				if(blockmap[cell] > 0)
1372 				{
1373 					--blockmap[cell];
1374 
1375 					// if cell is not blocked anymore, mark cell on buildmap as empty (only if it has been marked bloked
1376 					//					- if it is not marked as blocked its occupied by another building or unpassable)
1377 					if(blockmap[cell] == 0 && buildmap[cell] == 2)
1378 					{
1379 						buildmap[cell] = empty;
1380 
1381 						// debug
1382 						/*if(x%2 == 0 && y%2 == 0)
1383 						{
1384 							my_pos.x = x * 8;
1385 							my_pos.z = y * 8;
1386 							my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
1387 							ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 1500, ai->Getcb()->GetMyAllyTeam(), true, true);
1388 						}*/
1389 					}
1390 				}
1391 			}
1392 		}
1393 	}
1394 }
1395 
UpdateBuildMap(float3 build_pos,const UnitDef * def,bool block,bool water,bool factory)1396 void AAIMap::UpdateBuildMap(float3 build_pos, const UnitDef *def, bool block, bool water, bool factory)
1397 {
1398 	Pos2BuildMapPos(&build_pos, def);
1399 
1400 	if(block)
1401 	{
1402 		if(water)
1403 			SetBuildMap(build_pos.x, build_pos.z, def->xsize, def->zsize, 5);
1404 		else
1405 			SetBuildMap(build_pos.x, build_pos.z, def->xsize, def->zsize, 1);
1406 	}
1407 	else
1408 	{
1409 		// remove spaces before freeing up buildspace
1410 		CheckRows(build_pos.x, build_pos.z, def->xsize, def->zsize, block, water);
1411 
1412 		if(water)
1413 			SetBuildMap(build_pos.x, build_pos.z, def->xsize, def->zsize, 4);
1414 		else
1415 			SetBuildMap(build_pos.x, build_pos.z, def->xsize, def->zsize, 0);
1416 	}
1417 
1418 	if(factory)
1419 	{
1420 		// extra space for factories to keep exits clear
1421 		BlockCells(build_pos.x, build_pos.z - 8, def->xsize, 8, block, water);
1422 		BlockCells(build_pos.x + def->xsize, build_pos.z - 8, cfg->X_SPACE, def->zsize + 1.5f * (float)cfg->Y_SPACE, block, water);
1423 		BlockCells(build_pos.x, build_pos.z + def->zsize, def->xsize, 1.5f * (float)cfg->Y_SPACE - 8, block, water);
1424 	}
1425 
1426 	// add spaces after blocking buildspace
1427 	if(block)
1428 		CheckRows(build_pos.x, build_pos.z, def->xsize, def->zsize, block, water);
1429 }
1430 
1431 
1432 
GetNextX(int direction,int xPos,int yPos,int value)1433 int AAIMap::GetNextX(int direction, int xPos, int yPos, int value)
1434 {
1435 	int x = xPos;
1436 
1437 	if(direction)
1438 	{
1439 		while(buildmap[x+yPos*xMapSize] == value)
1440 		{
1441 			++x;
1442 
1443 			// search went out of map
1444 			if(x >= xMapSize)
1445 				return -1;
1446 		}
1447 	}
1448 	else
1449 	{
1450 		while(buildmap[x+yPos*xMapSize] == value)
1451 		{
1452 			--x;
1453 
1454 			// search went out of map
1455 			if(x < 0)
1456 				return -1;
1457 		}
1458 	}
1459 
1460 	return x;
1461 }
1462 
GetNextY(int direction,int xPos,int yPos,int value)1463 int AAIMap::GetNextY(int direction, int xPos, int yPos, int value)
1464 {
1465 	int y = yPos;
1466 
1467 	if(direction)
1468 	{
1469 		// scan line until next free cell found
1470 		while(buildmap[xPos+y*xMapSize] == value)
1471 		{
1472 			++y;
1473 
1474 			// search went out of map
1475 			if(y >= yMapSize)
1476 				return -1;
1477 		}
1478 	}
1479 	else
1480 	{
1481 		// scan line until next free cell found
1482 		while(buildmap[xPos+y*xMapSize] == value)
1483 		{
1484 			--y;
1485 
1486 			// search went out of map
1487 			if(y < 0)
1488 				return -1;
1489 		}
1490 	}
1491 
1492 	return y;
1493 }
1494 
GetSize(const UnitDef * def,int * xSize,int * ySize)1495 void AAIMap::GetSize(const UnitDef *def, int *xSize, int *ySize)
1496 {
1497 	// calculate size of building
1498 	*xSize = def->xsize;
1499 	*ySize = def->zsize;
1500 
1501 	// if building is a factory additional vertical space is needed
1502 	if(ai->Getbt()->IsFactory(def->id))
1503 	{
1504 		*xSize += cfg->X_SPACE;
1505 		*ySize += ((float)cfg->Y_SPACE)*1.5;
1506 	}
1507 }
1508 
GetCliffyCells(int xPos,int yPos,int xSize,int ySize)1509 int AAIMap::GetCliffyCells(int xPos, int yPos, int xSize, int ySize)
1510 {
1511 	int cliffs = 0;
1512 
1513 	// count cells with big slope
1514 	for(int x = xPos; x < xPos + xSize; ++x)
1515 	{
1516 		for(int y = yPos; y < yPos + ySize; ++y)
1517 		{
1518 			if(buildmap[x+y*xMapSize] == 3)
1519 				++cliffs;
1520 		}
1521 	}
1522 
1523 	return cliffs;
1524 }
1525 
GetCliffyCellsInSector(AAISector * sector)1526 int AAIMap::GetCliffyCellsInSector(AAISector *sector)
1527 {
1528 	int cliffs = 0;
1529 
1530 	int xPos = sector->x * xSectorSize;
1531 	int yPos = sector->y * ySectorSize;
1532 
1533 	// count cells with big slope
1534 	for(int x = xPos; x < xPos + xSectorSizeMap; ++x)
1535 	{
1536 		for(int y = yPos; y < yPos + ySectorSizeMap; ++y)
1537 		{
1538 			if(buildmap[x+y*xMapSize] == 3)
1539 				++cliffs;
1540 		}
1541 	}
1542 
1543 	return cliffs;
1544 }
1545 
AnalyseMap()1546 void AAIMap::AnalyseMap()
1547 {
1548 	float3 my_pos;
1549 
1550 	float slope;
1551 
1552 	const float *height_map = ai->Getcb()->GetHeightMap();
1553 
1554 	// get water/cliffs
1555 	for(int x = 0; x < xMapSize; ++x)
1556 	{
1557 		for(int y = 0; y < yMapSize; ++y)
1558 		{
1559 			// check for water
1560 			if(height_map[y * xMapSize + x] < 0)
1561 				buildmap[x+y*xMapSize] = 4;
1562 			else if(x < xMapSize - 4 && y < yMapSize - 4)
1563 			// check slope
1564 			{
1565 				slope = (height_map[y * xMapSize + x] - height_map[y * xMapSize + x + 4])/64.0;
1566 
1567 				// check x-direction
1568 				if(slope > cfg->CLIFF_SLOPE || -slope > cfg->CLIFF_SLOPE)
1569 					buildmap[x+y*xMapSize] = 3;
1570 				else	// check y-direction
1571 				{
1572 					slope = (height_map[y * xMapSize + x] - height_map[(y+4) * xMapSize + x])/64.0;
1573 
1574 					if(slope > cfg->CLIFF_SLOPE || -slope > cfg->CLIFF_SLOPE)
1575 						buildmap[x+y*xMapSize] = 3;
1576 				}
1577 			}
1578 		}
1579 	}
1580 
1581 	// calculate plateu map
1582 	int xSize = xMapSize/4;
1583 	int ySize = yMapSize/4;
1584 
1585 	int TERRAIN_DETECTION_RANGE = 6;
1586 	float my_height, diff;
1587 
1588 	for(int x = TERRAIN_DETECTION_RANGE; x < xSize - TERRAIN_DETECTION_RANGE; ++x)
1589 	{
1590 		for(int y = TERRAIN_DETECTION_RANGE; y < ySize - TERRAIN_DETECTION_RANGE; ++y)
1591 		{
1592 			my_height = height_map[4 * (x + y * xMapSize)];
1593 
1594 			for(int i = x - TERRAIN_DETECTION_RANGE; i < x + TERRAIN_DETECTION_RANGE; ++i)
1595 			{
1596 				for(int j = y - TERRAIN_DETECTION_RANGE; j < y + TERRAIN_DETECTION_RANGE; ++j)
1597 				{
1598 					 diff = (height_map[4 * (i + j * xMapSize)] - my_height);
1599 
1600 					 if(diff > 0)
1601 					 {
1602 						 if(buildmap[4 * (i + j * xMapSize)] != 3)
1603 							 plateau_map[i + j * xSize] += diff;
1604 					 }
1605 					 else
1606 						 plateau_map[i + j * xSize] += diff;
1607 				}
1608 			}
1609 		}
1610 	}
1611 
1612 	for(int x = 0; x < xSize; ++x)
1613 	{
1614 		for(int y = 0; y < ySize; ++y)
1615 		{
1616 			if(plateau_map[x + y * xSize] >= 0)
1617 				plateau_map[x + y * xSize] = sqrt(plateau_map[x + y * xSize]);
1618 			else
1619 				plateau_map[x + y * xSize] = -1.0f * sqrt((-1.0f) * plateau_map[x + y * xSize]);
1620 		}
1621 	}
1622 }
1623 
DetectMapType()1624 void AAIMap::DetectMapType()
1625 {
1626 	if( (float)max_land_continent_size < 0.5f * (float)max_water_continent_size || water_ratio > 0.80f)
1627 		map_type = WATER_MAP;
1628 	else if(water_ratio > 0.25f)
1629 		map_type = LAND_WATER_MAP;
1630 	else
1631 		map_type = LAND_MAP;
1632 }
1633 
CalculateWaterRatio()1634 void AAIMap::CalculateWaterRatio()
1635 {
1636 	water_ratio = 0;
1637 
1638 	for(int y = 0; y < yMapSize; ++y)
1639 	{
1640 		for(int x = 0; x < xMapSize; ++x)
1641 		{
1642 			if(buildmap[x + y*xMapSize] == 4)
1643 				++water_ratio;
1644 		}
1645 	}
1646 
1647 	water_ratio = water_ratio / ((float)(xMapSize*yMapSize));
1648 }
1649 
CalculateContinentMaps()1650 void AAIMap::CalculateContinentMaps()
1651 {
1652 	vector<int> *new_edge_cells;
1653 	vector<int> *old_edge_cells;
1654 
1655 	vector<int> a, b;
1656 
1657 	old_edge_cells = &a;
1658 	new_edge_cells = &b;
1659 
1660 	const float *height_map = ai->Getcb()->GetHeightMap();
1661 
1662 	int x, y;
1663 
1664 	AAIContinent temp;
1665 	int continent_id = 0;
1666 
1667 
1668 	for(int i = 0; i < xContMapSize; i += 1)
1669 	{
1670 		for(int j = 0; j < yContMapSize; j += 1)
1671 		{
1672 			// add new continent if cell has not been visited yet
1673 			if(continent_map[j * xContMapSize + i] < 0 && height_map[4 * (j * xMapSize + i)] >= 0)
1674 			{
1675 				temp.id = continent_id;
1676 				temp.size = 1;
1677 				temp.water = false;
1678 
1679 				continents.push_back(temp);
1680 
1681 				continent_map[j * xContMapSize + i] = continent_id;
1682 
1683 				old_edge_cells->push_back(j * xContMapSize + i);
1684 
1685 				// check edges of the continent as long as new cells have been added to the continent during the last loop
1686 				while(old_edge_cells->size() > 0)
1687 				{
1688 					for(vector<int>::iterator cell = old_edge_cells->begin(); cell != old_edge_cells->end(); ++cell)
1689 					{
1690 						// get cell indizes
1691 						x = (*cell)%xContMapSize;
1692 						y = ((*cell) - x) / xContMapSize;
1693 
1694 						// check edges
1695 						if(x > 0 && continent_map[y * xContMapSize + x - 1] == -1)
1696 						{
1697 							if(height_map[4 * (y * xMapSize + x - 1)] >= 0)
1698 							{
1699 								continent_map[y * xContMapSize + x - 1] = continent_id;
1700 								continents[continent_id].size += 1;
1701 								new_edge_cells->push_back( y * xContMapSize + x - 1 );
1702 							}
1703 							else if(height_map[4 * (y * xMapSize + x - 1)] >= - cfg->NON_AMPHIB_MAX_WATERDEPTH)
1704 							{
1705 								continent_map[y * xContMapSize + x - 1] = -2;
1706 								new_edge_cells->push_back( y * xContMapSize + x - 1 );
1707 							}
1708 						}
1709 
1710 						if(x < xContMapSize-1 && continent_map[y * xContMapSize + x + 1] == -1)
1711 						{
1712 							if(height_map[4 * (y * xMapSize + x + 1)] >= 0)
1713 							{
1714 								continent_map[y * xContMapSize + x + 1] = continent_id;
1715 								continents[continent_id].size += 1;
1716 								new_edge_cells->push_back( y * xContMapSize + x + 1 );
1717 							}
1718 							else if(height_map[4 * (y * xMapSize + x + 1)] >= - cfg->NON_AMPHIB_MAX_WATERDEPTH)
1719 							{
1720 								continent_map[y * xContMapSize + x + 1] = -2;
1721 								new_edge_cells->push_back( y * xContMapSize + x + 1 );
1722 							}
1723 						}
1724 
1725 						if(y > 0 && continent_map[(y - 1) * xContMapSize + x] == -1)
1726 						{
1727 							if(height_map[4 * ( (y - 1) * xMapSize + x)] >= 0)
1728 							{
1729 								continent_map[(y - 1) * xContMapSize + x] = continent_id;
1730 								continents[continent_id].size += 1;
1731 								new_edge_cells->push_back( (y - 1) * xContMapSize + x);
1732 							}
1733 							else if(height_map[4 * ( (y - 1) * xMapSize + x)] >= - cfg->NON_AMPHIB_MAX_WATERDEPTH)
1734 							{
1735 								continent_map[(y - 1) * xContMapSize + x] = -2;
1736 								new_edge_cells->push_back( (y - 1) * xContMapSize + x );
1737 							}
1738 						}
1739 
1740 						if(y < yContMapSize-1 && continent_map[(y + 1 ) * xContMapSize + x] == -1)
1741 						{
1742 							if(height_map[4 * ( (y + 1) * xMapSize + x)] >= 0)
1743 							{
1744 								continent_map[(y + 1) * xContMapSize + x] = continent_id;
1745 								continents[continent_id].size += 1;
1746 								new_edge_cells->push_back( (y + 1) * xContMapSize + x );
1747 							}
1748 							else if(height_map[4 * ( (y + 1) * xMapSize + x)] >= - cfg->NON_AMPHIB_MAX_WATERDEPTH)
1749 							{
1750 								continent_map[(y + 1) * xContMapSize + x] = -2;
1751 								new_edge_cells->push_back( (y + 1) * xContMapSize + x );
1752 							}
1753 						}
1754 					}
1755 
1756 					old_edge_cells->clear();
1757 
1758 					// invert pointers to new/old edge cells
1759 					if(new_edge_cells == &a)
1760 					{
1761 						new_edge_cells = &b;
1762 						old_edge_cells = &a;
1763 					}
1764 					else
1765 					{
1766 						new_edge_cells = &a;
1767 						old_edge_cells = &b;
1768 					}
1769 				}
1770 
1771 				// finished adding continent
1772 				++continent_id;
1773 				old_edge_cells->clear();
1774 				new_edge_cells->clear();
1775 			}
1776 		}
1777 	}
1778 
1779 	// water continents
1780 	for(int i = 0; i < xContMapSize; i += 1)
1781 	{
1782 		for(int j = 0; j < yContMapSize; j += 1)
1783 		{
1784 			// add new continent if cell has not been visited yet
1785 			if(continent_map[j * xContMapSize + i] < 0)
1786 			{
1787 				temp.id = continent_id;
1788 				temp.size = 1;
1789 				temp.water = true;
1790 
1791 				continents.push_back(temp);
1792 
1793 				continent_map[j * xContMapSize + i] = continent_id;
1794 
1795 				old_edge_cells->push_back(j * xContMapSize + i);
1796 
1797 				// check edges of the continent as long as new cells have been added to the continent during the last loop
1798 				while(old_edge_cells->size() > 0)
1799 				{
1800 					for(vector<int>::iterator cell = old_edge_cells->begin(); cell != old_edge_cells->end(); ++cell)
1801 					{
1802 						// get cell indizes
1803 						x = (*cell)%xContMapSize;
1804 						y = ((*cell) - x) / xContMapSize;
1805 
1806 						// check edges
1807 						if(x > 0 && continent_map[y * xContMapSize + x - 1] < 0)
1808 						{
1809 							if(height_map[4 * (y * xMapSize + x - 1)] < 0)
1810 								{
1811 								continent_map[y * xContMapSize + x - 1] = continent_id;
1812 								continents[continent_id].size += 1;
1813 								new_edge_cells->push_back( y * xContMapSize + x - 1 );
1814 							}
1815 						}
1816 
1817 						if(x < xContMapSize-1 && continent_map[y * xContMapSize + x + 1] < 0)
1818 						{
1819 							if(height_map[4 * (y * xMapSize + x + 1)] < 0)
1820 							{
1821 								continent_map[y * xContMapSize + x + 1] = continent_id;
1822 								continents[continent_id].size += 1;
1823 								new_edge_cells->push_back( y * xContMapSize + x + 1 );
1824 							}
1825 						}
1826 
1827 						if(y > 0 && continent_map[(y - 1) * xContMapSize + x ] < 0)
1828 						{
1829 							if(height_map[4 * ( (y - 1) * xMapSize + x )] < 0)
1830 							{
1831 								continent_map[(y - 1) * xContMapSize + x ] = continent_id;
1832 								continents[continent_id].size += 1;
1833 								new_edge_cells->push_back( (y - 1) * xContMapSize + x );
1834 							}
1835 						}
1836 
1837 						if(y < yContMapSize-1 && continent_map[(y + 1) * xContMapSize + x ] < 0)
1838 						{
1839 							if(height_map[4 * ( (y + 1) * xMapSize + x)] < 0)
1840 							{
1841 								continent_map[(y + 1) * xContMapSize + x ] = continent_id;
1842 								continents[continent_id].size += 1;
1843 								new_edge_cells->push_back( (y + 1) * xContMapSize + x  );
1844 							}
1845 						}
1846 					}
1847 
1848 					old_edge_cells->clear();
1849 
1850 					// invert pointers to new/old edge cells
1851 					if(new_edge_cells == &a)
1852 					{
1853 						new_edge_cells = &b;
1854 						old_edge_cells = &a;
1855 					}
1856 					else
1857 					{
1858 						new_edge_cells = &a;
1859 						old_edge_cells = &b;
1860 					}
1861 				}
1862 
1863 				// finished adding continent
1864 				++continent_id;
1865 				old_edge_cells->clear();
1866 				new_edge_cells->clear();
1867 			}
1868 		}
1869 	}
1870 
1871 	// calculate some statistical data
1872 	land_continents = 0;
1873 	water_continents = 0;
1874 
1875 	avg_land_continent_size = 0;
1876 	avg_water_continent_size = 0;
1877 	max_land_continent_size = 0;
1878 	max_water_continent_size = 0;
1879 	min_land_continent_size = xContMapSize * yContMapSize;
1880 	min_water_continent_size = xContMapSize * yContMapSize;
1881 
1882 	for(size_t i = 0; i < continents.size(); ++i)
1883 	{
1884 		if(continents[i].water)
1885 		{
1886 			++water_continents;
1887 			avg_water_continent_size += continents[i].size;
1888 
1889 			if(continents[i].size > max_water_continent_size)
1890 				max_water_continent_size = continents[i].size;
1891 
1892 			if(continents[i].size < min_water_continent_size)
1893 				min_water_continent_size = continents[i].size;
1894 		}
1895 		else
1896 		{
1897 			++land_continents;
1898 			avg_land_continent_size += continents[i].size;
1899 
1900 			if(continents[i].size > max_land_continent_size)
1901 				max_land_continent_size = continents[i].size;
1902 
1903 			if(continents[i].size < min_land_continent_size)
1904 				min_land_continent_size = continents[i].size;
1905 		}
1906 	}
1907 
1908 	if(water_continents > 0)
1909 		avg_water_continent_size /= water_continents;
1910 
1911 	if(land_continents > 0)
1912 		avg_land_continent_size /= land_continents;
1913 }
1914 
1915 // algorithm more or less by krogothe - thx very much
SearchMetalSpots()1916 void AAIMap::SearchMetalSpots()
1917 {
1918 	const int unitid = ai->Getbt()->GetBiggestMex()-1; //WTF, why -1?
1919 	if (unitid <= 0) {
1920 		ai->Log("No metal extractor unit known!");
1921 		return;
1922 	}
1923 	const UnitDef* def = &ai->Getbt()->GetUnitDef(unitid);
1924 
1925 	metalMap = false;
1926 	bool Stopme = false;
1927 	int TotalMetal = 0;
1928 	int MaxMetal = 0;
1929 	int TempMetal = 0;
1930 	int SpotsFound = 0;
1931 	int coordx = 0, coordy = 0;
1932 //	float AverageMetal;
1933 
1934 	AAIMetalSpot temp;
1935 	float3 pos;
1936 
1937 	int MinMetalForSpot = 30; // from 0-255, the minimum percentage of metal a spot needs to have
1938 							//from the maximum to be saved. Prevents crappier spots in between taken spaces.
1939 							//They are still perfectly valid and will generate metal mind you!
1940 	int MaxSpots = 5000; //If more spots than that are found the map is considered a metalmap, tweak this as needed
1941 
1942 	int MetalMapHeight = ai->Getcb()->GetMapHeight() / 2; //metal map has 1/2 resolution of normal map
1943 	int MetalMapWidth = ai->Getcb()->GetMapWidth() / 2;
1944 	int TotalCells = MetalMapHeight * MetalMapWidth;
1945 	unsigned char XtractorRadius = ai->Getcb()->GetExtractorRadius()/ 16.0;
1946 	unsigned char DoubleRadius = ai->Getcb()->GetExtractorRadius() / 8.0;
1947 	int SquareRadius = (ai->Getcb()->GetExtractorRadius() / 16.0) * (ai->Getcb()->GetExtractorRadius() / 16.0); //used to speed up loops so no recalculation needed
1948 	int DoubleSquareRadius = (ai->Getcb()->GetExtractorRadius() / 8.0) * (ai->Getcb()->GetExtractorRadius() / 8.0); // same as above
1949 //	int CellsInRadius = PI * XtractorRadius * XtractorRadius; //yadda yadda
1950 	unsigned char* MexArrayA = new unsigned char [TotalCells];
1951 	unsigned char* MexArrayB = new unsigned char [TotalCells];
1952 	int* TempAverage = new int [TotalCells];
1953 
1954 	// clear variables, just in case!
1955 	TotalMetal = 0;
1956 	MaxMetal = 0;
1957 	SpotsFound = 0;
1958 
1959 	//Load up the metal Values in each pixel
1960 	for (int i = 0; i != TotalCells - 1; i++)
1961 	{
1962 		MexArrayA[i] = *(ai->Getcb()->GetMetalMap() + i);
1963 		TotalMetal += MexArrayA[i];		// Count the total metal so you can work out an average of the whole map
1964 	}
1965 
1966 //	AverageMetal = ((float)TotalMetal) / ((float)TotalCells);  //do the average
1967 
1968 	// Now work out how much metal each spot can make by adding up the metal from nearby spots
1969 	for (int y = 0; y != MetalMapHeight; y++)
1970 	{
1971 		for (int x = 0; x != MetalMapWidth; x++)
1972 		{
1973 			TotalMetal = 0;
1974 			for (int myx = x - XtractorRadius; myx != x + XtractorRadius; myx++)
1975 			{
1976 				if (myx >= 0 && myx < MetalMapWidth)
1977 				{
1978 					for (int myy = y - XtractorRadius; myy != y + XtractorRadius; myy++)
1979 					{
1980 						if (myy >= 0 && myy < MetalMapHeight && ((x - myx)*(x - myx) + (y - myy)*(y - myy)) <= SquareRadius)
1981 						{
1982 							TotalMetal += MexArrayA[myy * MetalMapWidth + myx]; //get the metal from all pixels around the extractor radius
1983 						}
1984 					}
1985 				}
1986 			}
1987 			TempAverage[y * MetalMapWidth + x] = TotalMetal; //set that spots metal making ability (divide by cells to values are small)
1988 			if (MaxMetal < TotalMetal)
1989 				MaxMetal = TotalMetal;  //find the spot with the highest metal to set as the map's max
1990 		}
1991 	}
1992 	for (int i = 0; i != TotalCells; i++) // this will get the total metal a mex placed at each spot would make
1993 	{
1994 		MexArrayB[i] = TempAverage[i] * 255 / MaxMetal;  //scale the metal so any map will have values 0-255, no matter how much metal it has
1995 	}
1996 
1997 
1998 	for (int a = 0; a != MaxSpots; a++)
1999 	{
2000 		if(!Stopme)
2001 			TempMetal = 0; //reset tempmetal so it can find new spots
2002 		for (int i = 0; i != TotalCells; i=i+2)
2003 		{			//finds the best spot on the map and gets its coords
2004 			if (MexArrayB[i] > TempMetal && !Stopme)
2005 			{
2006 				TempMetal = MexArrayB[i];
2007 				coordx = i%MetalMapWidth;
2008 				coordy = i/MetalMapWidth;
2009 			}
2010 		}
2011 		if (TempMetal < MinMetalForSpot)
2012 			Stopme = true; // if the spots get too crappy it will stop running the loops to speed it all up
2013 
2014 		if (!Stopme)
2015 		{
2016 			pos.x = coordx * 2 * SQUARE_SIZE;
2017 			pos.z = coordy * 2 * SQUARE_SIZE;
2018 			pos.y = ai->Getcb()->GetElevation(pos.x, pos.z);
2019 
2020 			Pos2FinalBuildPos(&pos, def);
2021 
2022 			temp.amount = TempMetal * ai->Getcb()->GetMaxMetal() * MaxMetal / 255.0;
2023 			temp.occupied = false;
2024 			temp.pos = pos;
2025 
2026 			//if(ai->Getcb()->CanBuildAt(def, pos))
2027 			//{
2028 				Pos2BuildMapPos(&pos, def);
2029 
2030 				if(pos.z >= 2 && pos.x >= 2 && pos.x < xMapSize-2 && pos.z < yMapSize-2)
2031 				{
2032 					if(CanBuildAt(pos.x, pos.z, def->xsize, def->zsize))
2033 					{
2034 						metal_spots.push_back(temp);
2035 						SpotsFound++;
2036 
2037 						if(pos.y >= 0)
2038 							SetBuildMap(pos.x-2, pos.z-2, def->xsize+4, def->zsize+4, 1);
2039 						else
2040 							SetBuildMap(pos.x-2, pos.z-2, def->xsize+4, def->zsize+4, 5);
2041 					}
2042 				}
2043 			//}
2044 
2045 			for (int myx = coordx - XtractorRadius; myx != coordx + XtractorRadius; myx++)
2046 			{
2047 				if (myx >= 0 && myx < MetalMapWidth)
2048 				{
2049 					for (int myy = coordy - XtractorRadius; myy != coordy + XtractorRadius; myy++)
2050 					{
2051 						if (myy >= 0 && myy < MetalMapHeight && ((coordx - myx)*(coordx - myx) + (coordy - myy)*(coordy - myy)) <= SquareRadius)
2052 						{
2053 							MexArrayA[myy * MetalMapWidth + myx] = 0; //wipes the metal around the spot so its not counted twice
2054 							MexArrayB[myy * MetalMapWidth + myx] = 0;
2055 						}
2056 					}
2057 				}
2058 			}
2059 
2060 			// Redo the whole averaging process around the picked spot so other spots can be found around it
2061 			for (int y = coordy - DoubleRadius; y != coordy + DoubleRadius; y++)
2062 			{
2063 				if(y >=0 && y < MetalMapHeight)
2064 				{
2065 					for (int x = coordx - DoubleRadius; x != coordx + DoubleRadius; x++)
2066 					{//funcion below is optimized so it will only update spots between r and 2r, greatly speeding it up
2067 						if((coordx - x)*(coordx - x) + (coordy - y)*(coordy - y) <= DoubleSquareRadius && x >=0 && x < MetalMapWidth && MexArrayB[y * MetalMapWidth + x])
2068 						{
2069 							TotalMetal = 0;
2070 							for (int myx = x - XtractorRadius; myx != x + XtractorRadius; myx++)
2071 							{
2072 								if (myx >= 0 && myx < MetalMapWidth)
2073 								{
2074 									for (int myy = y - XtractorRadius; myy != y + XtractorRadius; myy++)
2075 									{
2076 										if (myy >= 0 && myy < MetalMapHeight && ((x - myx)*(x - myx) + (y - myy)*(y - myy)) <= SquareRadius)
2077 										{
2078 											TotalMetal += MexArrayA[myy * MetalMapWidth + myx]; //recalculate nearby spots to account for deleted metal from chosen spot
2079 										}
2080 									}
2081 								}
2082 							}
2083 							MexArrayB[y * MetalMapWidth + x] = TotalMetal * 255 / MaxMetal;; //set that spots metal amount
2084 						}
2085 					}
2086 				}
2087 			}
2088 		}
2089 	}
2090 
2091 	if(SpotsFound > 500)
2092 	{
2093 		metalMap = true;
2094 		metal_spots.clear();
2095 		ai->Log("Map is considered to be a metal map\n");
2096 	}
2097 	else
2098 		metalMap = false;
2099 
2100 	SafeDeleteArray(MexArrayA);
2101 	SafeDeleteArray(MexArrayB);
2102 	SafeDeleteArray(TempAverage);
2103 }
2104 
UpdateRecon()2105 void AAIMap::UpdateRecon()
2106 {
2107 	const UnitDef *def;
2108 	UnitCategory cat;
2109 	float3 pos;
2110 
2111 	int frame = ai->Getcb()->GetCurrentFrame();
2112 
2113 	fill(sector_in_los.begin(), sector_in_los.end(), 0);
2114 	fill(sector_in_los_with_enemies.begin(), sector_in_los_with_enemies.end(), 0);
2115 	fill(enemy_combat_units_spotted.begin(), enemy_combat_units_spotted.end(), 0);
2116 
2117 	//
2118 	// reset scouted buildings for all cells within current los
2119 	//
2120 	const unsigned short *los_map = ai->Getcb()->GetLosMap();
2121 
2122 	for(int y = 0; y < yLOSMapSize; ++y)
2123 	{
2124 		for(int x = 0; x < xLOSMapSize; ++x)
2125 		{
2126 			if(los_map[x + y * xLOSMapSize])
2127 			{
2128 				scout_map[x + y * xLOSMapSize] = 0;
2129 				last_updated_map[x + y * xLOSMapSize] = frame;
2130 				++sector_in_los[(losMapRes*x / xSectorSizeMap) + (losMapRes*y / ySectorSizeMap) * (xSectors+1)];
2131 			}
2132 		}
2133 	}
2134 
2135 
2136 	for(int y = 0; y < ySectors; ++y)
2137 	{
2138 		for(int x = 0; x < xSectors; ++x)
2139 			sector[x][y].enemies_on_radar = 0;
2140 	}
2141 
2142 	// update enemy units
2143 	int number_of_units = ai->Getcb()->GetEnemyUnitsInRadarAndLos(&(units_in_los.front()));
2144 	int x_pos, y_pos;
2145 
2146 	for(int i = 0; i < number_of_units; ++i)
2147 	{
2148 		//pos = ai->Getcb()->GetUnitPos(units_in_los[i]);
2149 		def = ai->Getcb()->GetUnitDef(units_in_los[i]);
2150 
2151 		if(def) // unit is within los
2152 		{
2153 			x_pos = (int)pos.x / (losMapRes * SQUARE_SIZE);
2154 			y_pos = (int)pos.z / (losMapRes * SQUARE_SIZE);
2155 
2156 			// make sure unit is within the map (e.g. no aircraft that has flown outside of the map)
2157 			if(x_pos >= 0 && x_pos < xLOSMapSize && y_pos >= 0 && y_pos < yLOSMapSize)
2158 			{
2159 				cat = ai->Getbt()->units_static[def->id].category;
2160 
2161 				// add buildings/combat units to scout map
2162 				if(cat >= STATIONARY_DEF && cat <= SUBMARINE_ASSAULT)
2163 				{
2164 					scout_map[x_pos + y_pos * xLOSMapSize] = def->id;
2165 					++sector_in_los_with_enemies[(losMapRes * x_pos) / xSectorSizeMap + (xSectors + 1) * ((losMapRes * y_pos) / ySectorSizeMap) ];
2166 				}
2167 
2168 				if(cat >= GROUND_ASSAULT && cat <= SUBMARINE_ASSAULT)
2169 					++enemy_combat_units_spotted[cat - GROUND_ASSAULT];
2170 			}
2171 		}
2172 		else // unit on radar only
2173 		{
2174 			pos = ai->Getcb()->GetUnitPos(units_in_los[i]);
2175 
2176 			x_pos = pos.x/xSectorSize;
2177 			y_pos = pos.z/ySectorSize;
2178 
2179 			if(x_pos >= 0 && y_pos >= 0 && x_pos < xSectors && y_pos < ySectors)
2180 				sector[x_pos][y_pos].enemies_on_radar += 1;
2181 		}
2182 	}
2183 
2184 	// map of known enemy buildings has been updated -> update sector data
2185 	for(int y = 0; y < ySectors; ++y)
2186 	{
2187 		for(int x = 0; x < xSectors; ++x)
2188 		{
2189 			// only update sector data if its within los
2190 			if(sector_in_los[x + y * (xSectors+1)])
2191 			{
2192 				sector[x][y].own_structures = 0;
2193 				sector[x][y].allied_structures = 0;
2194 
2195 				fill(sector[x][y].my_combat_units.begin(), sector[x][y].my_combat_units.end(), 0);
2196 				fill(sector[x][y].my_mobile_combat_power.begin(), sector[x][y].my_mobile_combat_power.end(), 0);
2197 				fill(sector[x][y].my_stat_combat_power.begin(), sector[x][y].my_stat_combat_power.end(), 0);
2198 			}
2199 		}
2200 	}
2201 
2202 	// update own/friendly units
2203 	int x, y;
2204 	int my_team = ai->Getcb()->GetMyTeam();
2205 
2206 	number_of_units = ai->Getcb()->GetFriendlyUnits(&(units_in_los.front()));
2207 
2208 	for(int i = 0; i < number_of_units; ++i)
2209 	{
2210 		// get unit def & category
2211 		def = ai->Getcb()->GetUnitDef(units_in_los[i]);
2212 		cat = ai->Getbt()->units_static[def->id].category;
2213 
2214 		if(cat >= STATIONARY_DEF && cat <= SUBMARINE_ASSAULT)
2215 		{
2216 			pos = ai->Getcb()->GetUnitPos(units_in_los[i]);
2217 
2218 			x = pos.x/xSectorSize;
2219 			y = pos.z/ySectorSize;
2220 
2221 			if(x >= 0 && y >= 0 && x < xSectors && y < ySectors)
2222 			{
2223 				// add building to sector (and update stat_combat_power if it's a stat defence)
2224 				if(cat <= METAL_MAKER)
2225 				{
2226 					if(ai->Getcb()->GetUnitTeam(units_in_los[i]) == my_team)
2227 						++sector[x][y].own_structures;
2228 					else
2229 						++sector[x][y].allied_structures;
2230 
2231 					if(cat == STATIONARY_DEF)
2232 					{
2233 						for(int i = 0; i < AAIBuildTable::ass_categories; ++i)
2234 							sector[x][y].my_stat_combat_power[i] += ai->Getbt()->units_static[def->id].efficiency[i];
2235 					}
2236 				}
2237 				// add unit to sector and update mobile_combat_power
2238 				else if(cat >= GROUND_ASSAULT)
2239 				{
2240 					++sector[x][y].my_combat_units[ai->Getbt()->units_static[def->id].category - GROUND_ASSAULT];
2241 
2242 					for(int i = 0; i < AAIBuildTable::combat_categories; ++i)
2243 						sector[x][y].my_mobile_combat_power[i] += ai->Getbt()->units_static[def->id].efficiency[i];
2244 				}
2245 			}
2246 		}
2247 	}
2248 
2249 	ai->Getbrain()->UpdateMaxCombatUnitsSpotted(enemy_combat_units_spotted);
2250 }
2251 
UpdateEnemyScoutingData()2252 void AAIMap::UpdateEnemyScoutingData()
2253 {
2254 	int def_id;
2255 	int frame = ai->Getcb()->GetCurrentFrame();
2256 	float last_seen;
2257 	AAISector *sector;
2258 
2259 	// map of known enemy buildings has been updated -> update sector data
2260 	for(int y = 0; y < ySectors; ++y)
2261 	{
2262 		for(int x = 0; x < xSectors; ++x)
2263 		{
2264 			sector = &this->sector[x][y];
2265 			sector->enemy_structures = 0;
2266 
2267 			fill(sector->enemy_combat_units.begin(), sector->enemy_combat_units.end(), 0);
2268 			fill(sector->enemy_stat_combat_power.begin(), sector->enemy_stat_combat_power.end(), 0);
2269 			fill(sector->enemy_mobile_combat_power.begin(), sector->enemy_mobile_combat_power.end(), 0);
2270 
2271 			for(int y = sector->y * ySectorSizeMap/losMapRes; y < (sector->y + 1) * ySectorSizeMap/losMapRes; ++y)
2272 			{
2273 				for(int x = sector->x * xSectorSizeMap/losMapRes; x < (sector->x + 1) * xSectorSizeMap/losMapRes; ++x)
2274 				{
2275 					def_id = scout_map[x + y * xLOSMapSize];
2276 
2277 					if(def_id)
2278 					{
2279 						// add building to sector (and update stat_combat_power if it's a stat defence)
2280 						if(ai->Getbt()->units_static[def_id].category <= METAL_MAKER)
2281 						{
2282 							++sector->enemy_structures;
2283 
2284 							if(ai->Getbt()->units_static[def_id].category == STATIONARY_DEF)
2285 							{
2286 								for(int i = 0; i < AAIBuildTable::ass_categories; ++i)
2287 									sector->enemy_stat_combat_power[i] += ai->Getbt()->units_static[def_id].efficiency[i];
2288 							}
2289 						}
2290 						// add unit to sector and update mobile_combat_power
2291 						else if(ai->Getbt()->units_static[def_id].category >= GROUND_ASSAULT)
2292 						{
2293 							// units that have been scouted long time ago matter less
2294 							last_seen = exp(cfg->SCOUTING_MEMORY_FACTOR * ((float)(last_updated_map[x + y * xLOSMapSize] - frame)) / 3600.0f  );
2295 
2296 							sector->enemy_combat_units[ai->Getbt()->units_static[def_id].category - GROUND_ASSAULT] += last_seen;
2297 							sector->enemy_combat_units[5] += last_seen;
2298 
2299 							for(int i = 0; i < AAIBuildTable::combat_categories; ++i)
2300 								sector->enemy_mobile_combat_power[i] += last_seen * ai->Getbt()->units_static[def_id].efficiency[i];
2301 						}
2302 					}
2303 				}
2304 			}
2305 		}
2306 	}
2307 }
2308 
UpdateSectors()2309 void AAIMap::UpdateSectors()
2310 {
2311 	for(int x = 0; x < xSectors; ++x)
2312 	{
2313 		for(int y = 0; y < ySectors; ++y)
2314 			sector[x][y].Update();
2315 	}
2316 }
2317 
GetMapTypeString(MapType map_type)2318 const char* AAIMap::GetMapTypeString(MapType map_type)
2319 {
2320 	if(map_type == LAND_MAP)
2321 		return "LAND_MAP";
2322 	else if(map_type == LAND_WATER_MAP)
2323 		return "LAND_WATER_MAP";
2324 	else if(map_type == WATER_MAP)
2325 		return "WATER_MAP";
2326 	else
2327 		return "UNKNOWN_MAP";
2328 }
2329 
GetMapTypeTextString(MapType map_type)2330 const char* AAIMap::GetMapTypeTextString(MapType map_type)
2331 {
2332 	if(map_type == LAND_MAP)
2333 		return "land map";
2334 	else if(map_type == LAND_WATER_MAP)
2335 		return "land-water map";
2336 	else if(map_type == WATER_MAP)
2337 		return "water map";
2338 	else
2339 		return "unknown map type";
2340 }
2341 
2342 
ValidSector(int x,int y)2343 bool AAIMap::ValidSector(int x, int y)
2344 {
2345 	if(x >= 0 && y >= 0 && x < xSectors && y < ySectors)
2346 		return true;
2347 	else
2348 		return false;
2349 }
2350 
GetSectorOfPos(float3 * pos)2351 AAISector* AAIMap::GetSectorOfPos(float3 *pos)
2352 {
2353 	int x = pos->x/xSectorSize;
2354 	int y = pos->z/ySectorSize;
2355 
2356 	if(ValidSector(x,y))
2357 		return &(sector[x][y]);
2358 	else
2359 		return 0;
2360 }
2361 
AddDefence(float3 * pos,int defence)2362 void AAIMap::AddDefence(float3 *pos, int defence)
2363 {
2364 	int range = ai->Getbt()->units_static[defence].range / (SQUARE_SIZE * 4);
2365 	int cell;
2366 
2367 	float power;
2368 	float air_power;
2369 	float submarine_power;
2370 
2371 	if(cfg->AIR_ONLY_MOD)
2372 	{
2373 		power = ai->Getbt()->fixed_eff[defence][0];
2374 		air_power = (ai->Getbt()->fixed_eff[defence][1] + ai->Getbt()->fixed_eff[defence][2])/2.0f;
2375 		submarine_power = ai->Getbt()->fixed_eff[defence][3];
2376 	}
2377 	else
2378 	{
2379 		if(ai->Getbt()->GetUnitDef(defence).minWaterDepth > 0)
2380 			power = (ai->Getbt()->fixed_eff[defence][2] + ai->Getbt()->fixed_eff[defence][3]) / 2.0f;
2381 		else
2382 			power = ai->Getbt()->fixed_eff[defence][0];
2383 
2384 		air_power = ai->Getbt()->fixed_eff[defence][1];
2385 		submarine_power = ai->Getbt()->fixed_eff[defence][4];
2386 	}
2387 
2388 	int xPos = (pos->x + ai->Getbt()->GetUnitDef(defence).xsize/2)/ (SQUARE_SIZE * 4);
2389 	int yPos = (pos->z + ai->Getbt()->GetUnitDef(defence).zsize/2)/ (SQUARE_SIZE * 4);
2390 
2391 	// x range will change from line to line
2392 	int xStart;
2393 	int xEnd;
2394 	int xRange;
2395 
2396 	// y range is const
2397 	int yStart = yPos - range;
2398 	int yEnd = yPos + range;
2399 
2400 	if(yStart < 0)
2401 		yStart = 0;
2402 	if(yEnd > yDefMapSize)
2403 		yEnd = yDefMapSize;
2404 
2405 	for(int y = yStart; y < yEnd; ++y)
2406 	{
2407 		// determine x-range
2408 		xRange = (int) floor( fastmath::apxsqrt2( (float) ( std::max(1, range * range - (y - yPos) * (y - yPos)) ) ) + 0.5f );
2409 
2410 		xStart = xPos - xRange;
2411 		xEnd = xPos + xRange;
2412 
2413 		if(xStart < 0)
2414 			xStart = 0;
2415 		if(xEnd > xDefMapSize)
2416 			xEnd = xDefMapSize;
2417 
2418 		for(int x = xStart; x < xEnd; ++x)
2419 		{
2420 			cell = x + xDefMapSize*y;
2421 
2422 			defence_map[cell] += power;
2423 			air_defence_map[cell] += air_power;
2424 			submarine_defence_map[cell] += submarine_power;
2425 		}
2426 	}
2427 
2428 	// further increase values close around the bulding (to prevent aai from packing buildings too close together)
2429 	xStart = xPos - 3;
2430 	xEnd = xPos + 3;
2431 	yStart = yPos - 3;
2432 	yEnd = yPos + 3;
2433 
2434 	if(xStart < 0)
2435 		xStart = 0;
2436 	if(xEnd >= xDefMapSize)
2437 		xEnd = xDefMapSize-1;
2438 
2439 	if(yStart < 0)
2440 		yStart = 0;
2441 	if(yEnd >= yDefMapSize)
2442 		yEnd = yDefMapSize-1;
2443 
2444 	float3 my_pos;
2445 
2446 	for(int y = yStart; y <= yEnd; ++y)
2447 	{
2448 		for(int x = xStart; x <= xEnd; ++x)
2449 		{
2450 			cell = x + xDefMapSize*y;
2451 
2452 			defence_map[cell] += 5000.0f;
2453 			air_defence_map[cell] += 5000.0f;
2454 			submarine_defence_map[cell] += 5000.0f;
2455 
2456 			/*my_pos.x = x * 32;
2457 			my_pos.z = y * 32;
2458 			my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
2459 			ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 8000, ai->Getcb()->GetMyAllyTeam(), false, true);
2460 			my_pos.x = (x+1) * 32;
2461 			my_pos.z = (y+1) * 32;
2462 			my_pos.y = ai->Getcb()->GetElevation(my_pos.x, my_pos.z);
2463 			ai->Getcb()->DrawUnit("ARMMINE1", my_pos, 0.0f, 8000, ai->Getcb()->GetMyAllyTeam(), false, true);*/
2464 		}
2465 	}
2466 
2467 	const std::string filename = cfg->GetFileName("AAIDefMap.txt", "", "", true);
2468 	FILE* file = fopen(filename.c_str(), "w+");
2469 	for(int y = 0; y < yDefMapSize; ++y)
2470 	{
2471 		for(int x = 0; x < xDefMapSize; ++x)
2472 		{
2473 			fprintf(file, "%i ", (int) defence_map[x + y *xDefMapSize]);
2474 		}
2475 
2476 		fprintf(file, "\n");
2477 	}
2478 	fclose(file);
2479 }
2480 
RemoveDefence(float3 * pos,int defence)2481 void AAIMap::RemoveDefence(float3 *pos, int defence)
2482 {
2483 	int cell;
2484 	int range = ai->Getbt()->units_static[defence].range / 32;
2485 
2486 	float power;
2487 	float air_power;
2488 	float submarine_power;
2489 
2490 	if(cfg->AIR_ONLY_MOD)
2491 	{
2492 		power = ai->Getbt()->fixed_eff[defence][0];
2493 		air_power = (ai->Getbt()->fixed_eff[defence][1] + ai->Getbt()->fixed_eff[defence][2])/2.0f;
2494 		submarine_power = ai->Getbt()->fixed_eff[defence][3];
2495 	}
2496 	else
2497 	{
2498 		if(ai->Getbt()->GetUnitDef(defence).minWaterDepth > 0)
2499 			power = (ai->Getbt()->fixed_eff[defence][2] + ai->Getbt()->fixed_eff[defence][3]) / 2.0f;
2500 		else
2501 			power = ai->Getbt()->fixed_eff[defence][0];
2502 
2503 		air_power = ai->Getbt()->fixed_eff[defence][1];
2504 		submarine_power = ai->Getbt()->fixed_eff[defence][4];
2505 	}
2506 
2507 	int xPos = (pos->x + ai->Getbt()->GetUnitDef(defence).xsize/2) / (SQUARE_SIZE * 4);
2508 	int yPos = (pos->z + ai->Getbt()->GetUnitDef(defence).zsize/2) / (SQUARE_SIZE * 4);
2509 
2510 	// further decrease values close around the bulding (to prevent aai from packing buildings too close together)
2511 	int xStart = xPos - 3;
2512 	int xEnd = xPos + 3;
2513 	int yStart = yPos - 3;
2514 	int yEnd = yPos + 3;
2515 
2516 	if(xStart < 0)
2517 		xStart = 0;
2518 	if(xEnd >= xDefMapSize)
2519 		xEnd = xDefMapSize-1;
2520 
2521 	if(yStart < 0)
2522 		yStart = 0;
2523 	if(yEnd >= yDefMapSize)
2524 		yEnd = yDefMapSize-1;
2525 
2526 	for(int y = yStart; y <= yEnd; ++y)
2527 	{
2528 		for(int x = xStart; x <= xEnd; ++x)
2529 		{
2530 			cell = x + xDefMapSize*y;
2531 
2532 			defence_map[cell] -= 5000.0f;
2533 			air_defence_map[cell] -= 5000.0f;
2534 			submarine_defence_map[cell] -= 5000.0f;
2535 		}
2536 	}
2537 
2538 	// y range is const
2539 	int xRange;
2540 	yStart = yPos - range;
2541 	yEnd = yPos + range;
2542 
2543 	if(yStart < 0)
2544 		yStart = 0;
2545 	if(yEnd > yDefMapSize)
2546 		yEnd = yDefMapSize;
2547 
2548 	for(int y = yStart; y < yEnd; ++y)
2549 	{
2550 		// determine x-range
2551 		xRange = (int) floor( fastmath::apxsqrt2( (float) ( std::max(1, range * range - (y - yPos) * (y - yPos)) ) ) + 0.5f );
2552 
2553 		xStart = xPos - xRange;
2554 		xEnd = xPos + xRange;
2555 
2556 		if(xStart < 0)
2557 			xStart = 0;
2558 		if(xEnd > xDefMapSize)
2559 			xEnd = xDefMapSize;
2560 
2561 		for(int x = xStart; x < xEnd; ++x)
2562 		{
2563 			cell = x + xDefMapSize*y;
2564 
2565 			defence_map[cell] -= power;
2566 			air_defence_map[cell] -= air_power;
2567 			submarine_defence_map[cell] -= submarine_power;
2568 
2569 			if(defence_map[cell] < 0)
2570 				defence_map[cell] = 0;
2571 
2572 			if(air_defence_map[cell] < 0)
2573 				air_defence_map[cell] = 0;
2574 
2575 			if(submarine_defence_map[cell] < 0)
2576 				submarine_defence_map[cell] = 0;
2577 		}
2578 	}
2579 }
2580 
GetDefenceBuildsite(float3 * best_pos,const UnitDef * def,int xStart,int xEnd,int yStart,int yEnd,UnitCategory category,float terrain_modifier,bool water)2581 float AAIMap::GetDefenceBuildsite(float3 *best_pos, const UnitDef *def, int xStart, int xEnd, int yStart, int yEnd, UnitCategory category, float terrain_modifier, bool water)
2582 {
2583 	float3 pos;
2584 	*best_pos = ZeroVector;
2585 	float my_rating, best_rating = -100000;
2586 	int edge_distance;
2587 
2588 	// get required cell-size of the building
2589 	int xSize, ySize, xPos, yPos, cell;
2590 	GetSize(def, &xSize, &ySize);
2591 
2592 	vector<float> *map = &defence_map;
2593 
2594 	if(cfg->AIR_ONLY_MOD)
2595 	{
2596 		if(category == AIR_ASSAULT || category == HOVER_ASSAULT)
2597 			map = &air_defence_map;
2598 		else if(category == SEA_ASSAULT)
2599 			map = &submarine_defence_map;
2600 	}
2601 	else if(category == AIR_ASSAULT)
2602 		map = &air_defence_map;
2603 	else if(category == SUBMARINE_ASSAULT)
2604 		map = &submarine_defence_map;
2605 
2606 	float range =  ai->Getbt()->units_static[def->id].range / 8.0;
2607 
2608 	const std::string filename = cfg->GetFileName("AAIDebug.txt", "", "", true);
2609 	FILE* file = fopen(filename.c_str(), "w+");
2610 	fprintf(file, "Search area: (%i, %i) x (%i, %i)\n", xStart, yStart, xEnd, yEnd);
2611 	fprintf(file, "Range: %g\n", range);
2612 
2613 	// check rect
2614 	for(yPos = yStart; yPos < yEnd; yPos += 4)
2615 	{
2616 		for(xPos = xStart; xPos < xEnd; xPos += 4)
2617 		{
2618 			// check if buildmap allows construction
2619 			if(CanBuildAt(xPos, yPos, xSize, ySize, water))
2620 			{
2621 
2622 				cell = (xPos/4 + xDefMapSize * yPos/4);
2623 
2624 				my_rating = terrain_modifier * plateau_map[cell] - (*map)[cell] + 0.5f *  (float)(rand()%10);
2625 				//my_rating = - (*map)[cell];
2626 
2627 				// determine minimum distance from buildpos to the edges of the map
2628 				edge_distance = GetEdgeDistance(xPos, yPos);
2629 
2630 				fprintf(file, "Pos: (%i,%i) -> Def map cell %i -> rating: %i  , edge_dist: %i\n",xPos, yPos, cell, (int)my_rating, edge_distance);
2631 
2632 				// prevent aai from building defences too close to the edges of the map
2633 				if( (float)edge_distance < range)
2634 					my_rating -= (range - (float)edge_distance) * (range - (float)edge_distance);
2635 
2636 				if(my_rating > best_rating)
2637 				{
2638 					pos.x = xPos;
2639 					pos.z = yPos;
2640 
2641 					// buildmap allows construction, now check if otherwise blocked
2642 					BuildMapPos2Pos(&pos, def);
2643 					Pos2FinalBuildPos(&pos, def);
2644 
2645 					if(ai->Getcb()->CanBuildAt(def, pos))
2646 					{
2647 						*best_pos = pos;
2648 						best_rating = my_rating;
2649 					}
2650 				}
2651 			}
2652 		}
2653 	}
2654 
2655 	fclose(file);
2656 
2657 	return best_rating;
2658 }
2659 
GetContinentID(int x,int y)2660 int AAIMap::GetContinentID(int x, int y)
2661 {
2662 	return continent_map[(y/4) * xContMapSize + x / 4];
2663 }
2664 
GetContinentID(float3 * pos)2665 int AAIMap::GetContinentID(float3 *pos)
2666 {
2667 	int x = pos->x / 32;
2668 	int y = pos->z / 32;
2669 
2670 	// check if pos inside of the map
2671 	if(x < 0)
2672 		x = 0;
2673 	else if(x >= xContMapSize)
2674 		x = xContMapSize - 1;
2675 
2676 	if(y < 0)
2677 		y = 0;
2678 	else if(y >= yContMapSize)
2679 		y = yContMapSize - 1;
2680 
2681 	return continent_map[y * xContMapSize + x];
2682 }
2683 
GetSmartContinentID(float3 * pos,unsigned int unit_movement_type)2684 int AAIMap::GetSmartContinentID(float3 *pos, unsigned int unit_movement_type)
2685 {
2686 	// check if non sea/amphib unit in shallow water
2687 	if(ai->Getcb()->GetElevation(pos->x, pos->z) < 0 && unit_movement_type & MOVE_TYPE_GROUND)
2688 	{
2689 		//look for closest land cell
2690 		for(int k = 1; k < 10; ++k)
2691 		{
2692 			if(ai->Getcb()->GetElevation(pos->x + k * 16, pos->z) > 0)
2693 			{
2694 				pos->x += k *16;
2695 				break;
2696 			}
2697 			else if(ai->Getcb()->GetElevation(pos->x - k * 16, pos->z) > 0)
2698 			{
2699 				pos->x -= k *16;
2700 				break;
2701 			}
2702 			else if(ai->Getcb()->GetElevation(pos->x, pos->z + k * 16) > 0)
2703 			{
2704 				pos->z += k *16;
2705 				break;
2706 			}
2707 			else if(ai->Getcb()->GetElevation(pos->x, pos->z - k * 16) > 0)
2708 			{
2709 				pos->z -= k *16;
2710 				break;
2711 			}
2712 		}
2713 	}
2714 
2715 	int x = pos->x / 32;
2716 	int y = pos->z / 32;
2717 
2718 	// check if pos inside of the map
2719 	if(x < 0)
2720 		x = 0;
2721 	else if(x >= xContMapSize)
2722 		x = xContMapSize - 1;
2723 
2724 	if(y < 0)
2725 		y = 0;
2726 	else if(y >= yContMapSize)
2727 		y = yContMapSize - 1;
2728 
2729 	return continent_map[y * xContMapSize + x];
2730 }
2731 
LocatedOnSmallContinent(float3 * pos)2732 bool AAIMap::LocatedOnSmallContinent(float3 *pos)
2733 {
2734 	return continents[GetContinentID(pos)].size < 0.25f * (avg_land_continent_size + avg_water_continent_size);
2735 }
2736 
2737 
UnitKilledAt(float3 * pos,UnitCategory category)2738 void AAIMap::UnitKilledAt(float3 *pos, UnitCategory category)
2739 {
2740 	int x = pos->x / xSectorSize;
2741 	int y = pos->z / ySectorSize;
2742 
2743 	if(sector[x][y].distance_to_base > 0)
2744 		sector[x][y].lost_units[category-COMMANDER] += 1;
2745 }
2746 
GetEdgeDistance(int xPos,int yPos)2747 int AAIMap::GetEdgeDistance(int xPos, int yPos)
2748 {
2749 	int edge_distance = xPos;
2750 
2751 	if(xMapSize - xPos < edge_distance)
2752 		edge_distance = xMapSize - xPos;
2753 
2754 	if(yPos < edge_distance)
2755 		edge_distance = yPos;
2756 
2757 	if(yMapSize - yPos < edge_distance)
2758 		edge_distance = yMapSize - yPos;
2759 
2760 	return edge_distance;
2761 }
2762 
GetEdgeDistance(float3 * pos)2763 float AAIMap::GetEdgeDistance(float3 *pos)
2764 {
2765 	float edge_distance = pos->x;
2766 
2767 	if(xSize - pos->x < edge_distance)
2768 		edge_distance = xSize - pos->x;
2769 
2770 	if(pos->z < edge_distance)
2771 		edge_distance = pos->z;
2772 
2773 	if(ySize - pos->z < edge_distance)
2774 		edge_distance = ySize - pos->z;
2775 
2776 	return edge_distance;
2777 }
2778