00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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) );
00145
00146 int unitHeight = veh->getPosition().getNumericalHeight();
00147 if ( !this->actmap->getField ( veh->getPosition())->unitHere ( veh ))
00148 unitHeight = -1;
00149
00150
00151
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
00160
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);
00200 else
00201 h = -1;
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
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
00365
00366
00367
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 }