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 <stdio.h>
00036 #include <stdlib.h>
00037
00038 #include "stratagus.h"
00039 #include "missile.h"
00040 #include "unittype.h"
00041 #include "unit_cache.h"
00042 #include "map.h"
00043 #include "pathfinder.h"
00044 #include "actions.h"
00045 #include "ai_local.h"
00046 #include "player.h"
00047
00048
00049
00050
00051
00052
00053
00054
00055
00065 static CUnit *EnemyOnMapTile(const CUnit *source, int tx, int ty)
00066 {
00067 CUnit *table[UnitMax];
00068 CUnit *unit;
00069 CUnit *best;
00070 const CUnitType *type;
00071 int n;
00072 int i;
00073
00074 n = UnitCache.Select(tx, ty, table, UnitMax);
00075 best = NoUnitP;
00076 for (i = 0; i < n; ++i) {
00077 unit = table[i];
00078
00079
00080
00081 if (unit->Removed ||
00082
00083 unit->Orders[0]->Action == UnitActionDie) {
00084 continue;
00085 }
00086 type = unit->Type;
00087 if (tx < unit->X || tx >= unit->X + type->TileWidth ||
00088 ty < unit->Y || ty >= unit->Y + type->TileHeight) {
00089 continue;
00090 }
00091 if (!CanTarget(source->Type, unit->Type)) {
00092 continue;
00093 }
00094 if (!source->Player->IsEnemy(unit)) {
00095 continue;
00096 }
00097
00098
00099
00100 if (!best || best->Type->Priority < unit->Type->Priority) {
00101 best = unit;
00102 }
00103 }
00104 return best;
00105 }
00106
00115 static void AiMarkWaterTransporter(const CUnit *unit, unsigned char *matrix)
00116 {
00117 static const int xoffset[] = { 0, -1, +1, 0, -1, +1, -1, +1 };
00118 static const int yoffset[] = { -1, 0, 0, +1, -1, -1, +1, +1 };
00119 struct p {
00120 unsigned short X;
00121 unsigned short Y;
00122 } *points;
00123 int size;
00124 int x;
00125 int y;
00126 int rx;
00127 int ry;
00128 int mask;
00129 int wp;
00130 int rp;
00131 int ep;
00132 int i;
00133 int w;
00134 unsigned char *m;
00135
00136 x = unit->X;
00137 y = unit->Y;
00138 w = Map.Info.MapWidth + 2;
00139 matrix += w + w + 2;
00140 if (matrix[x + y * w]) {
00141 DebugPrint("Done\n");
00142 return;
00143 }
00144
00145 size = Map.Info.MapWidth * Map.Info.MapHeight / 4;
00146 points = new p[size];
00147
00148
00149
00150
00151 mask = unit->Type->MovementMask;
00152
00153 mask &= ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit);
00154
00155 points[0].X = x;
00156 points[0].Y = y;
00157 rp = 0;
00158 matrix[x + y * w] = 66;
00159 ep = wp = 1;
00160
00161
00162
00163
00164 for (;;) {
00165 while (rp != ep) {
00166 rx = points[rp].X;
00167 ry = points[rp].Y;
00168 for (i = 0; i < 8; ++i) {
00169 x = rx + xoffset[i];
00170 y = ry + yoffset[i];
00171 m = matrix + x + y * w;
00172 if (*m) {
00173 continue;
00174 }
00175
00176 if (CanMoveToMask(x, y, mask)) {
00177 *m = 66;
00178 points[wp].X = x;
00179 points[wp].Y = y;
00180 if (++wp >= size) {
00181 wp = 0;
00182 }
00183
00184
00185
00186
00187 }
00188 }
00189
00190 if (++rp >= size) {
00191 rp = 0;
00192 }
00193 }
00194
00195
00196
00197
00198 if (rp == wp) {
00199 break;
00200 }
00201 ep = wp;
00202 }
00203
00204 delete[] points;
00205 }
00206
00218 static int AiFindTarget(const CUnit *unit, unsigned char *matrix, int *dx, int *dy,
00219 int *ds)
00220 {
00221 static const int xoffset[] = { 0, -1, +1, 0, -1, +1, -1, +1 };
00222 static const int yoffset[] = { -1, 0, 0, +1, -1, -1, +1, +1 };
00223 struct p {
00224 unsigned short X;
00225 unsigned short Y;
00226 unsigned char State;
00227 } *points;
00228 int size;
00229 int x;
00230 int y;
00231 int rx;
00232 int ry;
00233 int mask;
00234 int wp;
00235 int rp;
00236 int ep;
00237 int i;
00238 int w;
00239 enum {
00240 OnWater,
00241 OnLand,
00242 OnIsle
00243 };
00244 unsigned char state;
00245 unsigned char *m;
00246
00247 size = Map.Info.MapWidth * Map.Info.MapHeight / 4;
00248 points = new p[size];
00249
00250 x = unit->X;
00251 y = unit->Y;
00252
00253 w = Map.Info.MapWidth + 2;
00254 mask = unit->Type->MovementMask;
00255
00256 mask &= ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit);
00257
00258 points[0].X = x;
00259 points[0].Y = y;
00260 points[0].State = OnLand;
00261 matrix += w + w + 2;
00262 rp = 0;
00263 matrix[x + y * w] = 1;
00264 ep = wp = 1;
00265
00266
00267
00268
00269 for (;;) {
00270 while (rp != ep) {
00271 rx = points[rp].X;
00272 ry = points[rp].Y;
00273 state = points[rp].State;
00274 for (i = 0; i < 8; ++i) {
00275 x = rx + xoffset[i];
00276 y = ry + yoffset[i];
00277 m = matrix + x + y * w;
00278
00279 if (state != OnWater) {
00280 if (*m) {
00281 if (state == OnLand && *m == 66) {
00282 DebugPrint("->Water\n");
00283 *m = 6;
00284 points[wp].X = x;
00285 points[wp].Y = y;
00286 points[wp].State = OnWater;
00287 if (++wp >= size) {
00288 wp = 0;
00289 }
00290 }
00291 continue;
00292 }
00293
00294
00295
00296 if (EnemyOnMapTile(unit, x, y)) {
00297 DebugPrint("Target found %d,%d-%d\n" _C_ x _C_ y _C_ state);
00298 *dx = x;
00299 *dy = y;
00300 *ds = state;
00301 delete[] points;
00302 return 1;
00303 }
00304
00305 if (CanMoveToMask(x, y, mask)) {
00306
00307 *m = 1;
00308 points[wp].X = x;
00309 points[wp].Y = y;
00310 points[wp].State = state;
00311 if (++wp >= size) {
00312 wp = 0;
00313 }
00314 } else {
00315 *m = 99;
00316 }
00317 } else {
00318 if (*m) {
00319 if (*m == 66) {
00320 *m = 6;
00321 points[wp].X = x;
00322 points[wp].Y = y;
00323 points[wp].State = OnWater;
00324 if (++wp >= size) {
00325 wp = 0;
00326 }
00327 }
00328 continue;
00329 }
00330 if (CanMoveToMask(x, y, mask)) {
00331 DebugPrint("->Land\n");
00332 *m = 1;
00333 points[wp].X = x;
00334 points[wp].Y = y;
00335 points[wp].State = OnIsle;
00336 if (++wp >= size) {
00337 wp = 0;
00338 }
00339 } else {
00340 *m = 99;
00341 }
00342 }
00343 }
00344
00345 if (++rp >= size) {
00346 rp = 0;
00347 }
00348 }
00349
00350
00351
00352
00353 if (rp == wp) {
00354 break;
00355 }
00356 ep = wp;
00357 }
00358 delete[] points;
00359 return 0;
00360 }
00361
00375 int AiPlanAttack(AiForce *force)
00376 {
00377 unsigned char *watermatrix;
00378 const CUnit *aiunit = NULL;
00379 int x;
00380 int y;
00381 int i;
00382 int state;
00383 CUnit *transporter;
00384
00385 DebugPrint("Planning for force #%lu of player #%d\n" _C_
00386 static_cast<long unsigned int> (force - AiPlayer->Force) _C_ AiPlayer->Player->Index);
00387
00388 watermatrix = CreateMatrix();
00389
00390
00391
00392
00393
00394 state = 1;
00395 for (i = 0; i < (int)force->Units.size(); ++i) {
00396 aiunit = force->Units[i];
00397 if (aiunit->Type->CanTransport) {
00398 DebugPrint("Transporter #%d\n" _C_ UnitNumber(aiunit));
00399 AiMarkWaterTransporter(aiunit, watermatrix);
00400 state = 0;
00401 }
00402 }
00403
00404
00405
00406
00407 transporter = NULL;
00408 if (state) {
00409 for (i = 0; i < AiPlayer->Player->TotalNumUnits; ++i) {
00410 CUnit *unit;
00411
00412 unit = AiPlayer->Player->Units[i];
00413 if (unit->Type->CanTransport && unit->IsIdle()) {
00414 DebugPrint("Assign any transporter\n");
00415 AiMarkWaterTransporter(unit, watermatrix);
00416
00417 transporter = unit;
00418 state = 0;
00419 }
00420 }
00421 }
00422
00423 if (state) {
00424 DebugPrint("No transporter available\n");
00425
00426 return 0;
00427 }
00428
00429
00430
00431
00432 for (i = 0; i < (int)force->Units.size(); ++i) {
00433 aiunit = force->Units[i];
00434 if (aiunit->Type->UnitType == UnitTypeLand) {
00435 DebugPrint("Land unit %d\n" _C_ UnitNumber(aiunit));
00436 break;
00437 }
00438 }
00439 if (i == (int)force->Units.size()) {
00440 DebugPrint("No land unit in force\n");
00441 return 0;
00442 }
00443
00444 if (AiFindTarget(aiunit, watermatrix, &x, &y, &state)) {
00445 if (transporter) {
00446 force->Units.insert(force->Units.begin(), transporter);
00447 transporter->RefsIncrease();
00448 }
00449
00450 DebugPrint("Can attack\n");
00451 force->GoalX = x;
00452 force->GoalY = y;
00453 force->MustTransport = state == 2;
00454
00455 force->State = 1;
00456 return 1;
00457 }
00458 return 0;
00459 }
00460
00464 void AiSendExplorers(void)
00465 {
00466 AiExplorationRequest *request;
00467 int requestcount;
00468 int requestid;
00469 int centerx;
00470 int centery;
00471 int x;
00472 int y;
00473 int i;
00474 int targetok;
00475 int ray;
00476 int trycount;
00477 int outtrycount;
00478
00479 CUnit **unit;
00480 CUnitType *type;
00481 CUnit *bestunit;
00482 int distance;
00483 int bestdistance;
00484 int flyeronly;
00485
00486
00487 requestcount = AiPlayer->FirstExplorationRequest.size();
00488
00489
00490 if (!requestcount) {
00491 return;
00492 }
00493
00494 outtrycount = 0;
00495 do {
00496 bestunit = 0;
00497 ++outtrycount;
00498
00499
00500 requestid = SyncRand() % requestcount;
00501 request = &AiPlayer->FirstExplorationRequest[requestid];
00502
00503
00504 centerx = request->X;
00505 centery = request->Y;
00506 ray = 3;
00507 trycount = 0;
00508
00509 do {
00510 targetok = 0;
00511
00512 x = centerx + SyncRand() % (2 * ray + 1) - ray;
00513 y = centery + SyncRand() % (2 * ray + 1) - ray;
00514
00515 if (x >= 0 && y >= 0 && x < Map.Info.MapWidth && y < Map.Info.MapHeight) {
00516 targetok = !Map.IsFieldExplored(AiPlayer->Player, x, y);
00517 }
00518
00519 ray = 3 * ray / 2;
00520 ++trycount;
00521 } while (trycount < 8 && !targetok);
00522
00523 if (!targetok) {
00524 continue;
00525 }
00526
00527
00528
00529
00530 flyeronly = 0;
00531 bestdistance = -1;
00532
00533 unit = AiPlayer->Player->Units;
00534 for (i = AiPlayer->Player->TotalNumUnits; i > 0; ++unit) {
00535 --i;
00536
00537 if (!(*unit)->IsIdle()) {
00538 continue;
00539 }
00540
00541 if ((*unit)->X == -1 || (*unit)->Y == -1) {
00542 continue;
00543 }
00544
00545 type = (*unit)->Type;
00546
00547 if (!CanMove(*unit)) {
00548 continue;
00549 }
00550
00551 if (type->UnitType != UnitTypeFly) {
00552 if (flyeronly) {
00553 continue;
00554 }
00555 if ((request->Mask & MapFieldLandUnit) && type->UnitType != UnitTypeLand) {
00556 continue;
00557 }
00558 if ((request->Mask & MapFieldSeaUnit) && type->UnitType != UnitTypeNaval) {
00559 continue;
00560 }
00561 } else {
00562 flyeronly = 1;
00563 }
00564
00565 distance = ((*unit)->X - x) * ((*unit)->X - x) + ((*unit)->Y - y) * ((*unit)->Y - y);
00566 if (bestdistance == -1 || distance <= bestdistance ||
00567 (bestunit->Type->UnitType != UnitTypeFly && type->UnitType == UnitTypeFly)) {
00568 bestdistance = distance;
00569 bestunit = (*unit);
00570 }
00571 }
00572 } while (outtrycount <= 4 && !bestunit);
00573
00574 if (bestunit) {
00575 CommandMove(bestunit, x, y, FlushCommands);
00576 AiPlayer->LastExplorationGameCycle = GameCycle;
00577 }
00578
00579
00580 AiPlayer->FirstExplorationRequest.clear();
00581 }
00582