/*************************************************************************** buildingcapture.cpp - description ------------------- begin : Fri Mar 30 2001 copyright : (C) 2001 by Martin Bickel email : bickel@asc-hq.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "ai_common.h" float AI :: getCaptureValue ( const pbuilding bld, const pvehicle veh ) { HiddenAStar ast ( this, veh ); HiddenAStar::Path path; ast.findPath ( path, bld->getEntry().x, bld->getEntry().y ); if ( ast.getTravelTime() >= 0 ) // everything else being equal, prefer cheapest unit // TODO: factor 0.0001 should be made configurable return getCaptureValue ( bld, ast.getTravelTime() )-0.0001*veh->aiparam[getPlayerNum()]->getValue() ; else return -1; // return minfloat; // this makes no sense as minfloat>0! } void AI :: BuildingCapture :: write ( tnstream& stream ) const { stream.writeInt( 20 ); stream.writeInt ( state ); stream.writeInt ( unit ); for ( vector::const_iterator i = guards.begin(); i != guards.end(); i++ ) { stream.writeInt ( 1 ); stream.writeInt ( *i ); } stream.writeInt ( 0 ); stream.writeFloat ( captureValue ); stream.writeInt ( nearestUnit ); } void AI :: BuildingCapture :: read ( tnstream& stream ) { stream.readInt(); // version state = BuildingCaptureState( stream.readInt ( )); unit = stream.readInt ( ); int i = stream.readInt (); while ( i ) { guards.push_back ( stream.readInt() ); i = stream.readInt(); } captureValue = stream.readFloat (); nearestUnit = stream.readInt (); } class SearchReconquerBuilding : public SearchFields { protected: AI& ai; pbuilding buildingToCapture; int mode; // (1): nur fusstruppen; (2): 1 und transporter; (3): 2 und geb„ude vector enemyUnits; // that can conquer the building float getThreatValueOfUnit ( pvehicle veh ); public: void testfield ( const MapCoordinate& mc ); bool returnresult ( ); void unitfound ( pvehicle eht ); bool canUnitCapture ( pvehicle veh ); SearchReconquerBuilding ( AI& _ai, pbuilding bld ) : SearchFields ( _ai.getMap() ), ai ( _ai ), buildingToCapture ( bld ), mode(3) {}; }; bool SearchReconquerBuilding :: returnresult( ) { return !enemyUnits.empty(); } void SearchReconquerBuilding :: unitfound(pvehicle eht) { enemyUnits.push_back ( eht ); buildingToCapture->aiparam[ai.getPlayerNum()]->setAdditionalValue ( buildingToCapture->aiparam[ai.getPlayerNum()]->getValue() ); } bool SearchReconquerBuilding :: canUnitCapture( pvehicle eht ) { return (eht->typ->functions & cf_conquer ) && fieldAccessible ( buildingToCapture->getEntryField(), eht) == 2 ; } void SearchReconquerBuilding :: testfield(const MapCoordinate& mc) { pvehicle eht = gamemap->getField(mc)->vehicle; // pbuilding bld = getfield(xp,yp)->building; if ( eht ) if (getdiplomaticstatus(eht->color) != capeace) if ( canUnitCapture ( eht )) { VehicleMovement vm ( NULL ); if ( vm.available ( eht )) { vm.execute ( eht, -1, -1, 0 , -1, -1 ); if ( vm.reachableFields.isMember ( startPos )) unitfound(eht); } } else if (mode >= 2) if (eht->typ->maxLoadableUnits > 0) for ( int w = 0; w <= 31; w++) if ( eht->loading[w] ) if ( canUnitCapture ( eht->loading[w] )) if (eht->maxMovement() + eht->loading[w]->maxMovement() >= beeline(mc, startPos)) unitfound(eht); /* if ( bld ) if ( mode >= 3 ) if (getdiplomaticstatus(bld->color) != capeace) for ( int w = 0; w <= 31; w++) if ( bld->loading[w] ) if ( canUnitCapture ( bld->loading[w] )) if ( bld->loading[w]->typ->movement[log2(bld->loading[w]->height)] <= beeline(xp,yp,startx,starty)) unitfound ( bld->loading[w] ); */ } float AI :: getCaptureValue ( const pbuilding bld, int traveltime ) { if ( traveltime < 0 ) traveltime = 0; return float(bld->aiparam[getPlayerNum()]->getValue()) / float(traveltime+1); } bool AI :: checkReConquer ( pbuilding bld, pvehicle veh ) { SearchReconquerBuilding srb ( *this, bld ); srb.initsearch ( bld->getEntry(), (maxTransportMove + maxUnitMove/2) / maxmalq + 1, 1 ); srb.startsearch(); bool enemyNear = srb.returnresult(); if ( enemyNear && veh ) { float f = 0; for ( int i = 0; i < 32; i++ ) if ( bld->loading[i] ) if ( bld->loading[i]->getMovement() ) f += bld->loading[i]->aiparam[ getPlayerNum()]->getValue(); //! if the units inside the building are more worth than the own unit, capture the building regardless of whether it can be recaptured if ( f > veh->aiparam[getPlayerNum()]->getValue()) return false; else return true; } else return false; } struct CaptureTriple { pbuilding bld; pvehicle veh; float val; }; typedef struct CaptureTriple* pCaptureTriple; typedef vector CaptureList; class CaptureTripleComp : public binary_function { public: explicit CaptureTripleComp() {}; bool operator() (const pCaptureTriple t1, const pCaptureTriple t2) const { return t1->val > t2->val; } }; void AI :: checkConquer( ) { // remove all capture orders for buildings which are no longer controlled by the enemy for ( BuildingCaptureContainer::iterator bi = buildingCapture.begin(); bi != buildingCapture.end(); ) { BuildingCaptureContainer::iterator nxt = bi; ++nxt; pvehicle veh= getMap()->getUnit ( bi->second.unit ); pbuilding bld = getMap()->getField( bi->first )->building; if ( !bld ) { buildingCapture.erase ( bi ); bi = nxt; continue; } if ( getdiplomaticstatus2( bld->color, getPlayerNum()*8 ) != cawar || !( veh && fieldAccessible ( getMap()->getField( bi->first ), veh ) == 2 )) { if ( veh ) { veh->aiparam[getPlayerNum()]->resetTask (); veh->aiparam[getPlayerNum()]->setNextJob(); } buildingCapture.erase ( bi ); } else if ( veh && veh->color != getPlayerNum()*8 ) buildingCapture.erase ( bi ); bi = nxt; } displaymessage2("check for capturing buildings ... "); // check for stagnation int num_reachable_buildings=0; for ( BuildingCaptureContainer::iterator bi = buildingCapture.begin(); bi != buildingCapture.end(); bi++ ) if ( bi->second.state!=BuildingCapture::conq_unreachable ) num_reachable_buildings++; // if all blds are marked as unreachable, reevaluate in hope for a map-change if ( buildingCapture.size()>0 && num_reachable_buildings==0 ) { for ( BuildingCaptureContainer::iterator bi = buildingCapture.begin(); bi != buildingCapture.end(); bi++ ) bi->second.state=BuildingCapture::conq_noUnit; } CaptureList captureList; int buildingCounter = 0; for ( int c = 0; c <= 8; c++ ) { if ( c<8 ) { if ( !getPlayer(c).exist() ) continue; if ( getdiplomaticstatus2 ( getPlayerNum()*8, c*8 ) != cawar ) continue; } for ( Player::BuildingList::iterator bi = getPlayer(c).buildingList.begin(); bi != getPlayer(c).buildingList.end(); bi++ ) { pbuilding bld = *bi; int reachable = 0; if ( buildingCapture[ bld->getEntry() ].state != BuildingCapture::conq_noUnit ) continue; bool enemyNear = checkReConquer ( bld, 0 ); ++buildingCounter; displaymessage2("check for capturing building %d ", buildingCounter); for ( Player::VehicleList::iterator vi = getPlayer().vehicleList.begin(); vi != getPlayer().vehicleList.end(); vi++ ) { pvehicle veh = *vi; if ( !veh->canMove() ) continue; if ( fieldAccessible ( bld->getEntryField(), veh ) != 2 ) continue; if ( c!=8 && !(veh->typ->functions & cf_conquer) ) continue; if ( veh->aiparam[getPlayerNum()]->hasJob( AiParameter::job_conquer) && veh->aiparam[getPlayerNum()]->getTask() != AiParameter::tsk_nothing ) continue; // here, units can be excluded from capturing if ( c!=8 ) { if( !veh->aiparam[getPlayerNum()]->hasJob(AiParameter::job_conquer) ) continue; if( veh->aiparam[getPlayerNum()]->getTask() != AiParameter::tsk_nothing ) continue; } // any further factors should be incorporated into getCaptureValue float val=getCaptureValue( bld, veh ); // malus if enemy is near (relevant if we are short of capture-units // or building is practically worthless) // TODO: should be made an optional parameter to getCaptureValue if ( val>0 && enemyNear ) val -= 0.1*veh->aiparam[getPlayerNum()]->getValue(); if ( val > 0 ) { pCaptureTriple triple = new CaptureTriple; triple->bld=bld; triple->veh=veh; triple->val=val; captureList.push_back( triple ); reachable = true; } } if ( reachable==0 ) buildingCapture[ bld->getEntry() ].state = BuildingCapture::conq_unreachable; } } sort ( captureList.begin(), captureList.end(), CaptureTripleComp() ); for ( CaptureList::iterator i = captureList.begin(); i != captureList.end(); i++ ) { pbuilding bld = (*i)->bld; pvehicle veh = (*i)->veh; // float val = (*i)->val; delete (*i); // check whether bld and veh are still available if ( buildingCapture[ bld->getEntry() ].state != BuildingCapture::conq_noUnit ) continue; if ( veh->aiparam[getPlayerNum()]->getJob() == AiParameter::job_conquer && veh->aiparam[getPlayerNum()]->getTask() != AiParameter::tsk_nothing ) continue; // dispatch capture order BuildingCapture& bc = buildingCapture[ bld->getEntry() ]; if ( veh->typ->functions & cf_conquer ) bc.state = BuildingCapture::conq_conqUnit; else bc.state = BuildingCapture::conq_unitNotConq; bc.unit = veh->networkid; veh->aiparam[getPlayerNum()]->setJob ( AiParameter::job_conquer ); veh->aiparam[getPlayerNum()]->setTask ( AiParameter::tsk_move ); veh->aiparam[getPlayerNum()]->dest.setnum( bld->getEntry().x, bld->getEntry().y, -1 ); } // execute capture orders for ( BuildingCaptureContainer::iterator bi = buildingCapture.begin(); bi != buildingCapture.end(); ) { BuildingCaptureContainer::iterator nxt = bi; ++nxt; pvehicle veh = getMap()->getUnit ( bi->second.unit ); if ( veh ) { MapCoordinate3D dest = veh->aiparam[getPlayerNum()]->dest; int nwid = veh->networkid; moveUnit ( veh, dest, true ); if ( getMap()->getUnit ( nwid ) && veh->getPosition() == dest ) { veh->aiparam[getPlayerNum()]->resetTask (); buildingCapture.erase ( bi ); } } else buildingCapture.erase ( bi ); checkKeys(); bi = nxt; } // do something useful with units that are not used for capturing buildings for ( Player::VehicleList::iterator vi = getPlayer().vehicleList.begin(); vi != getPlayer().vehicleList.end(); vi++ ) if ( (*vi)->aiparam[getPlayerNum()]->getJob() == AiParameter::job_conquer && (*vi)->aiparam[getPlayerNum()]->getTask() == AiParameter::tsk_nothing ) (*vi)->aiparam[getPlayerNum()]->setNextJob(); }