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", §or[i][j].flat_ratio, §or[i][j].water_ratio, §or[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 ", §or[i][j].attacked_by_learned[cat], §or[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