00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00031
00032
00033
00034
00035 #include <string.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038
00039 #include "stratagus.h"
00040 #include "unit.h"
00041 #include "unit_cache.h"
00042 #include "unittype.h"
00043 #include "upgrade.h"
00044 #include "map.h"
00045 #include "pathfinder.h"
00046 #include "ai_local.h"
00047 #include "actions.h"
00048 #include "player.h"
00049
00050
00051
00052
00053
00054 #define COLLECT_RESOURCES_INTERVAL 4
00055
00056
00057
00058
00059
00069 static int AiCheckCosts(const int *costs)
00070 {
00071 int err = 0;
00072
00073 for (int i = 0; i < MaxCosts; ++i) {
00074 if (costs[i] && AiPlayer->Player->ProductionRate[i] == 0 &&
00075 AiPlayer->Player->StoredResources[i] == 0) {
00076 err |= (1 << i);
00077 }
00078 }
00079
00080 return err;
00081 }
00082
00092 static int AiCheckUnitTypeCosts(const CUnitType *type)
00093 {
00094 return AiCheckCosts(type->ProductionCosts);
00095 }
00096
00108 int AiEnemyUnitsInDistance(const CPlayer *player, const CUnitType *type, int x, int y, unsigned range)
00109 {
00110 const CUnit *dest;
00111 CUnit *table[UnitMax];
00112 unsigned n;
00113 unsigned i;
00114 int e;
00115
00116
00117
00118
00119 n = UnitCache.Select(x - range, y - range, x + range, y + range, table, UnitMax);
00120
00121
00122
00123
00124 for (e = i = 0; i < n; ++i) {
00125 dest = table[i];
00126
00127
00128
00129 if (dest->Removed || dest->Orders[0]->Action == UnitActionDie) {
00130 continue;
00131 }
00132
00133 if (!player->IsEnemy(dest)) {
00134 continue;
00135 }
00136
00137
00138
00139 if (!type || CanTarget(dest->Type, type)) {
00140 ++e;
00141 }
00142 }
00143
00144 return e;
00145 }
00146
00155 int AiEnemyUnitsInDistance(const CUnit *unit, unsigned range)
00156 {
00157 range += std::max(unit->Type->TileWidth, unit->Type->TileHeight);
00158 return AiEnemyUnitsInDistance(unit->Player, unit->Type, unit->X, unit->Y, range);
00159 }
00160
00171 static int AiBuildBuilding(const CUnitType *type, CUnitType *building)
00172 {
00173 CUnit *table[UnitMax];
00174 CUnit *unit;
00175 int nunits;
00176 int i;
00177 int num;
00178 int x;
00179 int y;
00180
00181
00182
00183
00184 nunits = FindPlayerUnitsByType(AiPlayer->Player, type, table, UnitMax);
00185 for (num = i = 0; i < nunits; ++i) {
00186 unit = table[i];
00187 for (x = 0; x < unit->OrderCount; ++x) {
00188 if (unit->Orders[x]->Action == UnitActionBuild ||
00189 unit->Orders[x]->Action == UnitActionRepair) {
00190 break;
00191 }
00192 }
00193 if (x == unit->OrderCount) {
00194 table[num++] = unit;
00195 }
00196 }
00197
00198 if (num == 0) {
00199
00200 return 0;
00201 }
00202
00203
00204 unit = table[SyncRand() % num];
00205
00206
00207 if (AiFindBuildingPlace(unit, building, &x, &y)) {
00208 CommandBuildBuilding(unit, x, y, building, FlushCommands);
00209 return 1;
00210 }
00211 return 0;
00212 }
00213
00224 static int AiTrainUnit(const CUnitType *type, CUnitType *what)
00225 {
00226 CUnit *table[UnitMax];
00227 CUnit *unit;
00228 int nunits;
00229 int i;
00230 int num;
00231
00232
00233
00234
00235 nunits = FindPlayerUnitsByType(AiPlayer->Player, type, table, UnitMax);
00236 for (num = i = 0; i < nunits; ++i) {
00237 unit = table[i];
00238 if (unit->IsIdle()) {
00239 table[num++] = unit;
00240 }
00241 }
00242
00243 for (i = 0; i < num; ++i) {
00244 unit = table[i];
00245
00246 CommandTrainUnit(unit, what, FlushCommands);
00247
00248 return 1;
00249 }
00250
00251 return 0;
00252 }
00253
00259 int AiCountUnitBuilders(CUnitType *type)
00260 {
00261 int result;
00262 int i;
00263 int n;
00264 const int *unit_count;
00265 std::vector<std::vector<CUnitType *> > *tablep;
00266 const std::vector<CUnitType *> *table;
00267
00268 if (UnitIdAllowed(AiPlayer->Player, type->Slot) == 0) {
00269 DebugPrint("Can't build `%s' now\n" _C_ type->Ident.c_str());
00270 return 0;
00271 }
00272
00273
00274
00275 if (type->Building) {
00276 n = AiHelpers.Build.size();
00277 tablep = &AiHelpers.Build;
00278 } else {
00279 n = AiHelpers.Train.size();
00280 tablep = &AiHelpers.Train;
00281 }
00282 if (type->Slot > n) {
00283 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00284 return 0;
00285 }
00286 table = &(*tablep)[type->Slot];
00287 if (!table->size()) {
00288 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00289 return 0;
00290 }
00291
00292 unit_count = AiPlayer->Player->UnitTypesCount;
00293 result = 0;
00294 for (i = 0; i < (int)table->size(); ++i) {
00295
00296
00297
00298 result += unit_count[(*table)[i]->Slot];
00299 }
00300 return result;
00301 }
00302
00312 static int AiMakeUnit(CUnitType *type)
00313 {
00314 int i;
00315 int n;
00316 const int *unit_count;
00317 std::vector<std::vector<CUnitType *> > *tablep;
00318 std::vector<CUnitType *> *table;
00319
00320 int usableTypes[UnitTypeMax + 1];
00321 int usableTypesCount;
00322 int currentType;
00323
00324
00325 usableTypesCount = AiFindAvailableUnitTypeEquiv(type, usableTypes);
00326
00327
00328 for (currentType = 0; currentType < usableTypesCount; ++currentType) {
00329
00330 type = UnitTypes[usableTypes[currentType]];
00331
00332
00333
00334
00335 if (type->Building) {
00336 n = AiHelpers.Build.size();
00337 tablep = &AiHelpers.Build;
00338 } else {
00339 n = AiHelpers.Train.size();
00340 tablep = &AiHelpers.Train;
00341 }
00342 if (type->Slot > n) {
00343 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00344 continue;
00345 }
00346 table = &(*tablep)[type->Slot];
00347 if (!table->size()) {
00348 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00349 continue;
00350 }
00351
00352 unit_count = AiPlayer->Player->UnitTypesCount;
00353 for (i = 0; i < (int)table->size(); ++i) {
00354
00355
00356
00357 if (unit_count[(*table)[i]->Slot]) {
00358 if (type->Building) {
00359 if (AiBuildBuilding((*table)[i], type)) {
00360 return 1;
00361 }
00362 } else {
00363 if (AiTrainUnit((*table)[i], type)) {
00364 return 1;
00365 }
00366 }
00367 }
00368 }
00369 }
00370 return 0;
00371 }
00372
00376 static void AiCheckingWork(void)
00377 {
00378 int c;
00379 CUnitType *type;
00380 AiBuildQueue *queue;
00381
00382
00383
00384
00385 int sz = AiPlayer->UnitTypeBuilt.size();
00386 for (int i = 0; i < sz; ++i) {
00387 queue = &AiPlayer->UnitTypeBuilt[AiPlayer->UnitTypeBuilt.size() - sz + i];
00388 type = queue->Type;
00389
00390
00391
00392
00393
00394
00395
00396
00397 if (queue->Want > queue->Made && AiPlayer->Player->CheckLimits(type) < 0) {
00398 continue;
00399 }
00400
00401
00402
00403 if ((c = AiCheckUnitTypeCosts(type))) {
00404 AiPlayer->NeededMask |= c;
00405
00406
00407
00408 continue;
00409 } else if (queue->Want > queue->Made && queue->Wait <= GameCycle) {
00410 if (AiMakeUnit(type)) {
00411
00412 queue = &AiPlayer->UnitTypeBuilt[AiPlayer->UnitTypeBuilt.size() - sz + i];
00413 ++queue->Made;
00414 queue->Wait = 0;
00415 } else if (queue->Type->Building) {
00416
00417 if (queue->Wait == 0) {
00418 queue->Wait = GameCycle + 150;
00419 } else {
00420 queue->Wait = GameCycle + 450;
00421 }
00422 }
00423 }
00424 }
00425 }
00426
00427
00428
00429
00430
00439 static int AiAssignHarvester(CUnit *unit, int resource)
00440 {
00441 std::vector<CUnitType *>::iterator i;
00442 int exploremask;
00443 CUnit *dest;
00444 int res = resource;
00445
00446
00447 if (unit->Removed) {
00448 return 0;
00449 }
00450
00451 if (res == -1) {
00452
00453 for (int i = 0; i < MaxCosts; ++i) {
00454 if (unit->Player->ProductionRate[i] == 0) {
00455 res = i;
00456 if (unit->Player->StoredResources[i] == 0) {
00457 break;
00458 }
00459 }
00460 }
00461 }
00462
00463
00464
00465
00466 dest = UnitFindResource(unit, unit->X, unit->Y, 1000, res);
00467 if (!dest && res != -1) {
00468 dest = UnitFindResource(unit, unit->X, unit->Y, 1000, -1);
00469 }
00470 if (dest) {
00471 CommandResource(unit, dest, FlushCommands);
00472 return 1;
00473 }
00474
00475 exploremask = 0;
00476 for (i = UnitTypes.begin(); i != UnitTypes.end(); i++) {
00477 if (*i && (*i)->CanHarvestFrom) {
00478 switch ((*i)->UnitType) {
00479 case UnitTypeLand:
00480 exploremask |= MapFieldLandUnit;
00481 break;
00482 case UnitTypeFly:
00483 exploremask |= MapFieldAirUnit;
00484 break;
00485 case UnitTypeNaval:
00486 exploremask |= MapFieldSeaUnit;
00487 break;
00488 default:
00489 Assert(0);
00490 }
00491 }
00492 }
00493
00494 AiExplore(unit->X, unit->Y, exploremask);
00495
00496
00497 return 0;
00498 }
00499
00503 static void AiCollectResources(void)
00504 {
00505 int i;
00506 int needed = -1;
00507 std::vector<CUnit *> harvesters;
00508
00509
00510 if (AiPlayer->NeededMask) {
00511 int n = AiPlayer->NeededMask;
00512 while (n != 0) {
00513 n >>= 1;
00514 ++needed;
00515 }
00516 }
00517
00518
00519 for (i = 0; i < AiPlayer->Player->TotalNumUnits; ++i) {
00520 CUnit *unit = AiPlayer->Player->Units[i];
00521
00522
00523 if (!unit->Type->Harvester) {
00524 continue;
00525 }
00526
00527 harvesters.push_back(unit);
00528 }
00529
00530
00531 if (needed != -1 && harvesters.size() != 0) {
00532 int best = -1;
00533 for (i = 0; i < (int)harvesters.size(); ++i) {
00534
00535 if (harvesters[i]->IsIdle()) {
00536 best = i;
00537 break;
00538 }
00539
00540 if (harvesters[i]->Orders[0]->Action == UnitActionResource &&
00541 harvesters[i]->Data.Harvest.CurrentProduction[needed] == 0) {
00542 best = i;
00543 }
00544 }
00545
00546 if (best == -1) {
00547
00548 best = SyncRand() % harvesters.size();
00549 }
00550
00551 if (AiAssignHarvester(harvesters[best], needed)) {
00552 harvesters.erase(harvesters.begin() + best);
00553 }
00554 }
00555
00556
00557 std::vector<CUnit *> idleHarvesters;
00558 for (i = 0; i < (int)harvesters.size(); ++i) {
00559 if (harvesters[i]->IsIdle()) {
00560 idleHarvesters.push_back(harvesters[i]);
00561 }
00562 }
00563 if (idleHarvesters.size() > 0) {
00564 AiAssignHarvester(idleHarvesters[SyncRand() % idleHarvesters.size()], -1);
00565 }
00566 }
00567
00568
00569
00570
00571
00580 static int AiRepairBuilding(const CUnitType *type, CUnit *building)
00581 {
00582 CUnit *table[UnitMax];
00583 CUnit *unit;
00584 CUnit *unit_temp;
00585 int distance[UnitMax];
00586 int rX;
00587 int rY;
00588 int r_temp;
00589 int index_temp;
00590 int nunits;
00591 int i;
00592 int j;
00593 int k;
00594 int num;
00595
00596 #ifdef DEBUG
00597 unit = NoUnitP;
00598 #endif
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609 nunits = FindPlayerUnitsByType(AiPlayer->Player, type, table, UnitMax);
00610 for (num = i = 0; i < nunits; ++i) {
00611 unit = table[i];
00612 if (unit->Type->RepairRange &&
00613 (unit->Orders[0]->Action == UnitActionResource ||
00614 unit->Orders[0]->Action == UnitActionStill) && unit->OrderCount == 1) {
00615 table[num++] = unit;
00616 }
00617 }
00618
00619
00620 for (i = 0; i < num; ++i) {
00621 unit = table[i];
00622
00623 if ((rX = unit->X - building->X) < 0) {
00624 rX = -rX;
00625 }
00626 if ((rY = unit->Y - building->Y) < 0) {
00627 rY = -rY;
00628 }
00629 if (rX < rY) {
00630 distance[i] = rX;
00631 } else {
00632 distance[i] = rY;
00633 }
00634 }
00635 for (i = 0; i < num; ++i) {
00636 r_temp = distance[i];
00637 index_temp = i;
00638 for (j = i; j < num; ++j) {
00639 if (distance[j] < r_temp) {
00640 r_temp = distance[j];
00641 index_temp = j;
00642 }
00643 }
00644 if (index_temp > i) {
00645 unit_temp = table[index_temp];
00646 table[index_temp] = table[i];
00647 distance[index_temp] = distance[i];
00648 table[i] = unit_temp;
00649 distance[i] = r_temp;
00650 }
00651 }
00652
00653
00654
00655 if ((j = AiPlayer->TriedRepairWorkers[UnitNumber(building)]) > num) {
00656 j = AiPlayer->TriedRepairWorkers[UnitNumber(building)] = 0;
00657 }
00658
00659 for (k = i = j; i < num && i < j + 3; ++i) {
00660
00661 unit = table[i];
00662
00663 if (UnitReachable(unit, building, unit->Type->RepairRange)) {
00664 CommandRepair(unit, 0, 0, building, FlushCommands);
00665 return 1;
00666 }
00667 k = i;
00668 }
00669 AiPlayer->TriedRepairWorkers[UnitNumber(building)] = k + 1;
00670 return 0;
00671 }
00672
00680 static int AiRepairUnit(CUnit *unit)
00681 {
00682 int i;
00683 int n;
00684 const CUnitType *type;
00685 const int *unit_count;
00686 std::vector<std::vector<CUnitType *> > *tablep;
00687 std::vector<CUnitType *> *table;
00688
00689 n = AiHelpers.Repair.size();
00690 tablep = &AiHelpers.Repair;
00691 type = unit->Type;
00692 if (type->Slot > n) {
00693 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00694 return 0;
00695 }
00696 table = &(*tablep)[type->Slot];
00697 if (!table->size()) {
00698 DebugPrint("Nothing known about `%s'\n" _C_ type->Ident.c_str());
00699 return 0;
00700 }
00701
00702 unit_count = AiPlayer->Player->UnitTypesCount;
00703 for (i = 0; i < (int)table->size(); ++i) {
00704
00705
00706
00707 if (unit_count[(*table)[i]->Slot]) {
00708 if (AiRepairBuilding((*table)[i], unit)) {
00709 return 1;
00710 }
00711 }
00712 }
00713
00714 return 0;
00715 }
00716
00720 static void AiCheckRepair(void)
00721 {
00722 int i;
00723 int k;
00724 int n;
00725 bool repair_flag;
00726 CUnit *unit;
00727
00728 n = AiPlayer->Player->TotalNumUnits;
00729 k = 0;
00730
00731 for (i = n - 1; i >= 0; --i) {
00732 unit = AiPlayer->Player->Units[i];
00733 if (UnitNumber(unit) == AiPlayer->LastRepairBuilding) {
00734 k = i + 1;
00735 }
00736 }
00737
00738
00739 for (i = k; i < n; ++i) {
00740 unit = AiPlayer->Player->Units[i];
00741 repair_flag = true;
00742
00743
00744 if (unit->Type->RepairHP &&
00745 unit->Orders[0]->Action != UnitActionBuilt &&
00746 unit->Variable[HP_INDEX].Value < unit->Variable[HP_INDEX].Max &&
00747 unit->Attacked + 5 * CYCLES_PER_SECOND < GameCycle) {
00748
00749
00750 if (AiEnemyUnitsInDistance(unit, unit->Stats->Variables[SIGHTRANGE_INDEX].Max)) {
00751 continue;
00752 }
00753
00754
00755
00756 for (int j = 0; j < MaxCosts; ++j) {
00757 if (unit->Type->ProductionCosts[j] && AiPlayer->Player->ProductionRate[j] == 0 &&
00758 AiPlayer->Player->StoredResources[j] == 0) {
00759 repair_flag = false;
00760 break;
00761 }
00762 }
00763
00764
00765
00766
00767 if (repair_flag) {
00768 AiRepairUnit(unit);
00769 AiPlayer->LastRepairBuilding = UnitNumber(unit);
00770 return;
00771 }
00772 }
00773
00774
00775 if (unit->Orders[0]->Action == UnitActionBuilt) {
00776 int j;
00777 for (j = 0; j < AiPlayer->Player->TotalNumUnits; ++j) {
00778 COrder *order = AiPlayer->Player->Units[j]->Orders[0];
00779 if (order->Action == UnitActionRepair && order->Goal == unit) {
00780 break;
00781 }
00782 }
00783 if (j == AiPlayer->Player->TotalNumUnits) {
00784
00785 for (j = 0; j < MaxCosts; ++j) {
00786
00787 if (AiPlayer->Player->StoredResources[j] < unit->Type->ProductionCosts[j]) {
00788 break;
00789 }
00790 }
00791 if (j == MaxCosts) {
00792 AiRepairUnit(unit);
00793 AiPlayer->LastRepairBuilding = UnitNumber(unit);
00794 return;
00795 }
00796 }
00797 }
00798 }
00799 AiPlayer->LastRepairBuilding = 0;
00800 }
00801
00810 void AiAddUnitTypeRequest(CUnitType *type, int count)
00811 {
00812 AiBuildQueue queue;
00813
00814 queue.Type = type;
00815 queue.Want = count;
00816 queue.Made = 0;
00817 AiPlayer->UnitTypeBuilt.push_back(queue);
00818 }
00819
00827 void AiExplore(int x, int y, int mask)
00828 {
00829 AiExplorationRequest req;
00830
00831
00832 req.X = x;
00833 req.Y = y;
00834 req.Mask = mask;
00835
00836 AiPlayer->FirstExplorationRequest.insert(
00837 AiPlayer->FirstExplorationRequest.begin(), req);
00838 }
00839
00843 void AiResourceManager(void)
00844 {
00845
00846
00847
00848 AiCheckingWork();
00849
00850
00851
00852
00853 if ((GameCycle / CYCLES_PER_SECOND) % COLLECT_RESOURCES_INTERVAL ==
00854 (unsigned long)AiPlayer->Player->Index % COLLECT_RESOURCES_INTERVAL) {
00855 AiCollectResources();
00856 }
00857
00858
00859
00860
00861 AiCheckRepair();
00862
00863 AiPlayer->NeededMask = 0;
00864 }
00865