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