00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <cmath>
00022
00023 #include "moveunit.h"
00024 #include "unitfieldregistration.h"
00025 #include "changeunitproperty.h"
00026 #include "consumeresource.h"
00027 #include "changeview.h"
00028 #include "convertcontainer.h"
00029 #include "discoverresources.h"
00030 #include "changeunitmovement.h"
00031 #include "action-registry.h"
00032
00033 #include "../vehicle.h"
00034 #include "../gamemap.h"
00035 #include "../reactionfire.h"
00036 #include "../soundList.h"
00037 #include "../controls.h"
00038 #include "../gameoptions.h"
00039 #include "../mapdisplayinterface.h"
00040 #include "../viewcalculation.h"
00041 #include "../spfst.h"
00042 #include "../gameeventsystem.h"
00043
00044
00045 void printTimer( int i )
00046 {
00047 #if 0
00048 static int lastTimer = 0;
00049 if ( i == 1 )
00050 lastTimer = SDL_GetTicks();
00051 else {
00052 printf("%d - %d : %d \n", i-1, i, SDL_GetTicks() - lastTimer);
00053 lastTimer = SDL_GetTicks();
00054 }
00055 #endif
00056 }
00057
00058
00059 MoveUnit::MoveUnit( Vehicle* veh, AStar3D::Path& pathToMove, bool dontInterrupt )
00060 : UnitAction( veh->getMap(), veh->networkid )
00061 {
00062 this->pathToMove = pathToMove;
00063 this->dontInterrupt = dontInterrupt;
00064 this->originalUnitMovement = veh->getMovement(false,false);
00065 }
00066
00067
00068 ASCString MoveUnit::getDescription() const
00069 {
00070 ASCString res = "Move unit ";
00071 if ( getUnit(false) )
00072 res += getUnit(false)->getName();
00073
00074 return res;
00075 }
00076
00077
00078 static const int moveUnitStreamVersion = 2;
00079
00080 void MoveUnit::readData ( tnstream& stream )
00081 {
00082 UnitAction::readData( stream );
00083 int version = stream.readInt();
00084 if ( version < 1 || version > moveUnitStreamVersion )
00085 throw tinvalidversion ( "ChangeUnitMovement", moveUnitStreamVersion, version );
00086
00087 dontInterrupt = stream.readInt();
00088 readClassContainerStaticConstructor( pathToMove, stream );
00089 if ( version >= 2 )
00090 originalUnitMovement = stream.readInt();
00091 else
00092 originalUnitMovement = -1;
00093 };
00094
00095
00096 void MoveUnit::writeData ( tnstream& stream ) const
00097 {
00098 UnitAction::writeData( stream );
00099 stream.writeInt( moveUnitStreamVersion );
00100 stream.writeInt( dontInterrupt );
00101 writeClassContainer( pathToMove, stream );
00102 stream.writeInt( originalUnitMovement );
00103 };
00104
00105
00106 GameActionID MoveUnit::getID() const
00107 {
00108 return ActionRegistry::MoveUnit;
00109 }
00110
00111
00112 ActionResult MoveUnit::runAction( const Context& context )
00113 {
00114 Vehicle* vehicle = getUnit();
00115
00116
00117 auto_ptr<WindMovement> wind;
00118
00119 if ( (vehicle->typ->height & ( chtieffliegend | chfliegend | chhochfliegend )) && getMap()->weather.windSpeed ) {
00120 wind.reset( new WindMovement ( vehicle ) );
00121 }
00122
00123 MapField* oldfield = getMap()->getField( vehicle->getPosition() );
00124
00125 AStar3D::Path::iterator pos = pathToMove.begin();
00126 AStar3D::Path::iterator stop = pathToMove.end()-1;
00127
00128 tsearchreactionfireingunits srfu( getMap() );
00129 treactionfire* rf = &srfu;
00130
00131 int orgMovement = vehicle->getMovement( false );
00132 int orgHeight = vehicle->height;
00133
00134 rf->init( vehicle, pathToMove );
00135
00136 if ( oldfield->vehicle == vehicle)
00137 (new UnitFieldRegistration( vehicle, vehicle->getPosition(), UnitFieldRegistration::RemoveView ))->execute( context );
00138
00139
00140 (new UnitFieldRegistration( vehicle, vehicle->getPosition(), UnitFieldRegistration::UnregisterOnField ))->execute( context );
00141
00142 int soundHeight = -1;
00143 if ( pos->getRealHeight() >= 0 )
00144 soundHeight = pos->getRealHeight();
00145 else
00146 soundHeight = stop->getRealHeight();
00147
00148 SoundLoopManager slm ( SoundList::getInstance().getSound( SoundList::moving, vehicle->typ->movemalustyp, vehicle->typ->movementSoundLabel, soundHeight ), false );
00149
00150 int cancelmovement = 0;
00151
00152 int movedist = 0;
00153 int fueldist = 0;
00154 int networkID = vehicle->networkid;
00155 int operatingPlayer = context.actingPlayer->getPosition();
00156
00157 bool viewInputChanged= false;
00158 bool mapDisplayUpToDate = true;
00159 bool finalRedrawNecessary = false;
00160
00161 bool inhibitAttack = false;
00162 while ( pos != stop && vehicle && cancelmovement!=1 ) {
00163
00164 if ( cancelmovement > 1 )
00165 cancelmovement--;
00166
00167 AStar3D::Path::iterator next = pos+1;
00168
00169
00170 bool container2container = pos->getNumericalHeight()==-1 && next->getNumericalHeight() == -1;
00171 pair<int,int> mm = calcMoveMalus( *pos, next->getRealPos(), vehicle, wind.get(), &inhibitAttack, container2container );
00172 movedist += mm.first;
00173 fueldist += mm.second;
00174
00175 if ( next->hasAttacked )
00176 vehicle->setAttacked( true, context );
00177
00178
00179 if ( next->getRealHeight() != pos->getRealHeight() && next->getRealHeight() >= 0 )
00180 (new ChangeUnitProperty( vehicle, ChangeUnitProperty::Height, 1 << next->getRealHeight() ))->execute( context );
00181
00182 int pathStepNum = beeline ( *pos, *next ) / maxmalq;
00183 int pathStep = 0;
00184 if ( !pathStepNum )
00185 pathStepNum = 1;
00186
00187 MapCoordinate3D to = *pos;
00188 do {
00189 MapCoordinate3D from;
00190 from.setnum ( to.x, to.y, pos->getRealHeight() );
00191 if ( next->x != from.x || next->y != from.y )
00192 to = getNeighbouringFieldCoordinate ( to, getdirection ( to, *next ));
00193 to.setnum ( to.x, to.y, next->getRealHeight() );
00194
00195 MapField* dest = getMap()->getField ( to );
00196
00197
00198 if ( vehicle ) {
00199 vehicle->setnewposition(to, context );
00200 (new UnitFieldRegistration( vehicle, to, UnitFieldRegistration::AddView ))->execute( context );
00201 if ( vehicle->typ->hasFunction( ContainerBaseType::DetectsMineralResources ) )
00202 (new DiscoverResources(vehicle))->execute(context);
00203 }
00204
00205
00206 printTimer(1);
00207
00208 {
00209 int dir = getdirection( from, to );
00210 if ( dir >= 0 && dir <= 5 )
00211 (new ChangeUnitProperty( vehicle, ChangeUnitProperty::Direction, dir ))->execute( context );
00212 }
00213
00214
00215 if ( context.display ) {
00216
00217 if ( next == stop && to.x==next->x && to.y==next->y)
00218 slm.fadeOut ( CGameOptions::Instance()->movespeed * 10 );
00219 context.display->displayMovingUnit ( from, to, vehicle, pathStep, pathStepNum, MapDisplayInterface::SoundStartCallback( &slm, &SoundLoopManager::activate ), context.display->getUnitMovementDuration() );
00220 finalRedrawNecessary = true;
00221 mapDisplayUpToDate = false;
00222 }
00223 pathStep++;
00224
00225 printTimer(4);
00226
00227 if ( vehicle ) {
00228 if ( vehicle->spawnMoveObjects( from, to, context ) )
00229 mapDisplayUpToDate = false;
00230
00231
00232 if ( inhibitAttack )
00233 vehicle->setAttacked( true, context );
00234 }
00235
00236
00237 printTimer(5);
00238 {
00239 dest->secondvehicle = vehicle;
00240 int fieldsWidthChangedVisibility;
00241 if ( context.viewingPlayer >= 0 )
00242 fieldsWidthChangedVisibility = evaluateviewcalculation ( getMap(), 1 << context.viewingPlayer, false, &context );
00243 else
00244 fieldsWidthChangedVisibility = evaluateviewcalculation ( getMap(), 0, false, &context );
00245
00246
00247 if ( fieldsWidthChangedVisibility )
00248 mapDisplayUpToDate = false;
00249
00250 dest->secondvehicle = NULL;
00251 printTimer(6);
00252 }
00253
00254 viewInputChanged = false;
00255
00256 if ( vehicle ) {
00257
00258 if ( context.display && fieldvisiblenow ( dest, vehicle, context.viewingPlayer ) ) {
00259
00260
00261 int oldheight = vehicle->height;
00262 if ( next->getRealHeight() > pos->getRealHeight() && pathStep < pathStepNum )
00263 vehicle->height = 1 << pos->getRealHeight();
00264
00265 if ( !mapDisplayUpToDate ) {
00266 context.display->displayMap( vehicle );
00267 mapDisplayUpToDate = true;
00268 finalRedrawNecessary = false;
00269 }
00270
00271 vehicle->height = oldheight;
00272 }
00273
00274 dest->secondvehicle = vehicle;
00275 if ( rf->checkfield ( to, vehicle, context )) {
00276 cancelmovement = 1;
00277 vehicle = getMap()->getUnit ( networkID );
00278 }
00279
00280 if ( vehicle && dest->mineattacks ( vehicle )) {
00281 tmineattacksunit battle ( to, -1, vehicle );
00282
00283 if ( context.display && (fieldvisiblenow ( dest, context.viewingPlayer) || dest->mineowner() == context.viewingPlayer ))
00284 context.display->showBattle( battle );
00285 else
00286 battle.calc();
00287
00288 battle.setresult ( context );
00289 if ( battle.dv.damage >= 100 ) {
00290 vehicle = NULL;
00291 viewInputChanged = true;
00292 }
00293
00294 updateFieldInfo();
00295 cancelmovement = 1;
00296 mapDisplayUpToDate = false;
00297 }
00298 dest->secondvehicle = NULL;
00299
00300
00301
00302 if ( !vehicle && context.display ) {
00303 context.display->displayMap();
00304 mapDisplayUpToDate = true;
00305 finalRedrawNecessary = false;
00306
00307 viewInputChanged = true;
00308 }
00309 } else
00310 if ( context.display ) {
00311 context.display->displayMap();
00312 mapDisplayUpToDate = true;
00313 finalRedrawNecessary = false;
00314 }
00315
00316 printTimer(7);
00317
00318
00319
00320 if ( vehicle )
00321 if ( !(stop->x == to.x && stop->y == to.y && next == stop ))
00322 (new UnitFieldRegistration( vehicle, to, UnitFieldRegistration::RemoveView ))->execute( context );
00323
00324 if ( cancelmovement == 1 )
00325 if ( dest->vehicle || dest->building )
00326 cancelmovement++;
00327
00328 if ( vehicle )
00329 if ( dontInterrupt )
00330 cancelmovement = 0;
00331
00332 if ( vehicle ) {
00333 dest->secondvehicle = vehicle;
00334 if ( dest->connection & cconnection_areaentered_anyunit )
00335 fieldCrossed( context );
00336
00337 if ((dest->connection & cconnection_areaentered_specificunit ) && ( vehicle->connection & cconnection_areaentered_specificunit ))
00338 fieldCrossed( context );
00339 dest->secondvehicle = NULL;
00340 }
00341 printTimer(8);
00342 } while ( (to.x != next->x || to.y != next->y) && vehicle );
00343
00344 pos = next;
00345 }
00346
00347 MapField* fld = getMap()->getField ( pos->x, pos->y );
00348
00349 if ( vehicle ) {
00350
00351 int newMovement = orgMovement - pos->dist;
00352
00353 vehicle->setnewposition( *pos, context );
00354
00355 if ( vehicle->typ->movement[getFirstBit(orgHeight)] ) {
00356 if ( orgHeight != vehicle->height ) {
00357
00358 int move = int(floor(vehicle->maxMovement() * float(orgMovement) / float(vehicle->typ->movement[getFirstBit(orgHeight)]) + 0.5));
00359 (new ChangeUnitMovement( vehicle, move, false, ChangeUnitMovement::NONE ))->execute(context);
00360 }
00361
00362
00363 int nm = int(floor(vehicle->maxMovement() * float(newMovement) / float(vehicle->typ->movement[getFirstBit(orgHeight)]) + 0.5));
00364
00365 (new ChangeUnitMovement( vehicle, nm, false, ChangeUnitMovement::NORMAL))->execute(context);
00366
00367
00368 if ( vehicle->getMovement() < 10 )
00369 finalRedrawNecessary = true;
00370 }
00371
00372
00373 (new ConsumeResource( vehicle, Resources(0,0,fueldist * vehicle->typ->fuelConsumption / maxmalq )))->execute( context );
00374
00375 if ( fld->vehicle || fld->building ) {
00376 (new ChangeUnitMovement( vehicle, 0, false, ChangeUnitMovement::NONE))->execute(context);
00377 vehicle->setAttacked( false, context );
00378 }
00379
00380 if ( vehicle ) {
00381 if ((fld->vehicle == NULL) && (fld->building == NULL)) {
00382 if ( !vehicle->isViewing() ) {
00383 (new UnitFieldRegistration( vehicle, *pos, UnitFieldRegistration::AddView ))->execute( context );
00384 viewInputChanged = true;
00385
00386
00387
00388
00389
00390
00391
00392
00393 (new UnitFieldRegistration( vehicle, *pos, UnitFieldRegistration::RegisterOnField ))->execute( context );
00394
00395 } else {
00396 (new UnitFieldRegistration( vehicle, *pos, UnitFieldRegistration::RegisterOnField ))->execute( context );
00397 int orgVisibility = fld->visible;
00398 for ( int i = 0; i < getMap()->getPlayerCount(); ++i )
00399 evaluatevisibilityfield ( getMap(), fld, i, -1, getMap()->getgameparameter ( cgp_initialMapVisibility ) );
00400
00401 if ( fld->visible != orgVisibility ) {
00402 ChangeView::ViewState viewState;
00403 viewState[MapCoordinate(pos->x,pos->y)] = fld->visible;
00404 fld->visible = orgVisibility;
00405 (new ChangeView(getMap(),viewState))->execute(context);
00406 }
00407 }
00408
00409 } else {
00410 ContainerBase* cn = fld->getContainer();
00411 if ( vehicle->isViewing() ) {
00412 (new UnitFieldRegistration( vehicle, *pos, UnitFieldRegistration::RemoveView ))->execute( context );
00413 viewInputChanged = true;
00414 }
00415
00416 (new UnitFieldRegistration( vehicle, *pos, UnitFieldRegistration::RegisterOnField ))->execute( context );
00417
00418 if (cn->getOwner() != vehicle->getOwner() && fld->building && getMap()->getPlayer(fld->building).diplomacy.isHostile( vehicle) ) {
00419 (new ConvertContainer( fld->building, vehicle->getOwner()))->execute(context);
00420 if ( fieldvisiblenow ( fld, context.viewingPlayer ) || context.viewingPlayer == vehicle->getOwner() )
00421 SoundList::getInstance().playSound ( SoundList::conquer_building, 0 );
00422 viewInputChanged = true;
00423 }
00424 mapDisplayUpToDate = false;
00425
00426 }
00427
00428 }
00429 }
00430
00431 if ( rf->finalCheck( operatingPlayer, context ))
00432 finalRedrawNecessary = true;
00433
00434
00435 finalRedrawNecessary = true;
00436
00437 if ( viewInputChanged ) {
00438 int fieldschanged;
00439 if ( context.viewingPlayer >= 0 )
00440 fieldschanged = evaluateviewcalculation ( getMap(), 1 << context.viewingPlayer, false, &context );
00441 else
00442 fieldschanged = evaluateviewcalculation ( getMap(), 0, false, &context );
00443
00444 if ( fieldschanged )
00445 mapDisplayUpToDate = false;
00446
00447 }
00448
00449 if ( context.display ) {
00450 context.display->resetMovement();
00451
00452 if (finalRedrawNecessary || !mapDisplayUpToDate)
00453 context.display->displayMap();
00454
00455
00456 }
00457
00458 return ActionResult(0);
00459 }
00460
00461
00462 ActionResult MoveUnit::undoAction( const Context& context )
00463 {
00464 if ( originalUnitMovement != -1 )
00465 getUnit()->setMovement( originalUnitMovement, 0 );
00466
00467 return ActionResult(0);
00468 }
00469
00470 ActionResult MoveUnit::preCheck()
00471 {
00472 return ActionResult(0);
00473 }
00474
00475 ActionResult MoveUnit::postCheck()
00476 {
00477 return ActionResult(0);
00478 }
00479
00480
00481
00482 namespace {
00483 const bool r1 = registerAction<MoveUnit> ( ActionRegistry::MoveUnit );
00484 }