vehicle.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           vehicle.cpp  -  description
00003                              -------------------
00004     begin                : Fri Sep 29 2000
00005     copyright            : (C) 2000 by Martin Bickel
00006     email                : bickel@asc-hq.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include <algorithm>
00019 #include <math.h>
00020 #include "research.h"
00021 #include "vehicletype.h"
00022 #include "vehicle.h"
00023 #include "buildingtype.h"
00024 #include "viewcalculation.h"
00025 #include "errors.h"
00026 #include "graphicset.h"
00027 #include "errors.h"
00028 #include "gameoptions.h"
00029 #include "spfst.h"
00030 #include "itemrepository.h"
00031 #include "graphics/blitter.h"
00032 
00033 #include "actions/context.h"
00034 #include "actions/changeunitmovement.h"
00035 #include "actions/changeunitproperty.h"
00036 #include "actions/spawnobject.h"
00037 #include "actions/unitfieldregistration.h"
00038 
00039 const float repairEfficiencyVehicle[resourceTypeNum*resourceTypeNum] = { 0,  0,  0,
00040                                                                          0,  0.5, 0,
00041                                                                          0.5, 0,  1 };
00042 
00043 
00044 #ifndef UNITVERSIONLIMIT
00045 # define UNITVERSIONLIMIT 0x7fffffff
00046 #endif
00047 
00048 
00049 
00050 Vehicle :: Vehicle (  )
00051           : ContainerBase ( NULL, NULL, 0 ), repairEfficiency ( repairEfficiencyVehicle ), typ ( NULL ), reactionfire ( this )
00052 {
00053 }
00054 
00055 Vehicle :: Vehicle ( const Vehicle& v )
00056           : ContainerBase ( NULL, NULL, 0 ), repairEfficiency ( repairEfficiencyVehicle ), typ ( NULL ), reactionfire ( this )
00057 {
00058 }
00059 
00060 
00061 Vehicle :: Vehicle ( const VehicleType* t, GameMap* actmap, int player )
00062           : ContainerBase ( t, actmap, player ), repairEfficiency ( repairEfficiencyVehicle ), typ ( t ), reactionfire ( this )
00063 {
00064    viewOnMap = false;
00065 
00066    if ( player > 8 )
00067       fatalError ( "Vehicle :: Vehicle ; invalid player ");
00068 
00069    init();
00070 
00071    gamemap->player[player].vehicleList.push_back ( this );
00072    
00073    networkid = gamemap->idManager.getNewNetworkID();
00074    gamemap->idManager.registerUnitNetworkID( this );
00075 }
00076 
00077 Vehicle :: Vehicle ( const VehicleType* t, GameMap* actmap, int player, int networkID )
00078           : ContainerBase ( t, actmap, player ), repairEfficiency ( repairEfficiencyVehicle ), typ ( t ), reactionfire ( this )
00079 {
00080    viewOnMap = false;
00081 
00082    if ( player > 8 )
00083       fatalError ( "Vehicle :: Vehicle ; invalid player ");
00084 
00085    init();
00086 
00087    gamemap->player[player].vehicleList.push_back ( this );
00088    if ( networkID == -1 ) {
00089       networkid = gamemap->idManager.getNewNetworkID();
00090    } else
00091       if ( networkID >= 0 ) {
00092          networkid = networkID;
00093       }
00094 
00095    if ( networkID >= 0 )
00096       gamemap->idManager.registerUnitNetworkID( this );
00097 }
00098 
00099 
00100 Vehicle :: ~Vehicle (  )
00101 {
00102    for ( int i = 0; i < 8; i++ ) {
00103       delete aiparam[i];
00104       aiparam[i] = NULL;
00105    }
00106       
00107 
00108    
00109    if ( viewOnMap && gamemap && gamemap->state != GameMap::Destruction ) {
00110       removeview();
00111       viewOnMap = false;
00112    }
00113 
00114    if ( gamemap ) {
00115       int c = color/8;
00116 
00117       Player::VehicleList::iterator i = find ( gamemap->player[c].vehicleList.begin(), gamemap->player[c].vehicleList.end(), this );
00118       if ( i != gamemap->player[c].vehicleList.end() )
00119          gamemap->player[c].vehicleList.erase ( i );
00120 
00121       gamemap->idManager.unregisterUnitNetworkID(this);
00122    }
00123 
00124    MapField* fld = gamemap->getField( xpos, ypos);
00125    if ( fld && fld->vehicle  == this )
00126        fld->vehicle = NULL;
00127    
00128    if ( getCarrier() )
00129       getCarrier()->removeUnitFromCargo( this, true );
00130 }
00131 
00132 
00133 
00134 
00135 void Vehicle :: init ( void )
00136 {
00137    xpos = -1;
00138    ypos = -1;
00139 
00140    for ( int i = 0; i < 16; i++ ) {
00141       weapstrength[i] = 0;
00142       ammo[i] = 0;
00143    }
00144 
00145    damage = 0;
00146 
00147    experience = 0;
00148    attacked = false;
00149 
00150    if ( typ ) {
00151       height = 1 << getFirstBit( typ->height );
00152 
00153       // These are the preferred levels of height
00154       if (typ->height & chfahrend )
00155          height = chfahrend;
00156       else
00157          if (typ->height & chschwimmend )
00158             height = chschwimmend;
00159 
00160       for ( int m = 0; m < typ->weapons.count ; m++)
00161          weapstrength[m] = typ->weapons.weapon[m].maxstrength;
00162 
00163       setMovement ( typ->movement[getFirstBit(height)], 0 );
00164    } else {
00165       height = 0;
00166 
00167       setMovement ( 0, 0 );
00168    }
00169 
00170    direction = 0;
00171    xpos = -1;
00172    ypos = -1;
00173    connection = 0;
00174    networkid = -1;
00175 
00176    if ( typ && typ->hasFunction(ContainerBaseType::MoveWithReactionFire))
00177       reactionfire.status = ReactionFire::ready;
00178    else
00179       reactionfire.status = ReactionFire::off;
00180 
00181    generatoractive = 0;
00182 
00183    cleanRemove = false;
00184 
00185    for ( int a = 0; a < 8 ; a++ )
00186       aiparam[a] = NULL;
00187 }
00188 
00189 
00190 bool Vehicle :: canRepair( const ContainerBase* item ) const
00191 {
00192    return typ->hasFunction( ContainerBaseType::InternalUnitRepair  ) ||
00193           typ->hasFunction( ContainerBaseType::ExternalRepair  ) ||
00194           (item == this && typ->autorepairrate ) ;
00195 }
00196 
00197 int Vehicle :: putResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00198 {
00199    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00200 
00201    if ( amount < 0 ) {
00202       return -getResource( -amount, resourcetype, queryonly, scope, player );
00203    } else {
00204       if ( resourcetype == 0 )  // no energy storable
00205          return 0;
00206       
00207       if ( player < 0 )
00208          player = getMap()->actplayer;
00209       
00210       if ( player != getOwner() && !(getMap()->getPlayer(getOwner()).diplomacy.getState(player) >= PEACE)  )
00211          return 0;
00212 
00213       int spaceAvail = getStorageCapacity().resource( resourcetype ) - tank.resource(resourcetype);
00214       if ( spaceAvail < 0 )
00215          spaceAvail = 0;
00216 
00217       int tostore = min ( spaceAvail, amount);
00218       if ( !queryonly ) {
00219          tank.resource(resourcetype) += tostore;
00220          // if ( tostore > 0 )
00221          //   resourceChanged();
00222       }
00223 
00224       return tostore;
00225    }
00226 }
00227 
00228 int Vehicle :: getResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00229 {
00230    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00231 
00232    if ( amount < 0 ) {
00233       return -putResource( -amount, resourcetype, queryonly, scope, player );
00234    } else {
00235       if ( resourcetype == 0 && !getGeneratorStatus() )
00236          return 0;
00237 
00238       if ( player < 0 )
00239          player = getMap()->actplayer;
00240       
00241       if ( player != getOwner() && (getMap()->getPlayer(getOwner()).diplomacy.getState(player) < PEACE) && !queryonly)
00242          return 0;
00243       
00244       
00245       int toget = min ( tank.resource(resourcetype), amount);
00246       if ( !queryonly ) {
00247          tank.resource(resourcetype) -= toget;
00248          // if ( toget > 0 )
00249          //   resourceChanged();
00250       }
00251 
00252       return toget;
00253    }
00254 }
00255 
00256 int Vehicle :: getAvailableResource ( int amount, int resourcetype, int scope ) const
00257 {
00258    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00259 
00260    if ( resourcetype == 0 && !getGeneratorStatus() )
00261       return 0;
00262 
00263    int toget = min ( tank.resource(resourcetype), amount);
00264    return toget;
00265 }
00266 
00267 
00268 Resources Vehicle::getResource ( const Resources& res ) const
00269 {
00270    Resources got;
00271    for ( int r = 0; r < 3; ++r )
00272       got.resource(r) = getAvailableResource( res.resource(r), r);
00273         
00274    return got;
00275 }
00276 
00277 
00278 Resources Vehicle::getTank() const
00279 {
00280    return tank;
00281 }
00282 
00283 
00284 
00285 void Vehicle :: setGeneratorStatus ( bool status )
00286 {
00287    if ( typ->hasFunction( ContainerBaseType::MatterConverter )) {
00288       generatoractive = status;
00289    } else
00290      generatoractive = 0;
00291 }
00292 
00293 
00294 
00295 int Vehicle::weight( void ) const
00296 {
00297    return typ->weight + cargoWeight();
00298 }
00299 
00300 int Vehicle::size ( void )
00301 {
00302    return typ->weight;
00303 }
00304 
00305 void Vehicle::transform ( const VehicleType* type )
00306 {
00307    if ( !type )
00308       return;
00309    
00310    typ = type;
00311 
00312    tank = getStorageCapacity();
00313    tank.energy = 0;
00314    
00315    generatoractive = 0;
00316 
00317    for ( int m = 0; m < typ->weapons.count ; m++) {
00318       ammo[m] = typ->weapons.weapon[m].count;
00319       weapstrength[m] = typ->weapons.weapon[m].maxstrength;
00320    }
00321 }
00322 
00323 void Vehicle :: postRepair ( int oldDamage )
00324 {
00325    for ( int i = 0; i < experienceDecreaseDamageBoundaryNum; i++)
00326       if ( oldDamage > experienceDecreaseDamageBoundaries[i] && damage < experienceDecreaseDamageBoundaries[i] )
00327          if ( experience > 0 )
00328             experience-=1;
00329 }
00330 
00331 
00332 void Vehicle :: beginTurn()
00333 {
00334    // changing unit height to the height with the maximumem movement, to reduce the inaccuracies due to rounding
00335    
00336    if ( getCarrier() ) {
00337       int mx = -1;
00338       int newHeight = height;
00339       for ( int h = 0; h < 8; h++ )
00340          if ( typ->height & ( 1 << h))
00341             if ( typ->movement[h] > mx ) {
00342                mx = typ->movement[h];
00343                newHeight = 1 << h;
00344             }
00345             
00346       if ( newHeight != height ) {
00347          height = newHeight;
00348       } 
00349    }
00350    resetMovement();
00351    
00352 }
00353 
00354 
00355 void Vehicle :: endRound ( void )
00356 {
00357    ContainerBase::endRound();
00358    if ( typ->hasFunction( ContainerBaseType::MatterConverter )) {
00359       int endiff = getStorageCapacity().energy - tank.energy;
00360       if ( tank.fuel < endiff * generatortruckefficiency )
00361          endiff = tank.fuel / generatortruckefficiency;
00362 
00363       tank.energy += endiff;
00364       tank.fuel -= endiff * generatortruckefficiency ;
00365    }
00366 }
00367 
00368 void Vehicle :: endAnyTurn()
00369 {
00370    ContainerBase::endAnyTurn();
00371    reactionfire.endAnyTurn();
00372 }
00373 
00374 
00375 void Vehicle :: endOwnTurn()
00376 {
00377    ContainerBase::endOwnTurn();
00378    
00379    if ( typ->autorepairrate > 0 )
00380       if ( damage )
00381          repairItem ( this, max ( damage - typ->autorepairrate, 0 ) );
00382 
00383    reactionfire.endOwnTurn();
00384 
00385    if ( !gamemap->getField(getPosition())->unitHere(this)) {
00386       int mx = -1;
00387       for ( int h = 0; h < 8; h++ )
00388          if ( typ->height & ( 1 << h))
00389             if ( typ->movement[h] > mx ) {
00390                mx = typ->movement[h];
00391                height = 1 << h;
00392             }
00393    }
00394 
00395    for ( int w = 0; w < typ->weapons.count; w++ )
00396       if ( typ->weapons.weapon[w].gettype() & cwlaserb ) {
00397          int cnt = 0;
00398          while ( cnt < typ->weapons.weapon[w].laserRechargeRate  && ammo[w] < typ->weapons.weapon[w].count ) {
00399             for ( int r = 0; r < 3; r++ )
00400                if ( typ->weapons.weapon[w].laserRechargeCost.resource(r) < 0 )
00401                   fatalError (" negative Laser recharge cost !");
00402             if ( ! (ContainerBase::getResource( typ->weapons.weapon[w].laserRechargeCost, 1 ) < typ->weapons.weapon[w].laserRechargeCost )) {
00403                ContainerBase::getResource( typ->weapons.weapon[w].laserRechargeCost, 0 );
00404                ammo[w] += 1;
00405             }
00406             ++cnt;
00407          }
00408       }
00409 
00410    resetMovement();
00411    attacked = false;
00412 
00413 }
00414 
00415 void Vehicle :: resetMovement ( void )
00416 {
00417     int move = typ->movement[getFirstBit(height)];
00418     setMovement ( move, 0 );
00419 }
00420 
00421 
00422 
00423 
00424 void Vehicle :: setMovement ( int newmove, double cargoDivisor )
00425 {
00426 
00427    if ( cargoDivisor < 0 )
00428       cargoDivisor = typ->cargoMovementDivisor;
00429 
00430    if ( newmove < 0 )
00431       newmove = 0;
00432 
00433    if ( cargoDivisor > 0 && typ)
00434       if ( typ->movement[ getFirstBit ( height ) ] ) {
00435          double diff = _movement - newmove;
00436          double perc = diff / typ->movement[ getFirstBit ( height ) ] ;
00437          for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00438             if ( *i ) {
00439                double lperc = perc;
00440                if ( cargoDivisor && cargoNestingDepth() == 0 )
00441                   lperc /= cargoDivisor;
00442 
00443                (*i)->decreaseMovement ( max( 1, int( ceil( lperc * double( (*i)->typ->movement[ getFirstBit ( (*i)->height)] )))));
00444             }
00445    }
00446    _movement = newmove;
00447 }
00448 
00449 bool Vehicle::hasMoved ( void ) const
00450 {
00451    return _movement != typ->movement[ getFirstBit ( height )];
00452 }
00453 
00454 
00455 int Vehicle :: getMovement ( bool checkFuel, bool checkRF ) const
00456 {
00457    if ( checkRF && !reactionfire.canMove() )
00458       return 0;
00459 
00460    if ( typ->fuelConsumption && checkFuel ) {
00461       if ( tank.fuel * minmalq / typ->fuelConsumption < _movement )
00462          return tank.fuel * minmalq / typ->fuelConsumption;
00463       else
00464          return _movement;
00465    } else
00466       return _movement;
00467 }
00468 
00469 void Vehicle :: decreaseMovement ( int amount )
00470 {
00471   int newMovement = _movement - amount;
00472   if ( newMovement < 0 )
00473     newMovement = 0;
00474   if ( newMovement > typ->movement[getFirstBit(height)] )
00475     newMovement = typ->movement[getFirstBit(height)];
00476   setMovement ( newMovement );
00477 }
00478 
00479 bool Vehicle::movementLeft() const
00480 {
00481    int mv  = getMovement();
00482    if ( mv <= 0 )
00483       return false;
00484    if ( mv >= 10 )
00485       return true;
00486 
00487    if ( height & ( chtieffliegend |chfliegend | chhochfliegend )) {
00488       WindMovement wm ( this );
00489       for ( int i = 0; i < 6; i++ )
00490          if ( 10 - wm.getDist( i ) <= mv )
00491             return true;
00492    }
00493    return false;
00494 }
00495 
00496 bool Vehicle :: canMove ( void ) const
00497 {
00498    // if ( attacked && !(typ->functions & cf_moveafterattack))
00499    //   return false;
00500 
00501    if ( movementLeft() && reactionfire.canMove() ) {
00502       MapField* fld = gamemap->getField ( getPosition() );
00503       if ( fld->unitHere ( this ) ) {
00504          if ( terrainaccessible ( fld, this ) || getMap()->getgameparameter(cgp_movefrominvalidfields))
00505             return true;
00506       } else {
00507          ContainerBase* cnt = fld->getContainer();
00508          if ( cnt )
00509             for ( Cargo::const_iterator i = cnt->getCargo().begin(); i != cnt->getCargo().end(); ++i )
00510                if ( *i == this ) 
00511                   if ( cnt->vehicleUnloadable( typ ) > 0 || cnt->vehicleDocking( this, true ) > 0 )
00512                      return true;
00513       }
00514    }
00515    return false;
00516 }
00517 
00518 bool Vehicle::spawnMoveObjects( const MapCoordinate& start, const MapCoordinate& dest, const Context& context )
00519 {
00520    if ( start == dest )
00521       return false;
00522       
00523    bool result = false;
00524    
00525    if ( typ->objectLayedByMovement.size() && (height == chfahrend || height == chschwimmend))  {
00526       int dir = getdirection( start, dest );
00527 
00528       MapField* startField = gamemap->getField(start);
00529       MapField* destField = gamemap->getField(dest);
00530 
00531       for ( int i = 0; i < typ->objectLayedByMovement.size(); i++ ) 
00532          for ( int id = typ->objectLayedByMovement[i].from; id <= typ->objectLayedByMovement[i].to; ++id ) {
00533             ObjectType* object = objectTypeRepository.getObject_byID( id );
00534             if ( object ) {
00535                (new SpawnObject( getMap(), start, id, 1 << dir ))->execute( context );
00536                if ( startField->checkForObject ( object ))
00537                   result = true;
00538             }
00539          }
00540            
00541       dir = (dir + sidenum/2) % sidenum;
00542 
00543       for ( int i = 0; i < typ->objectLayedByMovement.size(); i++ ) 
00544          for ( int id = typ->objectLayedByMovement[i].from; id <= typ->objectLayedByMovement[i].to; ++id ) {
00545             ObjectType* object = objectTypeRepository.getObject_byID( id );
00546             if ( object ) {
00547                (new SpawnObject( getMap(), dest, id, 1 << dir ))->execute( context );
00548                if ( destField->checkForObject ( object ))
00549                   result = true;
00550             }
00551          }
00552    }
00553    
00554    return result;
00555 }
00556 
00557 
00558 Vehicle::ReactionFire::ReactionFire ( Vehicle* _unit ) : unit ( _unit )
00559 {
00560    weaponShots.resize(unit->typ->weapons.count);
00561    resetShotCount();
00562 }
00563 
00564 
00565 void Vehicle::ReactionFire::checkData ( )
00566 {
00567    // the size could have changed because a unit was saved to disk with 1 weapon system, the type modified to 2 and the unit loaded again
00568    if ( weaponShots.size() < unit->typ->weapons.count ) {
00569       size_t oldsize = weaponShots.size();
00570       weaponShots.resize( unit->typ->weapons.count );
00571       for ( size_t i = oldsize; i < unit->typ->weapons.count; ++i )
00572          weaponShots[i] = unit->typ->weapons.weapon[i].reactionFireShots;
00573    }
00574 }
00575 
00576 
00577 
00578 void Vehicle::ReactionFire::resetShotCount()
00579 {
00580    assertOrThrow( unit->typ->weapons.count <= weaponShots.size() );
00581    for ( int i = 0; i < unit->typ->weapons.count; ++i ) 
00582       weaponShots[i] = unit->typ->weapons.weapon[i].reactionFireShots;
00583 }
00584 
00585 
00586 int Vehicle::ReactionFire::enable ( void )
00587 {
00588    if ( unit->typ->hasFunction( ContainerBaseType::NoReactionfire  ) )
00589       return -216;
00590       
00591    #ifdef karteneditor
00592    status = ready;
00593    #else
00594    if ( status == off ) {
00595       int weaponCount = 0;
00596       int shootableWeaponCount = 0;
00597       for ( int w = 0; w < unit->typ->weapons.count; w++ )
00598          if ( unit->typ->weapons.weapon[w].shootable() ) {
00599               weaponCount++;
00600               if ( unit->typ->weapons.weapon[w].sourceheight & unit->height )
00601                  shootableWeaponCount++;
00602          }
00603 
00604       if ( weaponCount == 0 )
00605          return -214;
00606 
00607       if ( shootableWeaponCount == 0 )
00608          return -213;
00609 
00610       if ( unit->typ->wait ) {
00611          // if ( unit->hasMoved())
00612          //    status = init1a;
00613          // else
00614             status = init2;
00615       } else {
00616          status = init2;
00617       }
00618    }
00619    #endif
00620    return 0;
00621 }
00622 
00623 void Vehicle::ReactionFire::disable ( void )
00624 {
00625    if ( status != off ) {
00626       if ( status != init1a && status != init2 && !unit->typ->hasFunction(ContainerBaseType::MoveWithReactionFire)  ) {
00627           unit->setMovement ( 0, 0 );
00628        }
00629        status = off;
00630    }
00631 }
00632 
00633 void Vehicle::ReactionFire::endAnyTurn()
00634 {
00635    resetShotCount();
00636 }
00637 
00638 
00639 void Vehicle::ReactionFire::endOwnTurn()
00640 {
00641    if ( status != off ) {
00642       if ( status == init1a )
00643          status = init1b;
00644       else
00645          if ( status == init2 || status == init1b )
00646             status = ready;
00647    }
00648    nonattackableUnits.clear();
00649 }
00650 
00651 bool Vehicle::ReactionFire::canMove() const
00652 {
00653    if ( unit->typ->hasFunction( ContainerBaseType::MoveWithReactionFire  ))
00654       return true;
00655    if ( status == off )
00656       return true;
00657    return false;
00658 }
00659 
00660 bool Vehicle::ReactionFire:: canPerformAttack( Vehicle* target )
00661 {
00662    if ( !unit->getCarrier() )
00663       if ( unit->getMap()->getPlayer(unit).diplomacy.isHostile( target->getOwner() ))
00664          if ( getStatus() >= ready )
00665             if ( find ( nonattackableUnits.begin(), nonattackableUnits.end(), target->networkid) == nonattackableUnits.end() ) 
00666                // if ( enemiesAttackable & ( 1 << target->getOwner() ))
00667                   return true;
00668    
00669    return false;
00670 }
00671 
00672 
00673 
00674 const VehicleType::HeightChangeMethod* Vehicle::getHeightChange( int dir, int height ) const
00675 {
00676    if ( !reactionfire.canMove() )
00677       return NULL;
00678 
00679    if ( height == 0 )
00680       height = this->height;
00681 
00682    for ( int i = 0; i < typ->heightChangeMethodNum; i++ )
00683       if ( typ->heightChangeMethod[i].startHeight & height )
00684          if ( ( dir > 0 && typ->heightChangeMethod[i].heightDelta > 0) || ( dir < 0 && typ->heightChangeMethod[i].heightDelta < 0))
00685             if ( (1 << (getFirstBit(height) + typ->heightChangeMethod[i].heightDelta)) & typ->height )
00686                return &typ->heightChangeMethod[i];
00687 
00688    return NULL;
00689 }
00690 
00691 
00692 
00693 bool Vehicle :: weapexist() const
00694 {
00695    if ( typ->weapons.count > 0)
00696       for ( int b = 0; b < typ->weapons.count ; b++)
00697          if ( typ->weapons.weapon[b].shootable() )
00698             if ( typ->weapons.weapon[b].offensive() )
00699                if ( ammo[b] )
00700                   return 1;
00701     return 0;
00702 }
00703 
00704 
00705 void Vehicle :: setnewposition ( int x , int y )
00706 {
00707    xpos = x;
00708    ypos = y;
00709    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00710       if ( *i ) 
00711          (*i)->setnewposition ( x , y );
00712 }
00713 
00714 void Vehicle :: setnewposition ( const MapCoordinate& mc )
00715 {
00716    setnewposition ( mc.x, mc.y );
00717 }
00718 
00719 void Vehicle :: setnewposition ( const MapCoordinate& mc, const Context& context )
00720 {
00721    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00722       if ( *i ) 
00723          (*i)->setnewposition ( mc, context );
00724    (new UnitFieldRegistration( this, MapCoordinate3D(mc,0), UnitFieldRegistration::Position ))->execute( context );
00725 }
00726 
00727 
00728 
00729 void Vehicle::convert ( int col, bool recursive )
00730 {
00731   if ( col > 8)
00732      fatalError("convertvehicle: \n invalid target player ");
00733 
00734   registerForNewOwner( col );
00735       
00736    if ( recursive )
00737       for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00738          if ( *i ) 
00739             (*i)->convert( col );
00740 }
00741 
00742 
00743 void Vehicle::registerForNewOwner( int player )
00744 {
00745    int oldcol = getOwner();
00746 
00747    Player::VehicleList::iterator i = find ( gamemap->player[oldcol].vehicleList.begin(), gamemap->player[oldcol].vehicleList.end(), this );
00748    if ( i != gamemap->player[oldcol].vehicleList.end())
00749       gamemap->player[oldcol].vehicleList.erase ( i );
00750 
00751    gamemap->player[player].vehicleList.push_back( this );
00752 
00753    color = player*8;
00754    
00755    // emit signal
00756    conquered();
00757    anyContainerConquered(this);
00758 }
00759 
00760 
00761 bool  Vehicle :: vehicleconstructable ( const VehicleType* tnk, int x, int y )
00762 {
00763    if ( gamemap->getgameparameter(cgp_produceOnlyResearchedStuffExternally) && 
00764        !tnk->techDependency.available ( gamemap->player[getOwner()].research))
00765       return 0;
00766    
00767    if ( getMovement() < maxMovement() * typ->unitConstructionMoveCostPercentage / 100 )
00768       return 0;
00769 
00770    if( (tnk->height & height ) || (( tnk->height & (chfahrend | chschwimmend)) && (height & (chfahrend | chschwimmend)))) {
00771       int hgt = height;
00772       if ( !(tnk->height & height))
00773          hgt = 1 << getFirstBit(tnk->height);
00774       if ( terrainaccessible2( gamemap->getField(x,y), tnk->terrainaccess, hgt ) > 0 )
00775          if ( getResource( getExternalVehicleConstructionCost( tnk ), true ) == getExternalVehicleConstructionCost( tnk ) )
00776             if ( beeline (x, y, xpos, ypos) <= maxmalq )
00777                return 1;
00778 
00779    }
00780    return 0;
00781 }
00782 
00783 
00784 Resources Vehicle::getExternalVehicleConstructionCost( const VehicleType* tnk ) const
00785 {
00786    Resources r;
00787    r.material = tnk->productionCost.material;
00788    r.fuel = tnk->productionCost.energy;
00789    return r;
00790 }
00791 
00792 /*
00793 bool Vehicle :: buildingconstructable ( const BuildingType* building, bool checkResources )
00794 {
00795    if ( !building )
00796       return 0;
00797 
00798 
00799    if ( gamemap->getgameparameter(cgp_forbid_building_construction) )
00800        return 0;
00801 
00802    if ( !building->techDependency.available ( gamemap->player[getOwner()].research))
00803       return 0;
00804 
00805 
00806    int mf = gamemap->getgameparameter ( cgp_building_material_factor );
00807    int ff = gamemap->getgameparameter ( cgp_building_fuel_factor );
00808 
00809    if ( !mf )
00810       mf = 100;
00811    if ( !ff )
00812       ff = 100;
00813 
00814    int hd = getheightdelta ( getFirstBit ( height ), getFirstBit ( building->height ));
00815 
00816    if ( hd != 0 ) // && !(hd ==-1 && (height == chschwimmend || height == chfahrend)) ???  what was that ??
00817       return 0;
00818 
00819 
00820    if ( (building->productionCost.material * mf / 100 <= tank.material   &&   building->productionCost.fuel * ff / 100 <= tank.fuel) || !checkResources ) {
00821       if ( typ->hasFunction( ContainerBaseType::ConstructBuildings) )
00822          for ( int i = 0; i < typ->buildingsBuildable.size(); i++ )
00823             if ( typ->buildingsBuildable[i].from <= building->id &&
00824                  typ->buildingsBuildable[i].to   >= building->id )
00825                return true;
00826    }
00827    
00828    return false;
00829 }
00830 
00831 */
00832 
00833 int Vehicle :: searchstackforfreeweight ( Vehicle* searchedInnerVehicle )
00834 {
00835    if ( searchedInnerVehicle == this ) {
00836       return typ->maxLoadableWeight - cargoWeight();
00837    } else {
00838       int currentFreeWeight = typ->maxLoadableWeight - cargoWeight();
00839       int innerFreeWeight = -1;
00840       for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00841          if ( *i ) {
00842             int w = (*i)->searchstackforfreeweight ( searchedInnerVehicle );
00843             if ( w >= 0 )
00844                innerFreeWeight = w;
00845          }
00846 
00847       if ( innerFreeWeight != -1 )
00848          return min ( currentFreeWeight, innerFreeWeight );
00849       else
00850          return -1;
00851    }
00852 }
00853 
00854 
00855 int Vehicle :: freeWeight ()
00856 {
00857    MapField* fld = gamemap->getField ( xpos, ypos );
00858    if ( fld->vehicle )
00859       return fld->vehicle->searchstackforfreeweight ( this );
00860    else
00861       if ( fld->building ) {
00862          for ( Cargo::const_iterator i = fld->building->getCargo().begin(); i != fld->building->getCargo().end(); ++i )
00863             if ( *i ) {
00864                int w3 = (*i)->searchstackforfreeweight ( this );
00865                if ( w3 >= 0 )
00866                   return w3;
00867             }
00868       }
00869 
00870    return -2;
00871 }
00872 
00873 
00874 void Vehicle :: addview ()
00875 {
00876    if ( viewOnMap )
00877       fatalError ("void Vehicle :: addview - the vehicle is already viewing the map");
00878 
00879    viewOnMap = true;
00880    tcomputevehicleview bes ( gamemap );
00881    bes.init( this, +1 );
00882    bes.startsearch();
00883 }
00884 
00885 void Vehicle :: resetview()
00886 {
00887    viewOnMap = false;  
00888 }
00889 
00890 void Vehicle :: removeview ()
00891 {
00892    if ( !viewOnMap )
00893       fatalError ("void Vehicle :: removeview - the vehicle is not viewing the map");
00894 
00895    tcomputevehicleview bes ( gamemap );
00896    bes.init( this, -1 );
00897    bes.startsearch();
00898 
00899    viewOnMap = false;
00900 }
00901 
00902 
00903 void Vehicle :: postAttack( bool reactionFire, const Context& context )
00904 {
00905    if ( !reactionFire ) {
00906       if ( typ->hasFunction( ContainerBaseType::MoveAfterAttack  ) ) {
00907          int decrease = maxMovement() * attackmovecost / 100;
00908          if ( decrease )
00909             (new ChangeUnitMovement( this, decrease, true ))->execute( context );
00910       } else {
00911          GameAction* a = new ChangeUnitMovement( this, 0 );
00912          a->execute( context );
00913       }
00914       
00915       GameAction* a = new ChangeUnitProperty( this, ChangeUnitProperty::AttackedFlag, 1 );
00916       a->execute( context );
00917    }
00918 }
00919 
00920 void Vehicle :: postAttack( bool reactionFire )
00921 {
00922    if ( !reactionFire )
00923       attacked = true;
00924    
00925    if ( typ->hasFunction( ContainerBaseType::MoveAfterAttack  ) )
00926       decreaseMovement ( maxMovement() * attackmovecost / 100 );
00927    else
00928       if ( reactionfire.getStatus() == ReactionFire::off )
00929          setMovement ( 0 );
00930 }
00931 
00932 
00933 void Vehicle::setAttacked()
00934 {
00935    attacked = true;
00936    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00937       if ( *i ) 
00938          (*i)->setAttacked();
00939 }
00940 
00941 void Vehicle::setAttacked( bool recursive, const Context& context )
00942 {
00943    GameAction* a = new ChangeUnitProperty( this, ChangeUnitProperty::AttackedFlag, 1 );
00944    a->execute( context );
00945    
00946    if ( recursive )
00947       for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00948          if ( *i ) 
00949             (*i)->setAttacked( true, context );
00950 }
00951 
00952 
00953 int Vehicle::maxMovement ( void ) const
00954 {
00955    return typ->movement[getFirstBit(height)];
00956 }
00957 
00958 
00959 void Vehicle :: fillMagically( bool ammunition, bool resources )
00960 {
00961    if ( resources )
00962       tank = getStorageCapacity();
00963 
00964    if ( ammunition ) 
00965       for ( int m = 0; m < typ->weapons.count ; m++) {
00966          ammo[m] = typ->weapons.weapon[m].count;
00967          weapstrength[m] = typ->weapons.weapon[m].maxstrength;
00968       }
00969 }
00970 
00971 
00972 
00973 
00974 #define cem_experience    0x1
00975 #define cem_damage        0x2
00976 #define cem_fuel          0x4
00977 #define cem_ammunition    0x8
00978 #define cem_weapstrength  0x10
00979 #define cem_loading       0x20
00980 #define cem_attacked      0x40
00981 #define cem_height        0x80
00982 #define cem_movement      0x100
00983 #define cem_direction     0x200
00984 #define cem_material      0x400
00985 #define cem_energy        0x800
00986 #define cem_class         0x1000
00987 #define cem_networkid     0x2000
00988 #define cem_name          0x4000
00989 #define cem_armor         0x8000
00990 #define cem_reactionfire  0x10000
00991 #define cem_reactionfire2 0x20000
00992 #define cem_poweron       0x40000
00993 #define cem_weapstrength2 0x80000
00994 #define cem_ammunition2   0x100000
00995 #define cem_energyUsed    0x200000
00996 #define cem_position      0x400000
00997 #define cem_aiparam       0x800000
00998 #define cem_version       0x1000000
00999 
01000 
01001 
01002 
01003 const int vehicleVersion = 8;
01004 
01005 void   Vehicle::write ( tnstream& stream, bool includeLoadedUnits ) const
01006 {
01007     stream.writeWord ( 0 );
01008     stream.writeInt( min( vehicleVersion, UNITVERSIONLIMIT ) );
01009     stream.writeInt( typ->id );
01010 
01011     stream.writeChar ( color );
01012 
01013     int bm = cem_version;
01014 
01015     if ( experience )
01016        bm |= cem_experience;
01017     if ( damage    )
01018        bm |= cem_damage;
01019 
01020     bm |= cem_energy;
01021     bm |= cem_fuel;
01022     bm |= cem_material;
01023 
01024     if ( typ->weapons.count )
01025        for (int m = 0; m < typ->weapons.count ; m++) {
01026           if ( ammo[m] < typ->weapons.weapon[m].count )
01027              bm |= cem_ammunition2;
01028           if ( weapstrength[m] != typ->weapons.weapon[m].maxstrength )
01029              bm |= cem_weapstrength2;
01030        }
01031 
01032     if ( includeLoadedUnits )
01033        for ( Cargo::const_iterator i = cargo.begin(); i != cargo.end(); ++i )
01034           if ( *i ) 
01035              bm |= cem_loading;
01036 
01037     if ( attacked  )
01038        bm |= cem_attacked;
01039     if ( height != chfahrend )
01040        bm |= cem_height;
01041 
01042 //    if ( _movement < typ->movement[getFirstBit(height)] )
01043     bm |= cem_movement;
01044 
01045     if ( direction )
01046        bm |= cem_direction;
01047 
01048 
01049     if ( networkid )
01050        bm |= cem_networkid;
01051 
01052     if ( !name.empty() )
01053        bm |= cem_name;
01054 
01055     if ( reactionfire.status )
01056        bm |= cem_reactionfire;
01057 
01058     if ( generatoractive )
01059        bm |= cem_poweron;
01060 
01061     if ( xpos != 0 || ypos != 0 )
01062        bm |= cem_position;
01063 
01064     for ( int i = 0; i < 8; i++ )
01065        if ( aiparam[i] )
01066           bm |= cem_aiparam;
01067 
01068 
01069 
01070     stream.writeInt( bm );
01071 
01072     stream.writeInt( min( vehicleVersion, UNITVERSIONLIMIT )  );
01073 
01074     if ( bm & cem_experience )
01075          stream.writeChar ( experience );
01076 
01077     if ( bm & cem_damage )
01078          stream.writeChar ( damage );
01079 
01080     if ( bm & cem_fuel )
01081          stream.writeInt ( tank.fuel );
01082 
01083     if ( bm & cem_ammunition2 )
01084        for ( int j= 0; j < 16; j++ )
01085          stream.writeInt ( ammo[j] );
01086 
01087     if ( bm & cem_weapstrength2 )
01088        for ( int j = 0; j < 16; j++ )
01089          stream.writeInt ( weapstrength[j] );
01090 
01091     if ( bm & cem_loading ) {
01092        int c=0;
01093        for ( Cargo::const_iterator i = cargo.begin(); i != cargo.end(); ++i )
01094           if ( *i ) 
01095              ++c;
01096 
01097        if ( UNITVERSIONLIMIT > 3 ) 
01098          stream.writeInt ( c );
01099        else
01100          stream.writeChar ( c );
01101 
01102        for ( Cargo::const_iterator i = cargo.begin(); i != cargo.end(); ++i )
01103           if ( *i ) 
01104              (*i)->write ( stream );
01105     }
01106 
01107     if ( bm & cem_height )
01108          stream.writeChar ( height );
01109 
01110     if ( bm & cem_movement )
01111          stream.writeInt ( _movement );
01112 
01113     if ( bm & cem_direction )
01114          stream.writeChar ( direction );
01115 
01116     if ( bm & cem_material )
01117          stream.writeInt ( tank.material );
01118 
01119     if ( bm & cem_energy )
01120          stream.writeInt ( tank.energy );
01121 
01122     if ( bm & cem_networkid )
01123          stream.writeInt ( networkid );
01124 
01125     if ( bm & cem_attacked )
01126          stream.writeChar ( attacked );
01127 
01128     if ( bm & cem_name     )
01129          stream.writeString ( name );
01130 
01131     if ( bm & cem_reactionfire )
01132        stream.writeChar ( reactionfire.status );
01133 
01134     if ( bm & cem_poweron )
01135        stream.writeInt ( generatoractive );
01136 
01137     if ( bm & cem_position ) {
01138        stream.writeInt ( xpos );
01139        stream.writeInt ( ypos );
01140     }
01141 
01142     if ( bm & cem_aiparam ) {
01143        stream.writeInt( 0x23451234 );
01144        for ( int i = 0; i < 8; i++ )
01145           stream.writeInt ( aiparam[i] != NULL );
01146 
01147        for ( int i = 0; i < 8; i++ )
01148           if ( aiparam[i] )
01149              aiparam[i]->write ( stream );
01150 
01151        stream.writeInt( 0x23451234 );
01152     }
01153 
01154     writeClassContainer( reactionfire.weaponShots, stream );
01155     writeClassContainer( reactionfire.nonattackableUnits, stream );
01156 
01157     if ( UNITVERSIONLIMIT >= 5 ) {
01158       stream.writeInt( internalUnitProduction.size() );
01159       for ( int i = 0; i < internalUnitProduction.size(); ++i )
01160          stream.writeInt( internalUnitProduction[i]->id );
01161     }
01162 
01163     if ( UNITVERSIONLIMIT >= 6 ) {
01164       stream.writeInt( maxresearchpoints );
01165       stream.writeInt( researchpoints );
01166       plus.write( stream );
01167       maxplus.write( stream );
01168       // actstorage.write( stream );
01169       bi_resourceplus.write( stream );
01170     }
01171     
01172     stream.writeInt( view );
01173     
01174     stream.writeString( privateName );
01175 }
01176 
01177 void   Vehicle::read ( tnstream& stream )
01178 {
01179     int _id = stream.readWord ();
01180     int version = 0;
01181     if ( _id == 0 ) {
01182        version = stream.readInt();
01183        _id = stream.readInt();
01184     }
01185 
01186     stream.readChar (); // color
01187     if ( _id != typ->id )
01188        fatalError ( "Vehicle::read - trying to read a unit of different type" );
01189 
01190     readData ( stream );
01191 }
01192 
01193 void   Vehicle::readData ( tnstream& stream )
01194 {
01195 
01196     int bm = stream.readInt();
01197 
01198     int version = 0;
01199     if ( bm & cem_version )
01200        version = stream.readInt();
01201 
01202     if ( bm & cem_experience )
01203        experience = stream.readChar();
01204     else
01205        experience = 0;
01206 
01207     if ( bm & cem_damage )
01208        damage = stream.readChar();
01209     else
01210        damage = 0;
01211 
01212     if ( bm & cem_fuel ) {
01213        tank.fuel = stream.readInt();
01214        if ( tank.fuel > getStorageCapacity().fuel )
01215           tank.fuel = getStorageCapacity().fuel;
01216     } else
01217        tank.fuel = getStorageCapacity().fuel;
01218 
01219     if ( bm & cem_ammunition ) {
01220        for ( int i = 0; i < 8; i++ )
01221          ammo[i] = stream.readWord();
01222     } else
01223      if ( bm & cem_ammunition2 ) {
01224         for ( int i = 0; i < 16; i++ ) {
01225           ammo[i] = stream.readInt();
01226           if ( ammo[i] > typ->weapons.weapon[i].count )
01227              ammo[i] = typ->weapons.weapon[i].count;
01228           if ( ammo[i] < 0 )
01229              ammo[i] = 0;
01230         }
01231 
01232      } else
01233        for (int i=0; i < typ->weapons.count ;i++ )
01234           ammo[i] = typ->weapons.weapon[i].count;
01235 
01236 
01237     if ( bm & cem_weapstrength ) {
01238        for ( int i = 0; i < 8; i++ )
01239          weapstrength[i] = stream.readWord();
01240 
01241     } else
01242      if ( bm & cem_weapstrength2 ) {
01243         for ( int i = 0; i < 16; i++ )
01244            weapstrength[i] = stream.readInt();
01245      } else
01246        for (int i=0; i < typ->weapons.count ;i++ )
01247           weapstrength[i] = typ->weapons.weapon[i].maxstrength;
01248 
01249     if ( bm & cem_loading ) {
01250        int c;
01251        if ( version <= 3 )
01252           c = stream.readChar();
01253        else
01254           c = stream.readInt();   
01255 
01256        if ( c ) {
01257           for (int k = 0; k < c; k++) {
01258              Vehicle* v = Vehicle::newFromStream ( gamemap, stream );
01259              addToCargo(v);
01260              if ( v && v->reactionfire.getStatus() != Vehicle::ReactionFire::off && !v->baseType->hasFunction( ContainerBaseType::MoveWithReactionFire ))
01261                 /* there are two reasons that a unit with enabled RF is put into a transport, although the unit cannot move with RF:
01262                    - the unitType was changed after the unit was moved into the transport
01263                    - RF was enable with the mapeditor
01264                    In order for the user to have any change to get the unit out again, we disable RF here */
01265                 v->reactionfire.disable();
01266           }
01267        }
01268     }
01269 
01270     if ( bm & cem_height )
01271        height = stream.readChar();
01272     else
01273        height = chfahrend;
01274 
01275     if ( ! (height & typ->height) )
01276        height = 1 << getFirstBit ( typ->height );
01277 
01278     if ( bm & cem_movement ) {
01279        int m;
01280        if ( version <= 6 )
01281           m = stream.readChar();
01282        else
01283           m = stream.readInt();
01284        
01285        setMovement ( min( m,typ->movement [ getFirstBit ( height ) ]), 0 );
01286     } else
01287        setMovement ( typ->movement [ getFirstBit ( height ) ], 0 );
01288 
01289     if ( bm & cem_direction )
01290        direction = stream.readChar();
01291     else
01292        direction = 0;
01293 
01294     if ( bm & cem_material ){
01295        tank.material = min( stream.readInt(), getStorageCapacity().material);
01296     } else
01297        tank.material = getStorageCapacity().material;
01298 
01299     if ( bm & cem_energy ) {
01300        tank.energy = min ( stream.readInt(), getStorageCapacity().energy);
01301        if ( tank.energy < 0 )
01302           tank.energy = 0;
01303     } else
01304        tank.energy = getStorageCapacity().energy;
01305 
01306     if ( bm & cem_class )
01307        stream.readChar(); // was: class
01308 
01309     if ( bm & cem_armor )
01310        stream.readWord(); // was: armor
01311 
01312     if ( bm & cem_networkid )
01313        networkid = stream.readInt();
01314     else
01315        networkid = 0;
01316 
01317     if ( bm & cem_attacked )
01318        attacked = stream.readChar();
01319     else
01320        attacked = false;
01321 
01322     if ( bm & cem_name )
01323        name = stream.readString ( );
01324 
01325     int reactionfirestatus = 0;
01326     if ( bm & cem_reactionfire )
01327        reactionfirestatus = stream.readChar();
01328 
01329     int reactionfireenemiesAttackable = 0;
01330     if ( bm & cem_reactionfire2 )
01331        reactionfireenemiesAttackable = stream.readChar();  
01332 
01333     if ( reactionfirestatus >= 8 && reactionfireenemiesAttackable <= 4 ) { // for transition from the old reactionfire system ( < ASC1.2.0 ) to the new one ( >= ASC1.2.0 )
01334        reactionfire.status = ReactionFire::Status ( reactionfireenemiesAttackable );
01335        setMovement ( typ->movement [ getFirstBit ( height ) ], 0 );
01336     } else
01337        reactionfire.status = ReactionFire::Status ( reactionfirestatus );
01338 
01339 
01340     if ( (reactionfire.status != ReactionFire::off)  && baseType->hasFunction(ContainerBaseType::NoReactionfire ))
01341        reactionfire.status = ReactionFire::off;
01342 
01343 
01344     if ( bm & cem_poweron )
01345        generatoractive = stream.readInt();
01346     else
01347        generatoractive = 0;
01348 
01349     if ( bm & cem_energyUsed )
01350        stream.readInt ();
01351 
01352     if ( bm & cem_position ) {
01353        int x = stream.readInt ( );
01354        int y = stream.readInt ( );
01355        setnewposition ( x, y );
01356     } else
01357        setnewposition ( 0, 0 );
01358 
01359     if ( bm & cem_aiparam ) {
01360        int magic = stream.readInt();
01361        if ( magic != 0x23451234 )
01362           throw ASCmsgException ( "Vehicle::read() - inconsistent data stream" );
01363        for ( int i = 0; i < 8; i++ ) {
01364           if ( aiparam[i] ) {
01365              delete aiparam[i];
01366              aiparam[i] = NULL;
01367           }
01368           if ( stream.readInt() ) 
01369              aiparam[i] = new AiParameter ( this );
01370        }
01371 
01372        for ( int i = 0; i < 8; i++ )
01373           if ( aiparam[i] )
01374              aiparam[i]->read ( stream );
01375 
01376        magic = stream.readInt();
01377        if ( magic != 0x23451234 )
01378           throw ASCmsgException ( "Vehicle::read() - inconsistent data stream" );
01379     }
01380 
01381     for ( int m = 0; m < typ->weapons.count ; m++)
01382        if ( typ->weapons.weapon[m].getScalarWeaponType() >= 0 )
01383           weapstrength[m] = typ->weapons.weapon[m].maxstrength;
01384        else
01385           weapstrength[m] = 0;
01386 
01387     if ( version >= 1 ) {
01388       readClassContainer( reactionfire.weaponShots, stream );
01389       reactionfire.checkData();
01390     }
01391     if ( version >= 2 )
01392       readClassContainer( reactionfire.nonattackableUnits, stream );
01393 
01394     internalUnitProduction.clear();
01395     if ( version >= 5 ) {
01396        int pcount = stream.readInt();
01397        for ( int i = 0; i< pcount; ++i ) {
01398           int id = stream.readInt();
01399           internalUnitProduction.push_back ( gamemap->getvehicletype_byid ( id ) );
01400           if ( !internalUnitProduction[i] )
01401              throw InvalidID ( "unit", id );
01402        }
01403     }
01404 
01405     if ( version >= 6 ) {
01406        maxresearchpoints = stream.readInt();
01407        researchpoints = stream.readInt();
01408        plus.read( stream );
01409        maxplus.read( stream );
01410        bi_resourceplus.read( stream );
01411     } 
01412     
01413     if ( version >= 7 )
01414        view = stream.readInt();
01415     else
01416        view = typ->view;
01417     
01418     if ( version >= 8 )
01419        privateName = stream.readString();
01420     else
01421        privateName = "";
01422 
01423 }
01424 
01425 MapCoordinate3D Vehicle :: getPosition ( ) const
01426 {
01427    return MapCoordinate3D ( xpos, ypos, height );
01428 }
01429 
01430 MapCoordinate3D Vehicle :: getPosition3D ( ) const
01431 {
01432    if ( gamemap->getField(xpos,ypos)->unitHere(this) )
01433       return MapCoordinate3D ( xpos, ypos, height );
01434    else {
01435       MapCoordinate3D pos;
01436       pos.setnum ( xpos, ypos, -1 );
01437       return pos;
01438    }
01439 }
01440 
01441 
01442 ASCString  Vehicle::getName() const
01443 {
01444    if ( name.empty() )
01445       return typ->getName();
01446    else
01447       return name;
01448 }
01449 
01450 
01451 int Vehicle::getAmmo( int type, int num ) const
01452 {
01453    int got = 0;
01454    int weap = 0;
01455    while ( weap < typ->weapons.count && got < num ) {
01456       if ( typ->weapons.weapon[weap].getScalarWeaponType() == type ) {
01457          int toget = min( num - got, ammo[weap]);
01458          got += toget;
01459       }
01460       ++weap;
01461    }
01462 
01463    return got;
01464 }
01465 
01466 int Vehicle::getAmmo( int type, int num, bool queryOnly ) 
01467 {
01468    if ( num < 0 )
01469       return -putAmmo( type, -num, queryOnly );
01470 
01471    int got = 0;
01472    
01473    
01474    // pass 1: only weapons with refuel 
01475    int weap = 0;
01476    while ( weap < typ->weapons.count && got < num ) {
01477       if ( typ->weapons.weapon[weap].getScalarWeaponType() == type  && typ->weapons.weapon[weap].canRefuel() ) {
01478          int toget = min( num - got, ammo[weap]);
01479          if ( !queryOnly )
01480             ammo[weap] -= toget;
01481          got += toget;
01482       }
01483       ++weap;
01484    }
01485    
01486    
01487    // pass 2: all the others
01488    weap = 0;
01489    while ( weap < typ->weapons.count && got < num ) {
01490       if ( typ->weapons.weapon[weap].getScalarWeaponType() == type && !typ->weapons.weapon[weap].canRefuel()) {
01491          int toget = min( num - got, ammo[weap]);
01492          if ( !queryOnly )
01493             ammo[weap] -= toget;
01494          got += toget;
01495       }
01496       ++weap;
01497    }
01498    // if ( got && !queryOnly )
01499    //   ammoChanged();
01500    return got;
01501 }
01502 
01503 int Vehicle::putAmmo( int type, int num, bool queryOnly )
01504 {
01505    if ( num < 0 )
01506       return -getAmmo( type, -num, queryOnly );
01507 
01508    int put = 0;
01509    int weap = 0;
01510    while ( weap < typ->weapons.count && put < num ) {
01511       if ( typ->weapons.weapon[weap].getScalarWeaponType() == type && typ->weapons.weapon[weap].shootable() ) {
01512          int toput = min( num - put, typ->weapons.weapon[weap].count - ammo[weap]);
01513          if ( !queryOnly )
01514             ammo[weap] += toput;
01515          put += toput;
01516       }
01517       ++weap;
01518    }
01519 
01520    weap = 0;
01521    while ( weap < typ->weapons.count && put < num ) {
01522       if ( typ->weapons.weapon[weap].getScalarWeaponType() == type ) {
01523          int toput = min( num - put, typ->weapons.weapon[weap].count - ammo[weap]);
01524          if ( !queryOnly )
01525             ammo[weap] += toput;
01526          put += toput;
01527       }
01528       ++weap;
01529    }
01530 
01531    // if ( put && !queryOnly )
01532    //   ammoChanged();
01533    return put;
01534 }
01535 
01536 
01537 int Vehicle::maxAmmo( int type ) const
01538 {
01539    int ammo = 0;
01540    for ( int i = 0; i < typ->weapons.count; ++i ) 
01541       if ( typ->weapons.weapon[i].getScalarWeaponType() == type )
01542          ammo += typ->weapons.weapon[i].count;
01543    return ammo;
01544 
01545 }
01546 
01547 
01548 int Vehicle::getArmor() const
01549 {
01550    return typ->armor;
01551 }
01552 
01553 
01554 void Vehicle::paint ( Surface& s, SPoint pos, int shadowDist ) const
01555 {
01556   #ifdef sgmain
01557    bool shaded = (!canMove()) && maxMovement() && ( color == gamemap->actplayer*8) && (attacked || !typ->weapons.count || CGameOptions::Instance()->units_gray_after_move );
01558   #else
01559    bool shaded = 0;
01560   #endif
01561 
01562    paintField( typ->getImage(), s, pos, direction, shaded, shadowDist );
01563 }
01564 
01565 void Vehicle::paint ( Surface& s, SPoint pos, bool shaded, int shadowDist ) const
01566 {
01567    paintField( typ->getImage(), s, pos, direction, shaded, shadowDist );
01568 }
01569 
01570 Surface Vehicle::getImage() const
01571 {
01572    Surface s = Surface::createSurface( fieldsizex, fieldsizey, 32, Surface::transparent << 24 );
01573    paint( s, SPoint(0,0), false, 0 );
01574    return s;
01575 }
01576 
01577 
01578 vector<MapCoordinate> Vehicle::getCoveredFields()
01579 {
01580    vector<MapCoordinate> fields;
01581    fields.push_back( getPosition() );
01582    return fields;
01583 }
01584 
01585 
01586 int Vehicle::getMemoryFootprint() const
01587 {
01588    return sizeof(*this);
01589 }
01590 
01591 
01592 Vehicle* Vehicle::newFromStream ( GameMap* gamemap, tnstream& stream, int forceNetworkID )
01593 {
01594    int id = stream.readWord ();
01595    int version = 0;
01596    if ( id == 0 ) {
01597       version = stream.readInt();
01598       id = stream.readInt();
01599    }
01600 
01601    VehicleType* fzt = gamemap->getvehicletype_byid ( id );
01602    if ( !fzt )
01603       throw InvalidID ( "vehicle", id );
01604 
01605    int color = stream.readChar ();
01606 
01607    // a forced networkID of -2 will prevent any ID from being assigned and the unit not being registered in the ID cache
01608    Vehicle* v = new Vehicle ( fzt, gamemap, color/8, -2 );
01609 
01610    v->readData ( stream );
01611 
01612    if ( forceNetworkID > 0 )
01613       v->networkid = forceNetworkID;
01614 
01615    if( gamemap->getUnit( v->networkid, false ))
01616       v->networkid = gamemap->idManager.getNewNetworkID();
01617    gamemap->idManager.registerUnitNetworkID(v);
01618 
01619    return v;
01620 }
01621 
01625 const SingleWeapon* Vehicle::getWeapon( unsigned weaponNum ) const
01626 {
01627   if ( weaponNum <= typ->weapons.count )
01628      return &typ->weapons.weapon[weaponNum];
01629   else
01630      return NULL;
01631 }
01632 
01633 
01634 
01635 ASCString getUnitReference ( Vehicle* veh )
01636 {
01637    ASCString s = "The unit " + veh->getName();
01638    s += " (position: "+ veh->getPosition().toString() + ") ";
01639    return s;
01640 }
01641 
01642 
01643 
01644 const int UnitHooveringLogic::FuelConsumption = 50;
01645 
01646 int UnitHooveringLogic::calcFuelUsage( const Vehicle* veh )
01647 {
01648    if ( veh->height < chtieffliegend || veh->height > chhochfliegend )
01649       return 0;
01650 
01651    if ( veh->maxMovement() )
01652       return veh->getMovement(false, false )  * FuelConsumption * veh->typ->fuelConsumption / (100 * minmalq ) ;
01653    else
01654       return (veh->getMap()->weather.windSpeed * maxwindspeed / 256 ) * veh->typ->fuelConsumption / ( minmalq * 64 );
01655 }
01656 
01657 
01658 int UnitHooveringLogic::getEndurance ( const VehicleType* veh, int height, int resourceModel )
01659 {
01660    assert( height < 8 );
01661    
01662    if ( ! (veh->height & (chtieffliegend | chfliegend | chhochfliegend )))
01663       return -1;
01664    
01665    if ( !veh->fuelConsumption )
01666       return -1;
01667    
01668    int maxmove;
01669    if ( height == -1 ) 
01670       maxmove = max( max( veh->movement[4], veh->movement[5]), veh->movement[6] );
01671    else 
01672       maxmove = veh->movement[height];
01673       
01674    int fields = minmalq * veh->getStorageCapacity(resourceModel).fuel * 100 / (veh->fuelConsumption * FuelConsumption ); 
01675    if ( maxmove )
01676       return (fields / maxmove );
01677    else
01678       return 0;
01679 }
01680 
01681 int UnitHooveringLogic::getEndurance ( const Vehicle* veh )
01682 {
01683    if ( veh->height < chtieffliegend || veh->height > chhochfliegend || veh->typ->fuelConsumption <= 0)
01684       return -1;
01685 
01686    if ( veh->getCarrier() )
01687       return -1;
01688 
01689    int fuelUsage = calcFuelUsage( veh );
01690    if ( fuelUsage > veh->getTank().fuel )
01691       return 0;
01692 
01693 
01694    int fields = (veh->getTank().fuel - fuelUsage) * 100 / (veh->typ->fuelConsumption * FuelConsumption ); 
01695    if ( veh->maxMovement() )
01696       return 1 + (fields * minmalq / veh->maxMovement() );
01697    else
01698       return 1;
01699 }

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