00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "ai_common.h"
00019
00020
00021 const int attack_unitdestroyed_bonus = 90;
00022
00023
00024 void AI :: searchTargets ( Vehicle* veh, const MapCoordinate3D& pos, TargetVector& tl, int moveDist, AStar3D& vm, int hemmingBonus )
00025 {
00026
00027 npush ( veh->xpos );
00028 npush ( veh->ypos );
00029 npush ( veh->height );
00030
00031 bool unitRadarActivated = getMap()->getField(veh->getPosition())->unitHere(veh);
00032 if ( unitRadarActivated )
00033 veh->removeview();
00034
00035 veh->xpos = pos.x;
00036 veh->ypos = pos.y;
00037 veh->height = pos.getBitmappedHeight();
00038 veh->addview();
00039 int fieldsWithChangedVisibility = evaluateviewcalculation ( getMap(), veh->getPosition(), veh->typ->view, 0xff );
00040
00041
00042 VehicleAttack va ( NULL, NULL );
00043 if ( va.available ( veh )) {
00044 va.execute ( veh, -1, -1, 0 , 0, -1 );
00045 for ( int g = 0; g < va.attackableVehicles.getFieldNum(); g++ ) {
00046 int xp, yp;
00047 va.attackableVehicles.getFieldCoordinates ( g, &xp, &yp );
00048 pattackweap aw = &va.attackableVehicles.getData ( g );
00049
00050 int bestweap = -1;
00051 int targdamage = -1;
00052 int weapstrength = -1;
00053 for ( int w = 0; w < aw->count; w++ ) {
00054 tunitattacksunit uau ( veh, getfield ( xp, yp)->vehicle, 1, aw->num[w] );
00055 uau.calc();
00056 if ( uau.dv.damage > targdamage ) {
00057 bestweap = aw->num[w];
00058 targdamage = uau.dv.damage;
00059 weapstrength = aw->strength[w];
00060 } else
00061 if ( uau.dv.damage == 100 )
00062 if ( weapstrength == -1 || weapstrength > aw->strength[w] ) {
00063 bestweap = aw->num[w];
00064 targdamage = uau.dv.damage;
00065 weapstrength = aw->strength[w];
00066 }
00067 }
00068
00069 if ( bestweap == -1 )
00070 displaymessage ( "inconsistency in AI :: searchTarget", 1 );
00071
00072
00073 MoveVariant* mv = new MoveVariant;
00074
00075 tunitattacksunit uau ( veh, getfield ( xp, yp)->vehicle, 1, bestweap );
00076 mv->orgDamage = uau.av.damage;
00077 mv->damageAfterMove = uau.av.damage;
00078 mv->enemyOrgDamage = uau.dv.damage;
00079 uau.calc();
00080
00081
00082 mv->damageAfterAttack = uau.av.damage;
00083 mv->enemyDamage = uau.dv.damage;
00084 mv->enemy = getfield ( xp, yp )->vehicle;
00085 mv->movePos = pos;
00086 mv->attackx = xp;
00087 mv->attacky = yp;
00088 mv->weapNum = bestweap;
00089 mv->moveDist = moveDist;
00090 mv->attacker = veh;
00091
00092 mv->positionThreat = getFieldThreat ( pos.x, pos.y ).threat[veh->getValueType()];
00093
00094
00095 for ( int nf = 0; nf < sidenum; nf++ ) {
00096 MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( mv->attackx, mv->attacky), nf );
00097 tfield* fld = getMap()->getField(mc);
00098 if ( fld && !veh->typ->wait)
00099 mv->neighbouringFieldsReachable[nf] = (vm.fieldVisited( MapCoordinate3D(mc.x, mc.y, pos.getBitmappedHeight()) ) || ( veh->xpos == mc.x && veh->ypos == mc.y )) && !fld->building && (!fld->vehicle || fld->unitHere(veh));
00100 else
00101 mv->neighbouringFieldsReachable[nf] = false;
00102
00103 }
00104
00105 int attackerDirection = getdirection ( xp, yp, pos.x, pos.y );
00106 float hemmingFactor = 1;
00107 for ( int nf = 0; nf < sidenum-1 && nf < hemmingBonus; nf++ ) {
00108
00109 int checkDir;
00110 if ( nf == 0 )
00111 checkDir = attackerDirection+sidenum/2;
00112 else {
00113 if ( nf & 1 )
00114 checkDir = attackerDirection+sidenum/2 + (nf+1)/2;
00115 else
00116 checkDir = attackerDirection+sidenum/2 - (nf+1)/2;
00117 }
00118
00119 MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( mv->attackx, mv->attacky), checkDir%sidenum );
00120 tfield* fld = getMap()->getField(mc);
00121 if ( fld && !fld->building && !fld->vehicle )
00122 hemmingFactor += AttackFormula::getHemmingFactor ( nf );
00123 }
00124
00125 mv->result = getAttackValue ( uau, mv->attacker, mv->enemy, hemmingFactor );
00126
00127 if ( mv->result > 0 )
00128 tl.push_back ( mv );
00129
00130 }
00131 }
00132
00133
00134
00135 veh->removeview();
00136 npop ( veh->height );
00137 npop ( veh->ypos );
00138 npop ( veh->xpos );
00139
00140 if ( unitRadarActivated )
00141 veh->addview();
00142
00143 if ( fieldsWithChangedVisibility || 1 )
00144 evaluateviewcalculation ( getMap(), veh->getPosition(), veh->typ->view, 0xff );
00145 }
00146
00147 bool operator > ( const AI::MoveVariant& mv1, const AI::MoveVariant& mv2 )
00148 {
00149 return ( mv1.result > mv2.result
00150 || (mv1.result == mv2.result && mv1.positionThreat < mv2.positionThreat )
00151 || (mv1.result == mv2.result && mv1.positionThreat == mv2.positionThreat && mv1.moveDist < mv2.moveDist) );
00152 }
00153
00154 bool operator < ( const AI::MoveVariant& mv1, const AI::MoveVariant& mv2 )
00155 {
00156 return ( mv1.result < mv2.result
00157 || (mv1.result == mv2.result && mv1.positionThreat > mv2.positionThreat )
00158 || (mv1.result == mv2.result && mv1.positionThreat == mv2.positionThreat && mv1.moveDist > mv2.moveDist) );
00159 }
00160
00161
00162 bool AI::moveVariantComp ( const AI::MoveVariant* mv1, const AI::MoveVariant* mv2 )
00163 {
00164 return *mv1 < *mv2;
00165
00166 }
00167
00168 void AI::getAttacks ( AStar3D& vm, Vehicle* veh, TargetVector& tv, int hemmingBonus, bool justOne, bool executeService )
00169 {
00170
00172
00173 int x1 = veh->xpos;
00174 int y1 = veh->ypos;
00175 int x2 = veh->xpos;
00176 int y2 = veh->ypos;
00177
00178 for ( AStar3D::Container::iterator ff = vm.visited.begin(); ff != vm.visited.end(); ++ff ) {
00179 x1 = min ( x1, ff->h.x );
00180 y1 = min ( y1, ff->h.y );
00181 x2 = max ( x2, ff->h.x );
00182 y2 = max ( y2, ff->h.y );
00183 }
00184
00185 int maxrange = 0;
00186 for ( int i = 0; i < veh->typ->weapons.count; ++i )
00187 maxrange = max ( maxrange, veh->typ->weapons.weapon[i].maxdistance );
00188
00189 maxrange += 30;
00190
00191 x1 = max ( 0, x1 - maxrange/10 );
00192 y1 = max ( 0, y1 - maxrange*2/10 );
00193 x2 = min ( getMap()->xsize, x2 + maxrange/10 );
00194 y2 = min ( getMap()->ysize, y2 + maxrange*2/10 );
00195
00196 int enemycount = 0;
00197 for ( int y = y1; y <= y2 && !enemycount; ++y)
00198 for ( int x = x1; x <= x2; ++x) {
00199 tfield* fld = getMap()->getField(x,y);
00200 if ( fld && fld->vehicle)
00201 if ( getPlayer(fld->vehicle->getOwner()).diplomacy.isHostile( getPlayerNum() ) )
00202 enemycount++;
00203 }
00204
00205 if( !enemycount )
00206 return;
00207
00208
00209
00210 int orgxpos = veh->xpos ;
00211 int orgypos = veh->ypos ;
00212
00213 if ( getfield ( veh->xpos, veh->ypos )->unitHere ( veh ) )
00214 searchTargets ( veh, veh->getPosition3D(), tv, 0, vm, hemmingBonus );
00215
00216 if ( tv.size() && justOne )
00217 return;
00218
00219
00220 if ( !veh->typ->wait ) {
00221
00222 RefuelConstraint* apl = NULL;
00223 if ( RefuelConstraint::necessary ( veh, *this ))
00224 apl = new RefuelConstraint( *this, veh );
00225
00226 int fuelLacking = 0;
00227 for ( AStar3D::Container::iterator ff = vm.visited.begin(); ff != vm.visited.end(); ++ff )
00228 if ( !ff->hasAttacked ) {
00229 tfield* fld = getMap()->getField (ff->h);
00230 if ( !fld->vehicle && !fld->building ) {
00231 if ( !apl || apl->returnFromPositionPossible ( ff->h )) {
00232 searchTargets ( veh, ff->h, tv, beeline ( ff->h.x, ff->h.y, orgxpos, orgypos ), vm, hemmingBonus );
00233 if ( tv.size() && justOne )
00234 return;
00235 } else
00236 fuelLacking++;
00237 }
00238 }
00239
00240 if ( apl ) {
00241 delete apl;
00242 apl = NULL;
00243 }
00244
00245 if ( !tv.size() && fuelLacking && executeService)
00246 issueRefuelOrder( veh, true );
00247 }
00248 }
00249
00250 AI::AiResult AI::executeMoveAttack ( Vehicle* veh, TargetVector& tv )
00251 {
00252 int unitNetworkID = veh->networkid;
00253 AiResult result;
00254
00255 MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00256
00257 if ( mv->movePos != veh->getPosition3D() ) {
00258 BaseVehicleMovement vm2 ( mapDisplay );
00259
00260 VisibilityStates org_vision = _vision ;
00261 _vision = visible_now;
00262
00263 vm2.execute ( veh, mv->movePos.x, mv->movePos.y, 0, mv->movePos.getNumericalHeight(), 1 );
00264 if ( vm2.getStatus() != 3 )
00265 displaymessage ( "AI :: executeMoveAttack \n error in movement step 0 with unit %d", 1, veh->networkid );
00266
00267 vm2.execute ( NULL, mv->movePos.x, mv->movePos.y, 3, mv->movePos.getNumericalHeight(), 1 );
00268 if ( vm2.getStatus() != 1000 )
00269 displaymessage ( "AI :: executeMoveAttack \n error in movement step 2 with unit %d", 1, veh->networkid );
00270
00271 result.unitsMoved ++;
00272 _vision = org_vision;
00273 }
00274
00275
00276 if ( !getMap()->getUnit(unitNetworkID)) {
00277 result.unitsDestroyed++;
00278 return result;
00279 }
00280
00281 if ( veh->attacked )
00282 return result;
00283
00284 VehicleAttack va ( mapDisplay, NULL );
00285 va.execute ( veh, -1, -1, 0 , 0, -1 );
00286 if ( va.getStatus() != 2 )
00287 displaymessage ( "AI :: executeMoveAttack \n error in attack step 2 with unit %d", 1, veh->networkid );
00288
00289 VehicleTypeEfficiencyCalculator vtec( *this, veh, mv->enemy );
00290
00291 va.execute ( NULL, mv->attackx, mv->attacky, 2 , -1, mv->weapNum );
00292 if ( va.getStatus() != 1000 )
00293 displaymessage ( "AI :: executeMoveAttack \n error in attack step 3 with unit %d", 1, veh->networkid );
00294
00295 vtec.calc();
00296
00297 result.unitsMoved ++;
00298
00299
00300 if ( !getMap()->getUnit(unitNetworkID)) {
00301 result.unitsDestroyed++;
00302 return result;
00303 }
00304
00305
00306 VehicleMovement vm3 ( mapDisplay, NULL );
00307 if ( vm3.available ( veh ))
00308 result += moveToSavePlace ( veh, vm3 );
00309
00310 return result;
00311 }
00312
00313 bool AI::targetsNear( Vehicle* veh )
00314 {
00315 AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00316 ast.findAllAccessibleFields ();
00317 TargetVector tv;
00318 getAttacks ( ast, veh, tv, 0, true, false );
00319 if ( tv.size() )
00320 return true;
00321 else
00322 return false;
00323 }
00324
00325
00326 int AI::getDirForBestTacticsMove ( const Vehicle* veh, TargetVector& tv )
00327 {
00328 if ( tv.size() <= 0 )
00329 return -1;
00330
00331 MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00332 return getdirection ( veh->xpos, veh->ypos, mv->movePos.x, mv->movePos.y );
00333 }
00334
00335 MapCoordinate AI::getDestination ( Vehicle* veh )
00336 {
00337 AiParameter::Task task = veh->aiparam[ getPlayerNum() ]->getTask();
00338 if ( task == AiParameter::tsk_nothing || task == AiParameter::tsk_tactics ) {
00339 TargetVector tv;
00340 AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00341 ast.findAllAccessibleFields ();
00342 getAttacks ( ast, veh, tv, 0 );
00343
00344 if ( tv.size() > 0 ) {
00345
00346 MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00347 return MapCoordinate ( mv->movePos.x, mv->movePos.y );
00348 } else
00349 task = AiParameter::tsk_strategy;
00350 }
00351
00352 if ( task == AiParameter::tsk_strategy ) {
00353 Section* sec = sections.getBest ( 0, veh );
00354 if ( sec )
00355 return MapCoordinate ( sec->centerx, sec->centery );
00356 }
00357
00358 return MapCoordinate ( veh->xpos, veh->ypos );
00359 }
00360
00361
00362 AI::AiResult AI::moveToSavePlace ( Vehicle* veh, VehicleMovement& vm3, int preferredHeight )
00363 {
00364 int unitNetworkID = veh->networkid;
00365
00366 AiResult result;
00367
00368 if ( vm3.getStatus() == 0 )
00369 vm3.execute ( veh, -1, -1, 0, -1, -1 );
00370
00371
00372 if ( vm3.getStatus() == -107 )
00373 return result;
00374
00375 if ( vm3.getStatus() != 2 )
00376 displaymessage ( "AI :: moveToSavePlace \n error in movement3 step 0 with unit %d", 1, veh->networkid );
00377
00378 int xtogo = veh->xpos;
00379 int ytogo = veh->ypos;
00380 int threat = maxint;
00381 int dist = maxint;
00382
00383 if ( getfield ( veh->xpos, veh->ypos)->unitHere ( veh ) ) {
00384 threat = int( getFieldThreat ( veh->xpos, veh->ypos).threat[ veh->aiparam[ getPlayerNum()]->valueType] * 1.5 + 1);
00385
00386 }
00387
00388 for ( int f = 0; f < vm3.reachableFields.getFieldNum(); f++ )
00389 if ( !vm3.reachableFields.getField( f )->vehicle && !vm3.reachableFields.getField( f )->building ) {
00390 int x,y;
00391 vm3.reachableFields.getFieldCoordinates ( f, &x, &y );
00392 AiThreat& ait = getFieldThreat ( x, y );
00393 int _dist = beeline ( x, y, veh->xpos, veh->ypos);
00394
00395
00396 int t = int( ait.threat[ veh->aiparam[ getPlayerNum()]->valueType ] * log ( double(_dist) )/log(double(10)) );
00397
00398 if ( t < threat || ( t == threat && _dist < dist )) {
00399 threat = t;
00400 xtogo = x;
00401 ytogo = y;
00402 dist = _dist;
00403 }
00404 }
00405
00406 if ( veh->xpos != xtogo || veh->ypos != ytogo ) {
00407 vm3.execute ( NULL, xtogo, ytogo, 2, -1, -1 );
00408 if ( vm3.getStatus() != 3 )
00409 displaymessage ( "AI :: moveToSavePlace \n error in movement3 step 2 with unit %d", 1, veh->networkid );
00410
00411 vm3.execute ( NULL, xtogo, ytogo, 3, -1, 1 );
00412 if ( vm3.getStatus() != 1000 )
00413 displaymessage ( "AI :: moveToSavePlace \n error in movement3 step 3 with unit %d", 1, veh->networkid );
00414
00415 result.unitsMoved++;
00416 }
00417
00418 if ( !getMap()->getUnit( unitNetworkID ))
00419 result.unitsDestroyed++;
00420
00421 return result;
00422 }
00423
00424
00425 int AI::changeVehicleHeight ( Vehicle* veh, VehicleMovement* vm, int preferredDirection )
00426 {
00427 #if 0 // ####
00428 int bh = getBestHeight ( veh );
00429 if ( bh != veh->height && bh != -1 ) {
00430 ChangeVehicleHeight* cvh;
00431 int newheight;
00432 if ( bh > veh->height ) {
00433 cvh = new IncreaseVehicleHeight ( mapDisplay, NULL );
00434 newheight = veh->height << 1;
00435 } else {
00436 cvh = new DecreaseVehicleHeight ( mapDisplay, NULL );
00437 newheight = veh->height >> 1;
00438 }
00439 auto_ptr<ChangeVehicleHeight> acvh ( cvh );
00440
00441 if ( newheight & veh->typ->height ) {
00442 if ( cvh->available ( veh ) ) {
00443
00444 int stat = cvh->execute ( veh, -1, -1, 0, newheight, 1 );
00445 if ( stat == 2 ) {
00446
00447 int bestx = -1;
00448 int besty = -1;
00449 int moveremain = minint;
00450
00451 if ( preferredDirection == -1 ) {
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461 preferredDirection = getdirection ( veh->xpos, veh->ypos, getMap()->xsize/2, getMap()->ysize/2 );
00462 }
00463
00464 for ( int i = 0; i < cvh->reachableFields.getFieldNum(); i++ ) {
00465 int newMoveRemain = cvh->reachableFields.getData ( i ).dist;
00466
00467
00468 if ( preferredDirection != -1 ) {
00469 int xp, yp;
00470 cvh->reachableFields.getFieldCoordinates ( i, &xp, &yp );
00471
00472 int dir = preferredDirection - getdirection ( veh->xpos, veh->ypos, xp, yp );
00473 if ( dir > sidenum/2 )
00474 dir -= sidenum;
00475 if ( dir < -sidenum/2 )
00476 dir += sidenum;
00477
00478 dir = abs ( dir );
00479
00480
00481
00482 newMoveRemain -= (dir*2-3)* veh->typ->steigung*maxmalq / 3;
00483
00484
00485 }
00486
00487 if ( newMoveRemain > moveremain ) {
00488 cvh->reachableFields.getFieldCoordinates ( i, &bestx, &besty );
00489 moveremain = newMoveRemain;
00490 }
00491 }
00492
00493 if ( bestx != -1 && besty != -1 ) {
00494 cvh->execute ( NULL, bestx, besty, 2, -1, -1 );
00495 if ( cvh->getStatus() == 1000 )
00496 return 1;
00497
00498 if ( cvh->getStatus() != 3 )
00499 displaymessage ( "AI :: changeVehicleHeight \n error in changeHeight step 2 with unit %d", 1, veh->networkid );
00500
00501 cvh->execute ( NULL, bestx, besty, 3, -1, -1 );
00502 if ( cvh->getStatus() != 1000 )
00503 displaymessage ( "AI :: changeVehicleHeight \n error in changeHeight step 3 with unit %d", 1, veh->networkid );
00504
00505 return 1;
00506 } else
00507 return -1;
00508 } else {
00509 if ( stat == 1000 )
00510 return 1;
00511
00512 if ( veh->typ->steigung == 0 )
00513 return -2;
00514
00515 return -1;
00516 }
00517 } else {
00518 return -2;
00519 }
00520 }
00521
00522 }
00523 #endif
00524 return 0;
00525 }
00526
00527
00528 AI::AiResult AI::tactics( void )
00529 {
00530 const int tsk_num = 3;
00531 AiParameter::Task tasks[tsk_num] = { AiParameter::tsk_nothing,
00532 AiParameter::tsk_tactics,
00533 AiParameter::tsk_tactwait
00534 };
00535 AiResult result;
00536
00537 displaymessage2("starting tactics ... ");
00538
00539 typedef list<int> TactVehicles;
00540 TactVehicles tactVehicles;
00541
00542 for ( Player::VehicleList::iterator vi = getPlayer().vehicleList.begin(); vi != getPlayer().vehicleList.end(); vi++ ) {
00543 Vehicle* veh = *vi;
00544
00545 bool unitUsable = false;
00546 if ( veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_fight
00547 || veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_undefined
00548 || (veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_guard && veh->aiparam[ getPlayerNum() ]->dest_nwid == -1 && veh->aiparam[ getPlayerNum() ]->dest.x == -1 ))
00549 for ( int j = 0; j < tsk_num; j++ )
00550 if ( veh->aiparam[ getPlayerNum() ]->getTask() == tasks[j] )
00551 unitUsable = true;
00552
00553 if ( getMap()->getField(veh->getPosition())->vehicle != veh ) {
00554 Vehicle* transport = getMap()->getField(veh->getPosition())->vehicle;
00555 if ( transport ) {
00556 const ContainerBaseType::TransportationIO* unloadSystem = transport->vehicleUnloadSystem( veh->typ, -1 );
00557 if ( unloadSystem && unloadSystem->disableAttack )
00558 continue;
00559 }
00560 }
00561
00562
00563 int maxWeapDist = minint;
00564 for ( int w = 0; w < veh->typ->weapons.count; w++ )
00565 if ( veh->typ->weapons.weapon[w].shootable() )
00566 maxWeapDist = max ( veh->typ->weapons.weapon[w].maxdistance , maxWeapDist );
00567
00568 int maxMove = minint;
00569 for ( int h = 0; h < 8; h++ )
00570 if ( veh->typ->height & ( 1 << h ))
00571 maxMove = max ( veh->typ->movement[h], maxMove );
00572
00573 if ( maxWeapDist > 0 ) {
00574 bool enemiesNear = false;
00575 int ydist = (maxMove + maxWeapDist) / maxmalq * 2;
00576 int xdist = ydist / 4;
00577 for ( int x = veh->xpos - xdist; x <= veh->xpos + xdist; x++ )
00578 for ( int y = veh->ypos - ydist; y <= veh->ypos + ydist; y++ ) {
00579 tfield* fld = getMap()->getField(x,y );
00580 if ( fld ) {
00581 if ( fld->vehicle && getPlayer(veh->getOwner()).diplomacy.isHostile( fld->vehicle->getOwner() ) )
00582 enemiesNear = true;
00583 if ( fld->building && getPlayer(veh->getOwner()).diplomacy.isHostile( fld->building->getOwner() ) )
00584 enemiesNear = true;
00585 }
00586 }
00587
00588 if ( unitUsable && enemiesNear)
00589 tactVehicles.push_back ( veh->networkid );
00590 }
00591 }
00592
00593 int hemmingBonus = 5;
00594
00595 size_t lastTactVehiclesSize;
00596
00597 while ( !tactVehicles.empty() ) {
00598 lastTactVehiclesSize = tactVehicles.size();
00599
00600 typedef map<int, MoveVariantContainer> Targets;
00601 Targets targets;
00602
00603 int directAttackNum;
00604 do {
00605 directAttackNum = 0;
00606 for ( TactVehicles::iterator i = tactVehicles.begin(); i != tactVehicles.end(); ) {
00607 Vehicle* veh = getMap()->getUnit( *i );
00608 if ( veh ) {
00609
00610 unitCounter++;
00611 displaymessage2("tact: processing operation %d", unitCounter );
00612 checkKeys();
00613
00614 int stat = changeVehicleHeight ( veh, NULL );
00615
00616 if ( !getMap()->getUnit(*i) )
00617 continue;
00618
00619 if ( stat == -1 ) {
00620 veh->aiparam[ getPlayerNum() ]->setTask( AiParameter::tsk_wait );
00621 result.unitsWaiting++;
00622 i++;
00623 } else {
00624
00625 AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00626 ast.findAllAccessibleFields ();
00627 TargetVector tv;
00628 getAttacks ( ast, veh, tv, hemmingBonus );
00629
00630 if ( !getMap()->getUnit(*i) )
00631 continue;
00632
00633 if ( tv.size() ) {
00634 MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00635
00636
00637
00638 bool directAttack = false;
00639 if ( beeline ( mv->movePos.x, mv->movePos.y, mv->attackx, mv->attacky ) > maxmalq || veh->height >= chtieffliegend || (mv->enemy && mv->enemy->height >= chtieffliegend) )
00640 directAttack = true;
00641
00642 int freeNeighbouringFields = 0;
00643 for ( int j = 0; j < sidenum; j++ ) {
00644 tfield* fld = getMap()->getField ( getNeighbouringFieldCoordinate ( MapCoordinate(mv->attackx, mv->attacky), j));
00645 if ( fld )
00646 if ( !fld->building && !fld->vehicle )
00647 freeNeighbouringFields++;
00648 }
00649
00650 if ( freeNeighbouringFields <= 1 )
00651 directAttack = true;
00652
00653 if ( mv->enemyDamage >= 100 )
00654 directAttack = true;
00655
00656 if ( directAttack ) {
00657
00658
00659
00660
00661
00662 VisibilityStates org_vision = _vision ;
00663 _vision = visible_all;
00664
00665 AiResult res = executeMoveAttack ( veh, tv );
00666 i = tactVehicles.erase ( i );
00667
00668 if ( !res.unitsDestroyed )
00669 veh->aiparam[ getPlayerNum() ]->setTask( AiParameter::tsk_tactics );
00670
00671 result += res;
00672 directAttackNum++;
00673
00674 _vision = org_vision;
00675
00676 } else {
00677 targets[mv->enemy->networkid].push_back( *mv );
00678 i++;
00679 }
00680 } else {
00681
00682 if ( veh->aiparam[ getPlayerNum() ]->getTask() != AiParameter::tsk_serviceRetreat )
00683 veh->aiparam[ getPlayerNum() ]->resetTask();
00684 i = tactVehicles.erase ( i );
00685 }
00686 }
00687 } else {
00688
00689 i = tactVehicles.erase ( i );
00690
00691 }
00692 }
00693
00694
00695
00696
00697 if ( directAttackNum )
00698 targets.clear();
00699
00700 } while ( directAttackNum );
00701
00702 if ( !targets.empty() ) {
00703
00704 Targets::iterator currentTarget = targets.begin();
00705 for ( Targets::iterator i = targets.begin(); i != targets.end(); i++ )
00706 if ( i->second.size() > currentTarget->second.size())
00707 currentTarget = i;
00708
00709 if ( currentTarget->second.size()-1 < hemmingBonus )
00710 hemmingBonus = currentTarget->second.size()-1;
00711
00712 typedef list<MapCoordinate> AffectedFields;
00713 AffectedFields affectedFields;
00714
00715
00716
00717
00718
00719
00720 VisibilityStates org_vision = _vision ;
00721 _vision = visible_all;
00722
00723
00724
00725
00726
00727 do {
00728
00729 Vehicle* enemy = getMap()->getUnit( currentTarget->first);
00730
00731 if ( enemy ) {
00732 affectedFields.push_back ( MapCoordinate(enemy->xpos, enemy->ypos) );
00733
00734 MoveVariantContainer attacker = currentTarget->second;
00735
00736
00737 sort( attacker.begin(), attacker.end() );
00738
00739 MoveVariantContainer::iterator mvci = attacker.begin();
00740 Vehicle* positions[sidenum];
00741 Vehicle* finalPositions[sidenum];
00742 for ( int i = 0; i< sidenum; i++ ) {
00743 positions[i] = NULL;
00744 finalPositions[i] = NULL;
00745 }
00746 float finalValue = 0;
00747
00748 tactics_findBestAttackUnits ( attacker, mvci, positions, 0, finalPositions, finalValue, 0, 0, ticker );
00749
00750 if ( finalValue > 0 ) {
00751 for ( int i = 0; i < sidenum; i++ )
00752 if ( finalPositions[i] ) {
00753 int nwid = finalPositions[i]->networkid;
00754 _vision = org_vision;
00755 MapCoordinate affected = MapCoordinate(finalPositions[i]->xpos, finalPositions[i]->ypos);
00756 MapCoordinate3D dst = getNeighbouringFieldCoordinate( MapCoordinate3D( enemy->xpos, enemy->ypos, finalPositions[i]->height ), i);
00757 dst.setnum ( dst.x, dst.y, -2 );
00758 moveUnit ( finalPositions[i], dst );
00759 _vision = visible_all;
00760
00761 affectedFields.push_back ( affected );
00762
00763
00764 if ( !getMap()->getUnit ( nwid ) ) {
00765 TactVehicles::iterator att = find ( tactVehicles.begin(), tactVehicles.end(), finalPositions[i]->networkid ) ;
00766 tactVehicles.erase ( att );
00767 finalPositions[i] = NULL;
00768 }
00769 }
00770
00771
00772 int unitcount = 0;
00773 for ( int i = 0; i < sidenum; i++ )
00774 if ( finalPositions[i] )
00775 unitcount++;
00776
00777 if ( unitcount ) {
00778 int attackOrder[sidenum];
00779 int finalOrder[sidenum];
00780 for ( int i = 0; i< sidenum; i++ )
00781 attackOrder[i] = finalOrder[i] = -1;
00782
00783
00784 int finalDamage = -1;
00785 int finalAttackNum = maxint;
00786 tactics_findBestAttackOrder ( finalPositions, attackOrder, enemy, 0, enemy->damage, finalDamage, finalOrder, finalAttackNum );
00787
00788
00789 tfield* enemyField = getMap()->getField(enemy->xpos, enemy->ypos);
00790 for ( int i = 0; i < finalAttackNum && enemyField->vehicle == enemy && finalAttackNum < maxint; i++ ) {
00791 checkKeys();
00792 if ( finalOrder[i] < 0 )
00793 warning("!!!");
00794
00795 if ( i < finalAttackNum && finalPositions[finalOrder[i]] ) {
00796 Vehicle* a = finalPositions[finalOrder[i]];
00797 VehicleAttack va ( mapDisplay, NULL );
00798 if ( finalOrder[i] < 0 )
00799 warning("!!!");
00800
00801 va.execute ( finalPositions[finalOrder[i]], -1, -1, 0, 0, -1 );
00802 if ( va.getStatus() != 2 && strictChecks )
00803 displaymessage("inconsistency #1 in AI::tactics attack", 1 );
00804
00805 VehicleTypeEfficiencyCalculator vtec (*this, finalPositions[finalOrder[i]], enemy );
00806 va.execute ( NULL, enemy->xpos, enemy->ypos, 2, 0, -1 );
00807 if ( va.getStatus() != 1000 && strictChecks )
00808 displaymessage("inconsistency #1 in AI::tactics attack", 1 );
00809
00810 vtec.calc();
00811
00812 TactVehicles::iterator att = find ( tactVehicles.begin(), tactVehicles.end(), a->networkid ) ;
00813 tactVehicles.erase ( att );
00814 }
00815 }
00816 }
00817 }
00818
00819 }
00820
00821 bool processable = true;
00822 do {
00823 currentTarget++;
00824 if ( currentTarget != targets.end() )
00825 if ( hemmingBonus == currentTarget->second.size()-1 ) {
00826 for ( AffectedFields::iterator i = affectedFields.begin(); i != affectedFields.end(); i++ )
00827 for ( MoveVariantContainer::iterator j = currentTarget->second.begin(); j != currentTarget->second.end(); j++ ) {
00828 Vehicle* veh = j->attacker;
00829
00830
00831 if ( beeline ( *i, veh->getPosition()) < veh->maxMovement()+20 )
00832 processable = false;
00833 }
00834 } else
00835 processable = false;
00836 } while ( !processable && currentTarget != targets.end() );
00837
00838 } while ( currentTarget != targets.end() );
00839
00840 _vision = org_vision;
00841
00842 } else {
00843
00844 tactVehicles.clear();
00845 }
00846 if ( lastTactVehiclesSize == tactVehicles.size() ) {
00847
00848 return result;
00849 }
00850
00851 }
00852
00853 displaymessage2("tactics completed ... ");
00854
00855 return result;
00856 }
00857
00858 void AI :: tactics_findBestAttackOrder ( Vehicle** units, int* attackOrder, Vehicle* enemy, int depth, int damage, int& finalDamage, int* finalOrder, int& finalAttackNum )
00859 {
00860 if ( damage > enemy->damage )
00861 if ( (depth < finalAttackNum && damage==finalDamage) || damage > finalDamage ) {
00862 finalDamage = damage;
00863 finalAttackNum = depth;
00864 for ( int i = 0; i < sidenum; i++ )
00865 finalOrder[i] = attackOrder[i];
00866 }
00867
00868 if ( damage >= 100 || depth >= 6 )
00869 return;
00870
00871 for ( int i = 0; i< sidenum; i++ ) {
00872 bool found = false;
00873 for ( int j = 0; j < depth; j++ )
00874 if ( attackOrder[j] == i)
00875 found = true;
00876 if ( units[i] && !found ) {
00877
00878 attackOrder[depth] = i;
00879
00880 npush ( units[i]->xpos );
00881 npush ( units[i]->ypos );
00882 MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( enemy->xpos, enemy->ypos), i );
00883 units[i]->xpos = mc.x;
00884 units[i]->ypos = mc.y;
00885
00886 tunitattacksunit battle ( units[i], enemy );
00887 int newdamage = battle.dv.damage;
00888 battle.calc();
00889 newdamage = battle.dv.damage - newdamage;
00890 npop ( units[i]->ypos );
00891 npop ( units[i]->xpos );
00892
00893 tactics_findBestAttackOrder ( units, attackOrder, enemy, depth+1, damage+newdamage, finalDamage, finalOrder, finalAttackNum );
00894
00895 attackOrder[depth] = -1;
00896 }
00897 }
00898
00899 }
00900
00901 float AI :: getAttackValue ( const tfight& battle, const Vehicle* attackingUnit, const Vehicle* attackedUnit, float factor )
00902 {
00903 float result = (battle.dv.damage - attackedUnit->damage) * attackedUnit->aiparam[getPlayerNum()]->getValue() * factor - 1/config.aggressiveness * (battle.av.damage - attackingUnit->damage) * attackedUnit->aiparam[getPlayerNum()]->getValue() ;
00904 if ( battle.dv.damage >= 100 )
00905 result += attackedUnit->aiparam[getPlayerNum()]->getValue() * attack_unitdestroyed_bonus;
00906 return result;
00907 }
00908
00909
00910
00911 class UnitAttacksUnit_FakeHemming : public tunitattacksunit {
00912 bool neighbours[sidenum];
00913 bool checkHemming ( Vehicle* d_eht, int direc )
00914 {
00915 return neighbours[direc];
00916 };
00917 public:
00918 UnitAttacksUnit_FakeHemming ( AI* ai, Vehicle* attacker, Vehicle* defender, Vehicle** _neighbours ) : tunitattacksunit ( attacker , defender )
00919 {
00920 for ( int i = 0; i < sidenum; i++ ) {
00921 tfield* fld = ai->getMap()->getField ( getNeighbouringFieldCoordinate ( attacker->getPosition(), i ));
00922
00923 Vehicle* v = NULL;
00924 if ( fld )
00925 v = fld->vehicle;
00926
00927
00928 if ( v && attackpossible2n ( v, defender ) )
00929 neighbours[i] = true;
00930 else
00931 neighbours[i] = false;
00932 }
00933
00934 for ( int i = 0; i < sidenum; i++ ) {
00935 if ( _neighbours[i] == attacker )
00936 break;
00937
00938 if ( _neighbours[i] )
00939 neighbours[i] = true;
00940 }
00941 setup ( attacker, defender, true, -1 );
00942 };
00943 };
00944
00945
00946
00947 void AI :: tactics_findBestAttackUnits ( const MoveVariantContainer& mvc, MoveVariantContainer::iterator& m, Vehicle** positions, float value, Vehicle** finalPosition, float& finalValue, int unitsPositioned, int recursionDepth, int startTime )
00948 {
00949 if ( m == mvc.end() || unitsPositioned >= 6 || recursionDepth >= 8 || (startTime + config.maxTactTime < ticker && !benchMark)) {
00950 float value = 0;
00951 Vehicle* target = mvc.begin()->enemy;
00952 npush ( target->damage );
00953 for ( int i = 0; i < sidenum && target->damage!=100; i++ )
00954 if ( positions[i] ) {
00955 npush ( positions[i]->ypos );
00956 npush ( positions[i]->xpos );
00957
00958 MapCoordinate mc = getNeighbouringFieldCoordinate ( target->getPosition(), i );
00959 positions[i]->xpos = mc.x;
00960 positions[i]->ypos = mc.y;
00961
00962 UnitAttacksUnit_FakeHemming uau ( this, positions[i], target, positions );
00963 uau.calc();
00964 value += getAttackValue ( uau, positions[i], target );
00965
00966 npop ( positions[i]->xpos );
00967 npop ( positions[i]->ypos );
00968 }
00969 npop ( target->damage );
00970 if ( value > finalValue ) {
00971 for ( int i = 0; i < sidenum; i++ )
00972 finalPosition[i] = positions[i] ;
00973
00974 finalValue = value;
00975 }
00976 } else {
00977 for ( int i = 0; i< sidenum; i++ ) {
00978 if ( m->neighbouringFieldsReachable[i] && !positions[i] ) {
00979 positions[i] = m->attacker;
00980 if ( m->result > 0 )
00981 value += m->result;
00982 else
00983 value += 1;
00984 m++;
00985 tactics_findBestAttackUnits ( mvc, m, positions, value, finalPosition, finalValue, unitsPositioned+1, recursionDepth+1, startTime );
00986 m--;
00987 value -= m->result;
00988 positions[i] = NULL;
00989 } else {
00990 m++;
00991 tactics_findBestAttackUnits ( mvc, m, positions, value, finalPosition, finalValue, unitsPositioned, recursionDepth+1, startTime );
00992 m--;
00993 }
00994 }
00995 }
00996 }
00997
00998
00999
01000 bool AI :: vehicleValueComp ( const Vehicle* v1, const Vehicle* v2 )
01001 {
01002 return v1->aiparam[ v1->color/8 ]->getValue() < v2->aiparam[ v1->color/8 ]->getValue();
01003 }
01004
01005 bool AI :: buildingValueComp ( const Building* v1, const Building* v2 )
01006 {
01007 return v1->aiparam[ v1->color/8 ]->getValue() < v2->aiparam[ v1->color/8 ]->getValue();
01008 }