00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <vector>
00022 #include <algorithm>
00023 #include <cmath>
00024 #include <SDL.h>
00025 #include <SDL_thread.h>
00026
00027 #include "unitctrl.h"
00028 #include "controls.h"
00029 #include "dialog.h"
00030 #include "attack.h"
00031 #include "stack.h"
00032 #include "vehicletype.h"
00033 #include "buildingtype.h"
00034 #include "viewcalculation.h"
00035 #include "replay.h"
00036 #include "gameoptions.h"
00037 #include "itemrepository.h"
00038 #include "astar2.h"
00039 #include "containercontrols.h"
00040 #include "mapdisplayinterface.h"
00041 #include "gameeventsystem.h"
00042 #include "actions/servicing.h"
00043 #include "soundList.h"
00044 #include "reactionfire.h"
00045
00046 PendingVehicleActions pendingVehicleActions;
00047
00048 SigC::Signal0<void> fieldCrossed;
00049
00050
00051
00052
00053 void BaseVehicleMovement :: PathFinder :: getMovementFields ( IntFieldList& reachableFields, IntFieldList& reachableFieldsIndirect, int height )
00054 {
00055 Path dummy;
00056 findPath ( dummy, MapCoordinate3D(-1, -1, veh->height) );
00057
00058 int unitHeight = veh->getPosition().getNumericalHeight();
00059 if ( !actmap->getField ( veh->getPosition())->unitHere ( veh ))
00060 unitHeight = -1;
00061
00062
00063
00064 typedef multimap<MapCoordinate,Container::iterator > Fields;
00065 Fields fields;
00066 int orgHeight=-1;
00067 int minMovement = maxint;
00068 for ( Container::iterator i = visited.begin(); i != visited.end(); ++i ) {
00069 if ( i->h.x != veh->getPosition().x || i->h.y != veh->getPosition().y || i->h.getNumericalHeight() != unitHeight ) {
00070 int h = i->h.getNumericalHeight();
00071
00072
00073 if ( h == -1 || height == -1 || h == height ) {
00074 if ( i->canStop )
00075 fields.insert(make_pair(MapCoordinate(i->h), i));
00076 else
00077 reachableFieldsIndirect.addField ( i->h, i->h.getNumericalHeight() );
00078 }
00079 }
00080 if ( i->h.getNumericalHeight() >= 0 )
00081 if ( i->gval < minMovement ) {
00082 orgHeight = i->h.getNumericalHeight();
00083 minMovement = int (i->gval);
00084 }
00085 }
00086 for ( Fields::iterator i = fields.begin(); i != fields.end(); ) {
00087 int height = i->second->h.getNumericalHeight();
00088 int move = int(i->second->gval);
00089 Fields::key_type key = i->first;
00090 ++i;
00091 while ( i != fields.end() && i->first == key ) {
00092 if ( i->second->gval < move || ( i->second->gval == move && abs(i->second->h.getNumericalHeight()-orgHeight) < abs(height-orgHeight) ))
00093 height = i->second->h.getNumericalHeight();
00094 ++i;
00095 }
00096 reachableFields.addField ( key, height );
00097 }
00098 }
00099
00100 bool multiThreadedViewCalculation = false;
00101
00102
00103
00104 class BackgroundViewCalculator {
00105 private:
00106 SDL_Thread *viewThreat;
00107 SDL_sem* sem;
00108 bool endThreat;
00109 int changedFields;
00110
00111 enum Status { waiting, dataavail, running, finished } status;
00112
00113 public:
00114
00115
00116 struct Data {
00117 GameMap* gamemap;
00118 int view;
00119 Data( GameMap* map, int v ) : gamemap ( map ), view( v) {};
00120 Data() : gamemap(NULL), view(-1) {};
00121 };
00122
00123 BackgroundViewCalculator() : endThreat(false), changedFields(0)
00124 {
00125 status = waiting;
00126 sem = SDL_CreateSemaphore( 1 );
00127 viewThreat = SDL_CreateThread( &BackgroundViewCalculator::calculator, this );
00128 }
00129
00130 void postData( Data data )
00131 {
00132 SDL_SemWait( sem );
00133 if ( status == waiting || status == finished ) {
00134 this->data = data;
00135 status = dataavail;
00136 } else
00137 fatalError( "Sequence error in BackgroundViewCalculator");
00138 SDL_SemPost(sem );
00139
00140 }
00141
00142 bool dataAvail( Data& data )
00143 {
00144 bool result;
00145 SDL_SemWait( sem );
00146 if ( status == dataavail ) {
00147 result = true;
00148 data = this->data;
00149 } else
00150 result = false;
00151 SDL_SemPost(sem );
00152 return result;
00153 }
00154
00155 void setCalculationCompletion( int changedFields )
00156 {
00157 SDL_SemWait( sem );
00158 status = finished;
00159 this->changedFields = changedFields;
00160 SDL_SemPost(sem );
00161 }
00162
00163 bool isCalculationCompleted()
00164 {
00165 bool result;
00166 SDL_SemWait( sem );
00167 if ( status == finished ) {
00168 result = true;
00169 } else
00170 result = false;
00171 SDL_SemPost(sem );
00172 return result;
00173 }
00174
00175 int waitForCompletion()
00176 {
00177 while ( !isCalculationCompleted() )
00178 SDL_Delay(10);
00179 return changedFields;
00180 }
00181
00182 bool haltThreat()
00183 {
00184 return endThreat;
00185 }
00186
00187 ~BackgroundViewCalculator()
00188 {
00189 endThreat = true;
00190 SDL_WaitThread( viewThreat, NULL );
00191 SDL_DestroySemaphore( sem );
00192 }
00193
00194 private:
00195
00196 Data data;
00197
00198 static int calculator( void* object )
00199 {
00200 BackgroundViewCalculator* bvc = static_cast<BackgroundViewCalculator*>(object);
00201 Data data;
00202 do {
00203 SDL_Delay(10);
00204 if ( bvc->dataAvail( data )) {
00205 int changedFields = evaluateviewcalculation ( data.gamemap, data.view );
00206 bvc->setCalculationCompletion( changedFields );
00207 }
00208 } while ( !bvc->haltThreat() && !exitprogram );
00209 return 0;
00210 }
00211
00212 };
00213
00214
00215
00216 void printTimer( int i )
00217 {
00218 #if 0
00219 static int lastTimer = 0;
00220 if ( i == 1 )
00221 lastTimer = SDL_GetTicks();
00222 else {
00223 printf("%d - %d : %d \n", i-1, i, SDL_GetTicks() - lastTimer);
00224 lastTimer = SDL_GetTicks();
00225 }
00226 #endif
00227 }
00228
00229
00230
00231 int BaseVehicleMovement :: moveunitxy(AStar3D::Path& pathToMove, int noInterrupt )
00232 {
00233 WindMovement* wind;
00234
00235 #ifdef WEATHERGENERATOR
00236 if ( (vehicle->typ->height & ( chtieffliegend | chfliegend | chhochfliegend )) && actmap->weatherSystem->getCurrentWindSpeed() ) {
00237 wind = new WindMovement ( vehicle );
00238 } else
00239 wind = NULL;
00240 #else
00241 if ( (vehicle->typ->height & ( chtieffliegend | chfliegend | chhochfliegend )) && actmap->weather.windSpeed ) {
00242 wind = new WindMovement ( vehicle );
00243 } else
00244 wind = NULL;
00245 #endif
00246
00247 tfield* oldfield = getfield( vehicle->xpos, vehicle->ypos );
00248
00249 AStar3D::Path::iterator pos = path.begin();
00250 AStar3D::Path::iterator stop = path.end()-1;
00251
00252 tsearchreactionfireingunits srfu;
00253 treactionfire* rf = &srfu;
00254
00255 int orgMovement = vehicle->getMovement( false );
00256 int orgHeight = vehicle->height;
00257
00258 rf->init( vehicle, pathToMove );
00259
00260 if ( oldfield->vehicle == vehicle) {
00261 vehicle->removeview();
00262 oldfield->vehicle = NULL;
00263 } else {
00264 ContainerBase* cn = oldfield->getContainer();
00265 cn->removeUnitFromCargo( vehicle );
00266 }
00267
00268 int soundHeight = -1;
00269 if ( pos->getRealHeight() >= 0 )
00270 soundHeight = pos->getRealHeight();
00271 else
00272 soundHeight = stop->getRealHeight();
00273
00274 SoundLoopManager slm ( SoundList::getInstance().getSound( SoundList::moving, vehicle->typ->movemalustyp, vehicle->typ->movementSoundLabel, soundHeight ), false );
00275
00276 int cancelmovement = 0;
00277
00278 int movedist = 0;
00279 int fueldist = 0;
00280 int networkID = vehicle->networkid;
00281 int operatingPlayer = vehicle->getOwner();
00282
00283 bool viewInputChanged= false;
00284 bool mapDisplayUpToDate = true;
00285 bool finalRedrawNecessary = false;
00286
00287 bool inhibitAttack = false;
00288 while ( pos != stop && vehicle && cancelmovement!=1 ) {
00289
00290 if ( cancelmovement > 1 )
00291 cancelmovement--;
00292
00293 AStar3D::Path::iterator next = pos+1;
00294
00295
00296 bool container2container = pos->getNumericalHeight()==-1 && next->getNumericalHeight() == -1;
00297 pair<int,int> mm = calcMoveMalus( *pos, next->getRealPos(), vehicle, wind, &inhibitAttack, container2container );
00298 movedist += mm.first;
00299 fueldist += mm.second;
00300
00301 if ( next->hasAttacked )
00302 vehicle->setAttacked();
00303
00304
00305 if ( next->getRealHeight() != pos->getRealHeight() && next->getRealHeight() >= 0 )
00306 vehicle->setNewHeight ( 1 << next->getRealHeight() );
00307
00308 int pathStepNum = beeline ( *pos, *next ) / maxmalq;
00309 int pathStep = 0;
00310 if ( !pathStepNum )
00311 pathStepNum = 1;
00312
00313 MapCoordinate3D to = *pos;
00314 do {
00315 MapCoordinate3D from;
00316 from.setnum ( to.x, to.y, pos->getRealHeight() );
00317 if ( next->x != from.x || next->y != from.y )
00318 to = getNeighbouringFieldCoordinate ( to, getdirection ( to, *next ));
00319 to.setnum ( to.x, to.y, next->getRealHeight() );
00320
00321 tfield* dest = getfield ( to.x, to.y );
00322
00323
00324 if ( vehicle ) {
00325 vehicle->xpos = to.x;
00326 vehicle->ypos = to.y;
00327 vehicle->addview();
00328 }
00329
00330
00331 printTimer(1);
00332 static BackgroundViewCalculator* bvc = NULL;
00333 if ( multiThreadedViewCalculation ) {
00334
00335
00336 if ( !bvc )
00337 bvc = new BackgroundViewCalculator;
00338
00339 printTimer(2);
00340
00341 int view;
00342 if ( actmap->getPlayerView() >= 0 )
00343 view = 1 << actmap->getPlayerView() ;
00344 else
00345 view = 0;
00346
00347
00348 bvc->postData( BackgroundViewCalculator::Data( actmap, view ));
00349 printTimer(3);
00350 }
00351
00352
00353 if ( mapDisplay ) {
00354
00355 if ( next == stop && to.x==next->x && to.y==next->y)
00356 slm.fadeOut ( CGameOptions::Instance()->movespeed * 10 );
00357 mapDisplay->displayMovingUnit ( from, to, vehicle, pathStep, pathStepNum, MapDisplayInterface::SoundStartCallback( &slm, &SoundLoopManager::activate ));
00358 finalRedrawNecessary = true;
00359 mapDisplayUpToDate = false;
00360 }
00361 pathStep++;
00362
00363 printTimer(4);
00364
00365 if ( vehicle ) {
00366 if ( vehicle->spawnMoveObjects( from, to ) )
00367 mapDisplayUpToDate = false;
00368
00369 int dir = getdirection( from, to );
00370 if ( dir >= 0 && dir <= 5 )
00371 vehicle->direction = dir;
00372 if ( inhibitAttack )
00373 vehicle->setAttacked();
00374 }
00375
00376
00377 printTimer(5);
00378 if ( multiThreadedViewCalculation ) {
00379 if ( bvc->waitForCompletion())
00380 mapDisplayUpToDate = false;
00381
00382 } else {
00383 Vehicle* temp = dest->vehicle;
00384
00385 dest->vehicle = vehicle;
00386 int fieldsWidthChangedVisibility;
00387 if ( actmap->getPlayerView() >= 0 )
00388 fieldsWidthChangedVisibility = evaluateviewcalculation ( actmap, 1 << actmap->getPlayerView() );
00389 else
00390 fieldsWidthChangedVisibility = evaluateviewcalculation ( actmap, 0);
00391
00392 if ( fieldsWidthChangedVisibility )
00393 mapDisplayUpToDate = false;
00394 dest->vehicle = temp;
00395 }
00396 printTimer(6);
00397
00398 viewInputChanged = false;
00399
00400 if ( vehicle ) {
00401
00402 if ( mapDisplay && fieldvisiblenow ( dest, vehicle, actmap->getPlayerView() ) ) {
00403
00404
00405 int oldheight = vehicle->height;
00406 if ( next->getRealHeight() > pos->getRealHeight() && pathStep < pathStepNum )
00407 vehicle->height = 1 << pos->getRealHeight();
00408
00409 if ( !mapDisplayUpToDate ) {
00410 mapDisplay->displayMap( vehicle );
00411 mapDisplayUpToDate = true;
00412 finalRedrawNecessary = false;
00413 }
00414
00415 vehicle->height = oldheight;
00416 }
00417
00418 if ( rf->checkfield ( to, vehicle, mapDisplay )) {
00419 cancelmovement = 1;
00420 attackedByReactionFire = true;
00421 vehicle = actmap->getUnit ( networkID );
00422 }
00423
00424
00425 if ( !vehicle && mapDisplay ) {
00426 mapDisplay->displayMap();
00427 mapDisplayUpToDate = true;
00428 finalRedrawNecessary = false;
00429
00430 viewInputChanged = true;
00431 }
00432 } else
00433 if ( mapDisplay ) {
00434 mapDisplay->displayMap();
00435 mapDisplayUpToDate = true;
00436 finalRedrawNecessary = false;
00437 }
00438
00439 printTimer(7);
00440
00441
00442 if ( vehicle ) {
00443 if ( !(stop->x == to.x && stop->y == to.y && next == stop ))
00444 vehicle->removeview();
00445
00446 if ( dest->mineattacks ( vehicle )) {
00447 tmineattacksunit battle ( dest, -1, vehicle );
00448
00449 if ( mapDisplay && (fieldvisiblenow ( dest, actmap->getPlayerView()) || dest->mineowner() == actmap->getPlayerView() ))
00450 mapDisplay->showBattle( battle );
00451 else
00452 battle.calc();
00453
00454 battle.setresult ();
00455 if ( battle.dv.damage >= 100 ) {
00456 vehicle = NULL;
00457 viewInputChanged = true;
00458 }
00459
00460 updateFieldInfo();
00461 cancelmovement = 1;
00462 }
00463 }
00464 if ( cancelmovement == 1 )
00465 if ( dest->vehicle || dest->building )
00466 cancelmovement++;
00467
00468 if ( vehicle )
00469 if ( noInterrupt > 0 )
00470 cancelmovement = 0;
00471
00472 if ( vehicle ) {
00473 npush ( dest->vehicle );
00474 dest->vehicle = vehicle;
00475 if ( dest->connection & cconnection_areaentered_anyunit )
00476 fieldCrossed();
00477
00478 if ((dest->connection & cconnection_areaentered_specificunit ) && ( vehicle->connection & cconnection_areaentered_specificunit ))
00479 fieldCrossed();
00480
00481 npop ( dest->vehicle );
00482 }
00483 printTimer(8);
00484 } while ( (to.x != next->x || to.y != next->y) && vehicle );
00485
00486 pos = next;
00487 }
00488
00489 tfield* fld = getfield ( pos->x, pos->y );
00490
00491 actmap->time.set ( actmap->time.turn(), actmap->time.move()+1);
00492
00493 int result = 0;
00494
00495 if ( vehicle ) {
00496
00497 int newMovement = orgMovement - pos->dist;
00498
00499 vehicle->setnewposition ( pos->x, pos->y );
00500
00501 if ( vehicle->typ->movement[log2(orgHeight)] ) {
00502 int nm = int(floor(vehicle->maxMovement() * float(newMovement) / float(vehicle->typ->movement[log2(orgHeight)]) + 0.5));
00503
00504
00505 vehicle->setMovement ( nm );
00506
00507
00508 if ( vehicle->getMovement() < 10 )
00509 finalRedrawNecessary = true;
00510 }
00511
00512
00513 vehicle->getResource ( fueldist * vehicle->typ->fuelConsumption / maxmalq, Resources::Fuel, false );
00514
00515 if ( fld->vehicle || fld->building ) {
00516 vehicle->setMovement ( 0 );
00517 vehicle->attacked = true;
00518 }
00519
00520 if ( vehicle ) {
00521 if ((fld->vehicle == NULL) && (fld->building == NULL)) {
00522 if ( !vehicle->isViewing() ) {
00523 vehicle->addview();
00524 viewInputChanged = true;
00525
00526
00527 if ( rf->checkfield ( *pos, vehicle, mapDisplay )) {
00528 attackedByReactionFire = true;
00529 vehicle = actmap->getUnit ( networkID );
00530 }
00531 fld->vehicle = vehicle;
00532
00533 } else {
00534 fld->vehicle = vehicle;
00535 for ( int i = 0; i < actmap->getPlayerCount(); ++i )
00536 evaluatevisibilityfield ( actmap, fld, i, -1, actmap->getgameparameter ( cgp_initialMapVisibility ) );
00537 }
00538
00539 } else {
00540 ContainerBase* cn = fld->getContainer();
00541 if ( vehicle->isViewing() ) {
00542 vehicle->removeview();
00543 viewInputChanged = true;
00544 }
00545 cn->addToCargo( vehicle );
00546 if (cn->getOwner() != vehicle->getOwner() && fld->building && actmap->getPlayer(fld->building).diplomacy.isHostile( vehicle) ) {
00547 fld->building->convert( vehicle->color / 8 );
00548 if ( fieldvisiblenow ( fld, actmap->getPlayerView() ) || actmap->getPlayerView() == vehicle->getOwner() )
00549 SoundList::getInstance().playSound ( SoundList::conquer_building, 0 );
00550 }
00551 mapDisplayUpToDate = false;
00552
00553 }
00554
00555 }
00556 }
00557
00558 if ( rf->finalCheck( mapDisplay, operatingPlayer ))
00559 finalRedrawNecessary = true;
00560
00561
00562
00563 if ( viewInputChanged ) {
00564 int fieldschanged;
00565 if ( actmap->getPlayerView() >= 0 )
00566 fieldschanged = evaluateviewcalculation ( actmap, 1 << actmap->getPlayerView() );
00567 else
00568 fieldschanged = evaluateviewcalculation ( actmap, 0 );
00569
00570 if ( fieldschanged )
00571 mapDisplayUpToDate = false;
00572
00573 }
00574
00575 if ( mapDisplay ) {
00576 mapDisplay->resetMovement();
00577
00578 if (finalRedrawNecessary || !mapDisplayUpToDate)
00579 mapDisplay->displayMap();
00580
00581
00582 }
00583 return result;
00584 }
00585
00586
00587
00588 int BaseVehicleMovement :: execute ( Vehicle* veh, int x, int y, int step, int height, int noInterrupt )
00589 {
00590 if ( step != status )
00591 return -1;
00592
00593 if ( status == 0 ) {
00594 vehicle = veh ;
00595 if ( !vehicle ) {
00596 status = -101;
00597 return status;
00598 }
00599
00600 MapCoordinate3D dest;
00601 dest.setnum ( x, y, height );
00602
00603 AStar3D ast ( actmap, vehicle, false, maxint );
00604 ast.findPath ( path, dest );
00605
00606 status = 3;
00607 return status;
00608 } else
00609 if ( status == 3 ) {
00610 if ( path.empty() || path.rbegin()->x != x || path.rbegin()->y != y ) {
00611 status = -105;
00612 return status;
00613 }
00614
00615 int nwid = vehicle->networkid;
00616 int xp = vehicle->xpos;
00617 int yp = vehicle->ypos;
00618
00619 if ( mapDisplay )
00620 mapDisplay->startAction();
00621 int stat = moveunitxy( path, noInterrupt );
00622 if ( mapDisplay )
00623 mapDisplay->stopAction();
00624
00625
00626 int destDamage;
00627 if ( actmap->getUnit( nwid ))
00628 destDamage = vehicle->damage;
00629 else
00630 destDamage = 100;
00631
00632 logtoreplayinfo ( rpl_move5, xp, yp, x, y, nwid, path.rbegin()->getNumericalHeight(), noInterrupt, destDamage );
00633
00634 if ( stat < 0 )
00635 status = stat;
00636 else
00637 status = 1000;
00638
00639 return status;
00640
00641 } else
00642 status = 0;
00643 return status;
00644 }
00645
00646
00647 int BaseVehicleMovement :: available ( Vehicle* veh ) const
00648 {
00649 if ( status == 0 )
00650 if ( veh )
00651 return veh->canMove();
00652 return 0;
00653 }
00654
00655
00656
00657
00658
00659 VehicleAction :: VehicleAction ( VehicleActionType _actionType, PPendingVehicleActions _pva )
00660 {
00661 if ( _pva ) {
00662 if ( _pva->action )
00663 displaymessage ( " VehicleAction :: VehicleAction type %d / another action (type %d) is running !", 2 , _actionType, _pva->actionType );
00664
00665 _pva->action = this;
00666 _pva->actionType = _actionType;
00667 }
00668 pva = _pva;
00669 actionType = _actionType;
00670 }
00671
00672 void VehicleAction :: registerPVA ( VehicleActionType _actionType, PPendingVehicleActions _pva )
00673 {
00674 if ( _pva ) {
00675 if ( _pva->action )
00676 displaymessage ( " VehicleAction :: VehicleAction type %d / another action (type %d) is running !", 2 , _actionType, _pva->actionType );
00677
00678 _pva->action = this;
00679 _pva->actionType = _actionType;
00680 }
00681 pva = _pva;
00682 actionType = _actionType;
00683 }
00684
00685
00686 VehicleAction :: ~VehicleAction ( )
00687 {
00688 if ( pva ) {
00689 pva->action = NULL;
00690 pva->actionType = 0;
00691 }
00692 }
00693
00694
00695
00696 VehicleMovement :: VehicleMovement ( MapDisplayInterface* md, PPendingVehicleActions _pva )
00697 : BaseVehicleMovement ( vat_move, _pva, md )
00698 {
00699 if ( pva )
00700 pva->move = this;
00701 }
00702
00703 void VehicleMovement :: registerPVA ( VehicleActionType _actionType, PPendingVehicleActions _pva )
00704 {
00705 VehicleAction::registerPVA ( _actionType, _pva );
00706 if ( pva )
00707 pva->move = this;
00708 }
00709
00710
00711 VehicleMovement :: ~VehicleMovement ( )
00712 {
00713 if ( pva )
00714 pva->move = NULL;
00715 }
00716
00717 bool VehicleMovement :: avail ( Vehicle* veh )
00718 {
00719 if ( veh )
00720 return veh->canMove();
00721 return false;
00722 }
00723
00724
00725 class HeightChangeLimitation: public AStar3D::OperationLimiter {
00726 bool allow_Height_Change;
00727 public:
00728 HeightChangeLimitation ( bool allow_Height_Change_ ) : allow_Height_Change ( allow_Height_Change_ ) {};
00729 virtual bool allowHeightChange() { return allow_Height_Change; };
00730 virtual bool allowMovement() { return true; };
00731 virtual bool allowEnteringContainer() { return true; };
00732 virtual bool allowLeavingContainer() { return true; };
00733 virtual bool allowDocking() { return true; };
00734 };
00735
00736
00737 int VehicleMovement :: execute ( Vehicle* veh, int x, int y, int step, int height, int capabilities )
00738 {
00739 if ( step != status )
00740 return -1;
00741
00742 if ( status == 0 ) {
00743 vehicle = veh ;
00744 if ( !vehicle ) {
00745 status = -101;
00746 return status;
00747 }
00748
00749 int h;
00750 if ( actmap->getField(veh->getPosition())->unitHere(veh) )
00751 h = log2(veh->height);
00752 else {
00753 h = -1;
00754
00755
00756 int mx = -1;
00757 int height = veh->height;
00758 for ( int h = 0; h < 8; h++ )
00759 if ( veh->typ->height & ( 1 << h))
00760 if ( veh->typ->movement[h] > mx ) {
00761 mx = veh->typ->movement[h];
00762 height = 1 << h;
00763 }
00764 veh->setNewHeight( height );
00765 }
00766 if ( height == -2 )
00767 h = -1;
00768
00769 bool heightChange = true;
00770 if ( capabilities & DisableHeightChange )
00771 heightChange = false;
00772 HeightChangeLimitation hcl ( heightChange );
00773
00774 PathFinder pf ( actmap, vehicle, veh->getMovement() );
00775 pf.registerOperationLimiter ( &hcl );
00776 pf.getMovementFields ( reachableFields, reachableFieldsIndirect, h );
00777
00778 if ( reachableFields.getFieldNum() == 0 ) {
00779 status = -107;
00780 return status;
00781 }
00782
00783 status = 2;
00784 return status;
00785 } else
00786 if ( status == 2 ) {
00787 if ( !reachableFields.isMember ( x, y )) {
00788 status = -105;
00789 return status;
00790 }
00791
00792 MapCoordinate3D dest;
00793 if ( height >= 0 )
00794 dest.setnum ( x, y, height );
00795 else
00796 dest.setnum ( x, y, reachableFields.getData(x,y) );
00797
00798 AStar3D ast ( actmap, vehicle, false, maxint );
00799 ast.findPath ( path, dest );
00800
00801 status = 3;
00802 return status;
00803 } else
00804 if ( status == 3 ) {
00805 int res = BaseVehicleMovement::execute ( veh, x, y, step, height, capabilities & NoInterrupt );
00806 mapChanged( actmap );
00807 return res;
00808 }
00809 else
00810 status = 0;
00811
00812 return status;
00813 }
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823 ChangeVehicleHeight :: ChangeVehicleHeight ( MapDisplayInterface* md, PPendingVehicleActions _pva, VehicleActionType vat, int _dir )
00824 : BaseVehicleMovement ( vat, _pva, md )
00825 {
00826 dir = _dir;
00827 }
00828
00829 class MovementLimitation: public AStar3D::OperationLimiter {
00830 bool simpleMode;
00831 int hcNum;
00832 public:
00833 MovementLimitation ( bool simpleMode_ ) : simpleMode ( simpleMode_ ), hcNum(0) {};
00834 virtual bool allowHeightChange() { ++hcNum; if ( simpleMode) return hcNum <= 1 ; else return true; };
00835 virtual bool allowMovement() { return !simpleMode; };
00836 virtual bool allowEnteringContainer() { return true; };
00837 virtual bool allowLeavingContainer() { return true; };
00838 virtual bool allowDocking() { return true; };
00839 };
00840
00841
00842 int ChangeVehicleHeight :: execute ( Vehicle* veh, int x, int y, int step, int noInterrupt, int disableMovement )
00843 {
00844 if ( step != status )
00845 return -1;
00846
00847 if ( status == 0 ) {
00848 vehicle = veh ;
00849 if ( !vehicle ) {
00850 status = -101;
00851 return status;
00852 }
00853
00854 if ( !actmap->getField(veh->getPosition())->unitHere(veh) )
00855 return -118;
00856
00857 const Vehicletype::HeightChangeMethod* hcm = veh->getHeightChange( dir );
00858 if ( !hcm )
00859 fatalError ( "Inconsistend call to changeheight ");
00860
00861 newheight = veh->getPosition().getNumericalHeight() + hcm->heightDelta;
00862
00863
00864
00865 PathFinder pf ( actmap, vehicle, vehicle->getMovement() );
00866 MovementLimitation ml ( disableMovement );
00867 pf.registerOperationLimiter ( &ml );
00868
00869 IntFieldList reachableFieldsIndirect;
00870 pf.getMovementFields ( reachableFields, reachableFieldsIndirect, newheight );
00871
00872 if ( reachableFields.getFieldNum() == 0 ) {
00873 status = -107;
00874 return status;
00875 }
00876
00877 status = 2;
00878 return status;
00879
00880 } else
00881 if ( status == 2 ) {
00882 if ( !reachableFields.isMember ( x, y )) {
00883 status = -105;
00884 return status;
00885 }
00886
00887 MapCoordinate3D dest;
00888 dest.setnum ( x, y, reachableFields.getData(x,y) );
00889
00890 AStar3D ast ( actmap, vehicle, false, maxint );
00891 ast.findPath ( path, dest );
00892
00893 status = 3;
00894 return status;
00895 } else
00896 if ( status == 3 )
00897 return BaseVehicleMovement::execute ( veh, x, y, step, newheight, noInterrupt );
00898 else
00899 status = 0;
00900 return status;
00901 }
00902
00903
00904 IncreaseVehicleHeight :: IncreaseVehicleHeight ( MapDisplayInterface* md, PPendingVehicleActions _pva )
00905 : ChangeVehicleHeight ( md, _pva, vat_ascent, +1 )
00906 {
00907 if ( pva )
00908 pva->ascent = this;
00909 }
00910
00911 bool IncreaseVehicleHeight :: avail ( Vehicle* veh )
00912 {
00913 if ( veh )
00914 if ( veh->getHeightChange( +1 ))
00915 return true;
00916 return false;
00917 }
00918
00919 int IncreaseVehicleHeight :: available ( Vehicle* veh ) const
00920 {
00921 return avail( veh );
00922 }
00923
00924
00925 IncreaseVehicleHeight :: ~IncreaseVehicleHeight ( )
00926 {
00927 if ( pva )
00928 pva->ascent = NULL;
00929 }
00930
00931
00932
00933 DecreaseVehicleHeight :: DecreaseVehicleHeight ( MapDisplayInterface* md, PPendingVehicleActions _pva )
00934 : ChangeVehicleHeight ( md, _pva, vat_descent, -1 )
00935 {
00936 if ( pva )
00937 pva->descent = this;
00938 }
00939
00940 bool DecreaseVehicleHeight :: avail ( Vehicle* veh )
00941 {
00942 if ( veh )
00943 if ( veh->getHeightChange( -1 ))
00944 return true;
00945 return false;
00946 }
00947
00948 int DecreaseVehicleHeight :: available ( Vehicle* veh ) const
00949 {
00950 return avail(veh);
00951 }
00952
00953
00954 DecreaseVehicleHeight :: ~DecreaseVehicleHeight ( )
00955 {
00956 if ( pva )
00957 pva->descent = NULL;
00958 }
00959
00960
00961
00962 PendingVehicleActions :: PendingVehicleActions ( void )
00963 {
00964 move = NULL;
00965 action = NULL;
00966 }
00967
00968 PendingVehicleActions :: ~PendingVehicleActions ( )
00969 {
00970 if ( action )
00971 delete action;
00972 }
00973
00974
00975 void PendingVehicleActions::reset()
00976 {
00977 delete action;
00978 action = NULL;
00979 move = NULL;
00980 ascent = NULL;
00981 descent = NULL;
00982 attack = NULL;
00983 service=NULL;
00984 newservice=NULL;
00985 actionType = 0 ;
00986 }
00987
00988
00989
00990
00991
00992
00993 VehicleAttack :: VehicleAttack ( MapDisplayInterface* md, PPendingVehicleActions _pva )
00994 : VehicleAction ( vat_attack, _pva ), search ( actmap )
00995 {
00996 status = 0;
00997 mapDisplay = md;
00998 if ( pva )
00999 pva->attack = this;
01000 }
01001
01002
01003 bool VehicleAttack :: avail ( Vehicle* eht )
01004 {
01005 if ( eht )
01006 if ( eht->attacked == false )
01007 if ( eht->weapexist() )
01008 if (eht->typ->wait == false || !eht->hasMoved() )
01009 return true;
01010 return false;
01011 }
01012
01013
01014 int VehicleAttack :: execute ( Vehicle* veh, int x, int y, int step, int _kamikaze, int weapnum )
01015 {
01016 if ( step != status )
01017 return -1;
01018
01019 if ( status == 0 ) {
01020 vehicle = veh ;
01021 if ( !vehicle ) {
01022 status = -101;
01023 return status;
01024 }
01025
01026 int weaponCount = 0;
01027 int shootableWeaponCount = 0;
01028 for ( int w = 0; w < vehicle->typ->weapons.count; w++ )
01029 if ( vehicle->typ->weapons.weapon[w].shootable() ) {
01030 weaponCount++;
01031 if ( vehicle->typ->weapons.weapon[w].sourceheight & vehicle->height )
01032 shootableWeaponCount++;
01033 }
01034
01035 if ( weaponCount == 0 )
01036 return -214;
01037
01038 if ( shootableWeaponCount == 0 )
01039 return -213;
01040
01041
01042 kamikaze = _kamikaze;
01043
01044 search.init( veh, kamikaze, this );
01045 int res = search.run();
01046 if ( res < 0 ) {
01047 status = res;
01048 return status;
01049 }
01050
01051 if ( search.anzahlgegner <= 0 ) {
01052 status = -206;
01053 return status;
01054 }
01055
01056 status = 2;
01057 return status;
01058 } else
01059 if ( status == 2 ) {
01060 pattackweap atw = NULL;
01061 if ( attackableVehicles.isMember ( x, y ))
01062 atw = &attackableVehicles.getData ( x, y );
01063 else
01064 if ( attackableObjects.isMember ( x, y ))
01065 atw = &attackableObjects.getData ( x, y );
01066 else
01067 if ( attackableBuildings.isMember ( x, y ))
01068 atw = &attackableBuildings.getData ( x, y );
01069
01070 if ( !atw ) {
01071 status = -1;
01072 return status;
01073 }
01074 tfight* battle = NULL;
01075 switch ( atw->target ) {
01076 case AttackWeap::vehicle: battle = new tunitattacksunit ( vehicle, getfield(x,y)->vehicle, 1, weapnum );
01077 break;
01078 case AttackWeap::building: battle = new tunitattacksbuilding ( vehicle, x, y , weapnum );
01079 break;
01080 case AttackWeap::object: battle = new tunitattacksobject ( vehicle, x, y, weapnum );
01081 break;
01082 default : status = -1;
01083 return status;
01084 }
01085
01086 int ad1 = battle->av.damage;
01087 int dd1 = battle->dv.damage;
01088
01089 int xp1 = vehicle->xpos;
01090 int yp1 = vehicle->ypos;
01091
01092 int shown;
01093 if ( mapDisplay && fieldvisiblenow ( getfield ( x, y ), actmap->getPlayerView()) ) {
01094 mapDisplay->displayActionCursor ( vehicle->xpos, vehicle->ypos, x, y );
01095 mapDisplay->showBattle( *battle );
01096 mapDisplay->removeActionCursor ( );
01097 shown = 1;
01098 } else {
01099 battle->calc();
01100 shown = 0;
01101 }
01102
01103 int ad2 = battle->av.damage;
01104 int dd2 = battle->dv.damage;
01105
01106 if ( !vehicle->typ->hasFunction( ContainerBaseType::MoveAfterAttack ))
01107 vehicle->setMovement ( 0 );
01108
01109 battle->setresult ();
01110
01111 logtoreplayinfo ( rpl_attack, xp1, yp1, x, y, ad1, ad2, dd1, dd2, weapnum );
01112
01113 evaluateviewcalculation( actmap );
01114
01115 if ( mapDisplay && shown )
01116 mapDisplay->displayMap();
01117
01118
01119 status = 1000;
01120 }
01121 return status;
01122 }
01123
01124
01125
01126 void VehicleAttack :: tsearchattackablevehicles :: init( const Vehicle* eht, int _kamikaze, VehicleAttack* _va )
01127 {
01128 angreifer = eht;
01129 kamikaze = _kamikaze;
01130 va = _va;
01131 }
01132
01133
01134
01135 void VehicleAttack :: tsearchattackablevehicles::testfield( const MapCoordinate& mc )
01136 {
01137 if ( fieldvisiblenow( gamemap->getField(mc)) ) {
01138 if ( !kamikaze ) {
01139 pattackweap atw = attackpossible( angreifer, mc.x, mc.y );
01140 if (atw->count > 0) {
01141 switch ( atw->target ) {
01142 case AttackWeap::vehicle: va->attackableVehicles.addField ( mc, *atw );
01143 break;
01144 case AttackWeap::building: va->attackableBuildings.addField ( mc, *atw );
01145 break;
01146 case AttackWeap::object: va->attackableObjects.addField ( mc, *atw );
01147 break;
01148 default:;
01149 }
01150 anzahlgegner++;
01151 }
01152 delete atw;
01153 } else {
01154 tfield* fld = gamemap->getField(mc);
01155 if (fieldvisiblenow( fld )) {
01156 Vehicle* eht = fld->vehicle;
01157 if (eht != NULL)
01158 if (((angreifer->height >= chtieffliegend) && (eht->height <= angreifer->height) && (eht->height >= chschwimmend))
01159 || ((angreifer->height == chfahrend) && (eht->height == chfahrend))
01160 || ((angreifer->height == chschwimmend) && (eht->height == chschwimmend))
01161 || ((angreifer->height == chgetaucht) && (eht->height >= chgetaucht) && (eht->height <= chschwimmend))) {
01162 fld->a.temp = dist;
01163 anzahlgegner++;
01164 }
01165 }
01166 }
01167 }
01168 }
01169
01170
01171 int VehicleAttack :: tsearchattackablevehicles::run( void )
01172 {
01173 anzahlgegner = 0;
01174
01175 if (angreifer == NULL)
01176 return -201;
01177
01178 moveparams.movesx = angreifer->xpos;
01179 moveparams.movesy = angreifer->ypos;
01180 if (fieldvisiblenow(getfield(angreifer->xpos,angreifer->ypos)) == false)
01181 return -1;
01182
01183 if (angreifer->attacked)
01184 return -202;
01185
01186
01187 if (angreifer->typ->weapons.count == 0)
01188 return -204;
01189
01190 if ( angreifer->typ->wait && angreifer->hasMoved() && angreifer->reactionfire.getStatus() != Vehicle::ReactionFire::ready )
01191 return -215;
01192
01193
01194 int d = 0;
01195 int maxdist = 0;
01196 int mindist = 20000;
01197 for ( int a = 0; a < angreifer->typ->weapons.count; a++)
01198 if (angreifer->ammo[a] > 0) {
01199 d++;
01200 maxdist = max( maxdist, angreifer->typ->weapons.weapon[a].maxdistance / maxmalq );
01201 mindist = min ( mindist, (angreifer->typ->weapons.weapon[a].mindistance + maxmalq - 1) / maxmalq);
01202 }
01203
01204
01205 if (d == 0)
01206 return -204;
01207
01208 initsearch( angreifer->getPosition(), maxdist, mindist );
01209 startsearch();
01210
01211 return 0;
01212 }
01213
01214 void VehicleAttack :: registerPVA ( VehicleActionType _actionType, PPendingVehicleActions _pva )
01215 {
01216 VehicleAction::registerPVA ( _actionType, _pva );
01217 if ( pva )
01218 pva->attack = this;
01219 }
01220
01221
01222 VehicleAttack :: ~VehicleAttack ( )
01223 {
01224 if ( pva )
01225 pva->attack = NULL;
01226 }
01227
01228
01229
01230
01232
01233 VehicleService :: VehicleService ( MapDisplayInterface* md, PPendingVehicleActions _pva )
01234 : VehicleAction ( vat_service, _pva ), fieldSearch ( *this, actmap )
01235 {
01236 building = NULL;
01237 vehicle = NULL;
01238 status = 0;
01239 mapDisplay = md;
01240 guimode = 0;
01241 if ( pva )
01242 pva->service = this;
01243 }
01244
01245
01246 int VehicleService :: avail ( const Vehicle* veh )
01247 {
01248 int av = 0;
01249 if ( veh && !veh->attacked ) {
01250 if ( veh->canRepair( NULL ) && (veh->typ->hasFunction( ContainerBaseType::ExternalRepair )))
01251 for ( int i = 0; i < veh->typ->weapons.count; i++ )
01252 if ( veh->typ->weapons.weapon[i].service() )
01253 av++;
01254
01255
01256 const Vehicletype* fzt = veh->typ;
01257 for ( int i = 0; i < fzt->weapons.count; i++ ) {
01258 if ( fzt->weapons.weapon[i].service() ) {
01259
01260 if ( veh->typ->hasFunction( ContainerBaseType::ExternalEnergyTransfer ) )
01261 if ( veh->getStorageCapacity().energy )
01262 av++;
01263
01264 if ( veh->typ->hasFunction( ContainerBaseType::ExternalMaterialTransfer ) )
01265 if ( veh->getStorageCapacity().material )
01266 av++;
01267
01268 if ( veh->typ->hasFunction( ContainerBaseType::ExternalFuelTransfer ) )
01269 if ( veh->getStorageCapacity().fuel )
01270 av++;
01271
01272 }
01273 if ( fzt->weapons.weapon[i].canRefuel() )
01274 av++;
01275 }
01276 }
01277 if ( veh->reactionfire.getStatus() == Vehicle::ReactionFire::off )
01278 return av;
01279 else
01280 return 0;
01281 }
01282
01283 int VehicleService :: available ( Vehicle* veh ) const
01284 {
01285 return avail(veh);
01286 }
01287
01288 int VehicleService :: getServices ( Vehicle* veh )
01289 {
01290 int res = 0;
01291 if ( veh ) {
01292 if ( veh->canRepair( NULL ) && (veh->typ->hasFunction( ContainerBaseType::ExternalRepair )))
01293 for ( int i = 0; i < veh->typ->weapons.count; i++ )
01294 if ( veh->typ->weapons.weapon[i].service() )
01295 if ( !veh->attacked )
01296 res |= 1 << srv_repair;
01297
01298
01299 const Vehicletype* fzt = veh->typ;
01300 for ( int i = 0; i < fzt->weapons.count; i++ ) {
01301 if ( fzt->weapons.weapon[i].service() ) {
01302 if ( veh->typ->hasFunction( ContainerBaseType::ExternalEnergyTransfer ) )
01303 if ( veh->getStorageCapacity().energy )
01304 res |= 1 << srv_resource;
01305 if ( veh->typ->hasFunction( ContainerBaseType::ExternalMaterialTransfer ) )
01306 if ( veh->getStorageCapacity().material )
01307 res |= 1 << srv_resource;
01308 if ( veh->typ->hasFunction( ContainerBaseType::ExternalFuelTransfer ) )
01309 if ( veh->getStorageCapacity().fuel )
01310 res |= 1 << srv_resource;
01311 }
01312
01313
01314 if ( fzt->weapons.weapon[i].canRefuel() )
01315 res |= 1 << srv_ammo;
01316 }
01317 }
01318 return res;
01319 }
01320
01321
01322
01323
01324 void VehicleService :: FieldSearch :: checkVehicle2Vehicle ( Vehicle* targetUnit, int xp, int yp )
01325 {
01326 VehicleService::Target targ;
01327 targ.dest = targetUnit;
01328
01329 if ( xp == startPos.x && yp == startPos.y )
01330 return;
01331
01332 int dist;
01333 if ( bypassChecks.distance )
01334 dist = maxmalq;
01335 else
01336 dist = beeline ( xp, yp , startPos.x, startPos.y );
01337
01338
01339 const SingleWeapon* serviceWeapon = NULL;
01340 for (int i = 0; i < veh->typ->weapons.count ; i++)
01341 if ( veh->typ->weapons.weapon[i].service() )
01342 serviceWeapon = &veh->typ->weapons.weapon[i];
01343
01344
01345 if ( serviceWeapon ) {
01346 int targheight = 0;
01347
01348 for ( int h = 0; h < 8; h++ )
01349 if ( serviceWeapon->targ & ( 1 << h ))
01350 if ( serviceWeapon->efficiency[ 6 + getheightdelta ( log2(veh->height), h ) ] )
01351 targheight |= 1 << h;
01352
01353 if ( (serviceWeapon->sourceheight & veh->height) || ( bypassChecks.height && (serviceWeapon->sourceheight & veh->typ->height)))
01354 for (int i = 0; i < veh->typ->weapons.count ; i++) {
01355 const SingleWeapon& sourceWeapon = veh->typ->weapons.weapon[i];
01356 if ( sourceWeapon.service() || sourceWeapon.canRefuel() ) {
01357 if ( targetUnit && serviceWeapon )
01358 if ( !targetUnit->typ->hasFunction(ContainerBaseType::NoInairRefuelling) || targetUnit->height <= chfahrend )
01359 if ( serviceWeapon->targetingAccuracy[targetUnit->typ->movemalustyp] > 0 )
01360 if ( actmap->player[veh->getOwner()].diplomacy.getState( targetUnit->getOwner()) >= PEACE )
01361 if ( (serviceWeapon->maxdistance >= dist && serviceWeapon->mindistance <= dist) || bypassChecks.distance )
01362 if ( targetUnit->height & targheight || ( bypassChecks.height && ( targetUnit->typ->height & targheight) )) {
01363 if ( sourceWeapon.canRefuel() ) {
01364 for ( int j = 0; j < targetUnit->typ->weapons.count ; j++) {
01365 const SingleWeapon& targetWeapon = targetUnit->typ->weapons.weapon[j];
01366 if ( targetWeapon.getScalarWeaponType() == sourceWeapon.getScalarWeaponType()
01367 && targetWeapon.requiresAmmo() ) {
01368 VehicleService::Target::Service s;
01369 s.type = VehicleService::srv_ammo;
01370 s.sourcePos = i;
01371 s.targetPos = j;
01372 s.curAmount = targetUnit->ammo[j];
01373 s.orgSourceAmount = veh->ammo[i];
01374 s.maxAmount = min ( targetWeapon.count, s.curAmount+s.orgSourceAmount );
01375 int sourceSpace = sourceWeapon.count - veh->ammo[i];
01376 s.minAmount = max ( s.curAmount - sourceSpace, 0 );
01377 if ( targetWeapon.count )
01378 s.maxPercentage = 100 * s.maxAmount/ targetWeapon.count;
01379 else
01380 s.maxPercentage = 0;
01381 targ.service.push_back ( s );
01382 }
01383 }
01384 }
01385
01386 if ( sourceWeapon.service() ) {
01387 static ContainerBaseType::ContainerFunctions resourceVehicleFunctions[resourceTypeNum] = { ContainerBaseType::ExternalEnergyTransfer,
01388 ContainerBaseType::ExternalMaterialTransfer,
01389 ContainerBaseType::ExternalFuelTransfer };
01390 for ( int r = 0; r < resourceTypeNum; r++ )
01391 if ( veh->getStorageCapacity().resource(r) && targetUnit->getStorageCapacity().resource(r) && veh->typ->hasFunction(resourceVehicleFunctions[r])) {
01392 VehicleService::Target::Service s;
01393 s.type = VehicleService::srv_resource;
01394 s.sourcePos = r;
01395 s.targetPos = r;
01396 s.curAmount = targetUnit->getTank().resource(r);
01397 s.orgSourceAmount = veh->getTank().resource(r);
01398 s.maxAmount = s.curAmount + min ( targetUnit->putResource(maxint, r, 1) , s.orgSourceAmount );
01399 int sourceSpace = veh->putResource(maxint, r, 1);
01400 s.minAmount = max ( s.curAmount - sourceSpace, 0 );
01401 s.maxPercentage = 100 * s.maxAmount/ veh->getStorageCapacity().resource(r);
01402 targ.service.push_back ( s );
01403 }
01404
01405 if ( veh->canRepair( targetUnit ) && (veh->typ->hasFunction( ContainerBaseType::ExternalRepair )))
01406 if ( veh->getTank().fuel && veh->getTank().material )
01407