moveunitcommand.cpp

Go to the documentation of this file.
00001 /*
00002      This file is part of Advanced Strategic Command; http://www.asc-hq.de
00003      Copyright (C) 1994-2010  Martin Bickel  and  Marc Schellenberger
00004  
00005      This program is free software; you can redistribute it and/or modify
00006      it under the terms of the GNU General Public License as published by
00007      the Free Software Foundation; either version 2 of the License, or
00008      (at your option) any later version.
00009  
00010      This program is distributed in the hope that it will be useful,
00011      but WITHOUT ANY WARRANTY; without even the implied warranty of
00012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013      GNU General Public License for more details.
00014  
00015      You should have received a copy of the GNU General Public License
00016      along with this program; see the file COPYING. If not, write to the 
00017      Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
00018      Boston, MA  02111-1307  USA
00019 */
00020 
00021 #include <iostream>
00022 
00023 #include "moveunitcommand.h"
00024 
00025 #include "../vehicle.h"
00026 #include "../mapfield.h"
00027 #include "../gamemap.h"
00028 #include "../viewcalculation.h"
00029 #include "../spfst.h"
00030 #include "../mapdisplayinterface.h"
00031 #include "vehicleattack.h"
00032 #include "action-registry.h"
00033 #include "moveunit.h"
00034 
00035 
00036 bool MoveUnitCommand :: avail ( Vehicle* eht )
00037 {
00038    
00039    if ( eht ) 
00040       return eht->canMove();
00041    
00042    return false;
00043 }
00044 
00045 
00046 bool MoveUnitCommand :: ascendAvail ( Vehicle* veh )
00047 {
00048    if ( veh )
00049       if ( veh->getHeightChange( +1 ))
00050          return true;
00051    return false;
00052 }
00053 
00054 bool MoveUnitCommand :: descendAvail ( Vehicle* veh )
00055 {
00056    if ( veh )
00057       if ( veh->getHeightChange( -1 ))
00058          return true;
00059    
00060    return false;
00061 }
00062 
00063 
00064 bool MoveUnitCommand :: longDistAvailable( const MapCoordinate& pos )
00065 {
00066    MapField* fld = getMap()->getField(pos);
00067    if ( !fld )
00068       return false;
00069    
00070    if ( !getUnit() )
00071       return false;
00072    
00073    if ( pos == getUnit()->getPosition() )
00074       return false;
00075 
00076    for ( int h = 0; h < 8; ++h )
00077       if ( getUnit()->typ->height & (1<<h))
00078          if ( fieldAccessible( fld, getUnit(), 1 << h ) == 2 )
00079             return true;
00080    
00081    return false;
00082 
00083 }
00084 
00085 void MoveUnitCommand::changeCoordinates( const MapCoodinateVector& delta )
00086 {
00087    destination += delta;
00088 }
00089 
00090 MoveUnitCommand::MoveUnitCommand( GameMap* map ) : UnitCommand( map ) 
00091 {
00092    map->sigCoordinateShift.connect( SigC::slot( *this, &MoveUnitCommand::changeCoordinates ));
00093 }
00094 
00095 
00096 
00097 MoveUnitCommand :: MoveUnitCommand ( Vehicle* unit )
00098    : UnitCommand ( unit ), flags(0), verticalDirection(0), multiTurnMovement(false)
00099 {
00100    if ( unit )
00101       unit->getMap()->sigCoordinateShift.connect( SigC::slot( *this, &MoveUnitCommand::changeCoordinates ));
00102 }
00103 
00104 
00105 class HeightChangeLimitation: public AStar3D::OperationLimiter {
00106          bool allow_Height_Change;
00107       public:
00108          HeightChangeLimitation ( bool allow_Height_Change_ ) : allow_Height_Change ( allow_Height_Change_ ) {};
00109          virtual bool allowHeightChange() { return allow_Height_Change; };
00110          virtual bool allowMovement() { return true; };
00111          virtual bool allowEnteringContainer() { return true; };
00112          virtual bool allowLeavingContainer() { return true; };
00113          virtual bool allowDocking() { return true; };
00114 };
00115 
00116 class MovementLimitation: public AStar3D::OperationLimiter {
00117          bool simpleMode;
00118          int hcNum;
00119       public:
00120          MovementLimitation ( bool simpleMode_ ) : simpleMode ( simpleMode_ ), hcNum(0) {};
00121          virtual bool allowHeightChange() { ++hcNum; if ( simpleMode) return hcNum <= 1 ; else return true; };
00122          virtual bool allowMovement() { return !simpleMode; };
00123          virtual bool allowEnteringContainer() { return true; };
00124          virtual bool allowLeavingContainer() { return true; };
00125          virtual bool allowDocking() { return true; };
00126 };
00127 
00128       
00129 class PathFinder : public AStar3D {
00130    public:
00131       PathFinder ( Vehicle* veh, int maxDistance ) : AStar3D(veh->getMap(), veh, false, maxDistance ) {};
00132 
00137       void getMovementFields ( set<MapCoordinate3D>& reachableFields, set<MapCoordinate3D>& reachableFieldsIndirect, int height );
00138 };
00139       
00140       
00141 void PathFinder :: getMovementFields ( set<MapCoordinate3D>& reachableFields, set<MapCoordinate3D>& reachableFieldsIndirect, int height )
00142 {
00143    Path dummy;
00144    findPath ( dummy, MapCoordinate3D(-1, -1, veh->height) );  //this field does not exist...
00145 
00146    int unitHeight = veh->getPosition().getNumericalHeight();
00147    if ( !this->actmap->getField ( veh->getPosition())->unitHere ( veh ))
00148       unitHeight = -1;
00149 
00150    // there are different entries for the same x/y coordinate but different height.
00151    // Since the UI is only in xy, we need to find the height which is the easiest to reach
00152    typedef multimap<MapCoordinate,Container::iterator > Fields;
00153    Fields fields;
00154    int orgHeight=-1;
00155    int minMovement = maxint;
00156    for ( Container::iterator i = visited.begin(); i != visited.end(); ++i ) {
00157       if ( i->h.x != veh->getPosition().x || i->h.y != veh->getPosition().y || i->h.getNumericalHeight() != unitHeight ) {
00158          int h = i->h.getNumericalHeight();
00159          // if ( h == -1 )
00160          //   h = i->enterHeight;
00161          if ( h == -1 || height == -1 || h == height ) {
00162             if ( i->canStop )
00163                fields.insert(make_pair(MapCoordinate(i->h),  i));
00164             else
00165                reachableFieldsIndirect.insert( i->h );
00166          }
00167       }
00168       if ( i->h.getNumericalHeight() >= 0 )
00169          if ( i->gval < minMovement ) {
00170             orgHeight = i->h.getNumericalHeight();
00171             minMovement = int (i->gval);
00172          }
00173    }
00174    for ( Fields::iterator i = fields.begin(); i != fields.end();  ) {
00175       int height = i->second->h.getNumericalHeight();
00176       int move = int(i->second->gval);
00177       Fields::key_type key = i->first;
00178       ++i;
00179       while ( i != fields.end() && i->first == key ) {
00180          if ( i->second->gval  < move || ( i->second->gval == move && abs(i->second->h.getNumericalHeight()-orgHeight) < abs(height-orgHeight) ))
00181             height = i->second->h.getNumericalHeight();
00182          ++i;
00183       }
00184       MapCoordinate3D f;
00185       f.setnum( key.x, key.y, height );
00186       reachableFields.insert ( f );
00187    }
00188 }
00189       
00190 
00191 ActionResult MoveUnitCommand::searchFields(int height, int capabilities)
00192 {
00193    Vehicle* veh = getUnit();
00194    if ( !veh ) 
00195       return ActionResult(101);
00196 
00197    int h;
00198    if ( getMap()->getField(veh->getPosition())->unitHere(veh) )
00199       h = getFirstBit(veh->height); // != height-change: true
00200    else 
00201       h = -1; // height-change = false
00202       
00203    if ( height == -2  )
00204       h = -1;
00205 
00206    if ( (capabilities | flags) & LimitVerticalDirection ) {
00207       if ( getVerticalDirection() == 0 ) {
00208          HeightChangeLimitation hcl ( !(capabilities & DisableHeightChange) );
00209          PathFinder pf ( veh, veh->getMovement() );
00210          pf.registerOperationLimiter( &hcl );
00211          pf.getMovementFields ( reachableFields, reachableFieldsIndirect, h );
00212       } else {
00213          const VehicleType::HeightChangeMethod* hcm = veh->getHeightChange( getVerticalDirection() );
00214          if ( !hcm )
00215             fatalError ( "Inconsistent call to changeheight ");
00216    
00217          h = veh->getPosition().getNumericalHeight() + hcm->heightDelta;
00218          
00219          MovementLimitation ml ( capabilities & ShortestHeightChange );
00220          PathFinder pf ( veh, veh->getMovement() );
00221          pf.registerOperationLimiter( &ml );
00222          pf.getMovementFields ( reachableFields, reachableFieldsIndirect, h );
00223       }
00224    } else {
00225       PathFinder pf ( veh, veh->getMovement() );
00226       pf.getMovementFields ( reachableFields, reachableFieldsIndirect, -1 );
00227    }
00228 
00229    if ( reachableFields.size() == 0 ) 
00230       return ActionResult(107);
00231    
00232    setState( Evaluated );
00233    
00234    return ActionResult(0);
00235 }
00236 
00237 
00238 
00239 void MoveUnitCommand :: setDestination( const MapCoordinate3D& destination )
00240 {
00241    this->destination = destination;
00242    setState( SetUp );
00243 }
00244 
00245 void MoveUnitCommand :: setDestination( const MapCoordinate& destination )
00246 {
00247    if ( getState() != Evaluated )
00248       searchFields();
00249    
00250    bool found = false;
00251    for ( set<MapCoordinate3D>::iterator i = reachableFields.begin(); i != reachableFields.end(); ++i )
00252       if ( destination.x == i->x && destination.y == i->y ) {
00253          this->destination = *i;
00254          found = true;
00255          multiTurnMovement = false;
00256          break;
00257       }
00258       
00259    if ( !found ) {
00260       // assuming long dist movement
00261       MapField* fld  = getMap()->getField( destination );
00262       if ( !fld )
00263          return;
00264       
00265       if ( fld->getContainer() ) 
00266          this->destination.setnum( destination.x, destination.y, -1 );
00267       else
00268           this->destination = MapCoordinate3D( destination, getUnit()->getHeight() );
00269       
00270       
00271       multiTurnMovement = true;
00272    }
00273    
00274    setState( SetUp );
00275 }
00276 
00277 bool MoveUnitCommand::isFieldReachable( const MapCoordinate& pos, bool direct )
00278 {
00279    if ( direct ) {
00280       for ( set<MapCoordinate3D>::iterator i = reachableFields.begin(); i != reachableFields.end(); ++i )
00281          if ( i->x == pos.x && i->y == pos.y )
00282             return true;
00283    } else {
00284       for ( set<MapCoordinate3D>::iterator i = reachableFieldsIndirect.begin(); i != reachableFieldsIndirect.end(); ++i )
00285          if ( i->x == pos.x && i->y == pos.y )
00286             return true;
00287    }
00288    return false;  
00289 }
00290 
00291 bool MoveUnitCommand::isFieldReachable3D( const MapCoordinate3D& pos, bool direct )
00292 {
00293    AStar3D ast ( getMap(), getUnit(), false, getUnit()->getMovement() );
00294    AStar3D::Path localPath;
00295    ast.findPath ( localPath, pos );
00296    return !localPath.empty();
00297 }
00298 
00299 
00300 void MoveUnitCommand::calcPath()
00301 {
00302    path.clear();
00303    {
00304       AStar3D ast ( getMap(), getUnit(), false, getUnit()->getMovement() );
00305       ast.findPath ( path, destination );
00306    }
00307    
00308    
00309    if ( path.empty() && multiTurnMovement ) {
00310       
00311       bool enterContainer = true;
00312       
00313       AStar3D::Path totalPath;
00314       AStar3D astar ( getMap(), getUnit(), false );
00315    
00316       astar.findPath ( totalPath, destination );
00317       if ( totalPath.empty() )
00318          return;
00319    
00320       AStar3D::Path::const_iterator pi = totalPath.begin();
00321       AStar3D::Path::const_iterator lastmatch = totalPath.begin();
00322       
00323       while ( pi != totalPath.end() ) {
00324          MapField* fld = getMap()->getField ( pi->x, pi->y );
00325          bool ok = true;
00326          if ( fld->getContainer() ) {
00327             if ( pi+1 !=totalPath.end() )
00328                ok = false;
00329             else {
00330                if ( fld->building && !enterContainer )
00331                   ok = false;
00332                if ( fld->vehicle && !enterContainer )
00333                   ok = false;
00334             }
00335          }
00336 
00337          if ( ok )
00338             if ( isFieldReachable3D(*pi, true) )
00339                lastmatch = pi;
00340 
00341          ++pi;
00342       }
00343 
00344       if ( lastmatch == totalPath.begin() )
00345          return;
00346   
00347       AStar3D ast ( getMap(), getUnit(), false, getUnit()->getMovement() );
00348       ast.findPath ( path, *lastmatch );
00349    }
00350 }
00351 
00352 const AStar3D::Path& MoveUnitCommand::getPath()
00353 {
00354    return path;  
00355 }
00356 
00357 
00358 ActionResult MoveUnitCommand::go ( const Context& context )
00359 {
00360    if ( getState() != SetUp )
00361       return ActionResult(22000);
00362    
00363    /*
00364    searchFields();
00365    
00366    if ( reachableFields.find( destination ) == reachableFields.end() ) 
00367       return ActionResult(105);
00368 */
00369    if ( !getUnit() )
00370       return ActionResult(21001);
00371    
00372    if (( getUnit()->getPosition() == destination) || (getUnit()->getPosition().x == destination.x && getUnit()->getPosition().y == destination.y && destination.getNumericalHeight() == -1  ))
00373       return ActionResult(22003);
00374    
00375    calcPath();
00376    
00377    if ( path.empty() ) 
00378       return ActionResult( 105 );
00379    
00380    if ( !multiTurnMovement && ( path.rbegin()->x != destination.x || path.rbegin()->y != destination.y  )) 
00381       return ActionResult( 105 );
00382    
00383    int nwid = getUnit()->networkid;
00384 
00385    if ( context.display )
00386       context.display->startAction();
00387    
00388    ActionResult res = (new MoveUnit(getUnit(), path, flags&NoInterrupt))->execute(context);
00389    
00390    if ( context.display )
00391       context.display->stopAction();
00392 
00393 
00394    int destDamage;
00395    if ( getMap()->getUnit( nwid ))
00396       destDamage = getUnit()->damage;
00397    else
00398       destDamage = 100;
00399 
00400    if ( res.successful() ) {
00401       if ( destDamage < 100 ) {
00402          if ( multiTurnMovement && getUnit()->getPosition3D() != destination )
00403             setState( Run );
00404          else
00405             setState( Finished );
00406       } else 
00407          setState( Finished );
00408    } else
00409       setState( Failed );
00410    
00411    return res;
00412 
00413 }
00414 
00415 void MoveUnitCommand :: setVerticalDirection( int dir )
00416 {
00417    verticalDirection = dir;
00418    flags |= LimitVerticalDirection;  
00419 }
00420 
00421 
00422 static const int moveCommandVersion = 2;
00423 
00424 void MoveUnitCommand :: readData ( tnstream& stream )
00425 {
00426    UnitCommand::readData( stream );
00427    int version = stream.readInt();
00428    if ( version > moveCommandVersion )
00429       throw tinvalidversion ( "MoveCommand", moveCommandVersion, version );
00430    destination.read( stream );
00431    
00432    flags = stream.readInt( );
00433    verticalDirection = stream.readInt();
00434    if ( version >= 2 )
00435       multiTurnMovement = stream.readInt();
00436    else
00437       multiTurnMovement = false;
00438 }
00439 
00440 void MoveUnitCommand :: writeData ( tnstream& stream ) const
00441 {
00442    UnitCommand::writeData( stream );
00443    stream.writeInt( moveCommandVersion );
00444    destination.write( stream );
00445    stream.writeInt( flags );
00446    stream.writeInt( verticalDirection );
00447    stream.writeInt( multiTurnMovement );
00448 }
00449 
00450 ASCString MoveUnitCommand :: getCommandString() const {
00451    ASCString c;
00452    c.format("unitMovement ( map, %d, asc.MapCoordinate( %d, %d), %d )", getUnitID(), destination.x, destination.y, destination.getNumericalHeight() );
00453    return c;
00454 }
00455 
00456 GameActionID MoveUnitCommand::getID() const 
00457 {
00458    return ActionRegistry::MoveUnitCommand;   
00459 }
00460 
00461 ASCString MoveUnitCommand::getDescription() const
00462 {
00463    ASCString s = "Move unit"; 
00464    if ( getUnit() ) {
00465       s += " " + getUnit()->getName();
00466    }
00467    s += " to " + destination.toString();
00468    return s;
00469 }
00470 
00471 
00472 int MoveUnitCommand::getCompletion()
00473 {
00474    return 50;
00475 }
00476 
00477 bool MoveUnitCommand::operatable()
00478 {
00479    return getUnit();
00480 }
00481 
00482 void MoveUnitCommand::rearm()
00483 {
00484    if ( getState() == Finished || getState() == Run )
00485       setState( SetUp );
00486    
00487    deleteChildren();
00488 }
00489 
00490 vector<MapCoordinate> MoveUnitCommand::getCoordinates() const
00491 {
00492    vector<MapCoordinate> pos;
00493    pos.push_back( destination );
00494    return pos;
00495 }
00496 
00497 
00498 namespace {
00499    const bool r1 = registerAction<MoveUnitCommand> ( ActionRegistry::MoveUnitCommand );
00500 }

Generated on Mon May 21 01:26:35 2012 for Advanced Strategic Command by  doxygen 1.5.1