Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

unitctrl.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-2005  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 <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) );  //this field does not exist...
00057 
00058    int unitHeight = veh->getPosition().getNumericalHeight();
00059    if ( !actmap->getField ( veh->getPosition())->unitHere ( veh ))
00060       unitHeight = -1;
00061 
00062    // there are different entries for the same x/y coordinate but different height.
00063    // Since the UI is only in xy, we need to find the height which is the easiest to reach
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          // if ( h == -1 )
00072          //   h = i->enterHeight;
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) // the unit will reach its destination
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                // here comes an ugly hack to get the shadow of starting / descending aircraft right
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 //         if ( nm < 0 )
00504 //            result = -1;
00505          vehicle->setMovement ( nm );
00506          
00507          // the unit will be shaded if movement is exhausted
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                // do we really need this check?
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    // finalRedrawNecessary = true;
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       // if ( fieldschanged > 0 )
00578       if (finalRedrawNecessary || !mapDisplayUpToDate)
00579          mapDisplay->displayMap();
00580       // else
00581       //   mapDisplay->displayPosition ( pos->x, pos->y );
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); // != height-change: true
00752       else {
00753          h = -1; // height-change = false
00754 
00755          // resetting unit movement
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; //  = h != -1;
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       } /* endswitch */
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             } /* endswitch */
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;  // this is currently still needed for wepselguihost
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