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

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 const float repairEfficiencyVehicle[resourceTypeNum*resourceTypeNum] = { 0,  0,  0,
00034                                                                          0,  0.5, 0,
00035                                                                          0.5, 0,  1 };
00036 
00037 
00038 #ifndef UNITVERSIONLIMIT
00039 # define UNITVERSIONLIMIT 0x7fffffff
00040 #endif
00041 
00042 
00043 
00044 Vehicle :: Vehicle (  )
00045           : ContainerBase ( NULL, NULL, 0 ), repairEfficiency ( repairEfficiencyVehicle ), typ ( NULL ), reactionfire ( this )
00046 {
00047 }
00048 
00049 Vehicle :: Vehicle ( const Vehicle& v )
00050           : ContainerBase ( NULL, NULL, 0 ), repairEfficiency ( repairEfficiencyVehicle ), typ ( NULL ), reactionfire ( this )
00051 {
00052 }
00053 
00054 
00055 Vehicle :: Vehicle ( const Vehicletype* t, GameMap* actmap, int player )
00056           : ContainerBase ( t, actmap, player ), repairEfficiency ( repairEfficiencyVehicle ), typ ( t ), reactionfire ( this )
00057 {
00058    viewOnMap = false;
00059 
00060    if ( player > 8 )
00061       fatalError ( "Vehicle :: Vehicle ; invalid player ");
00062 
00063    init();
00064 
00065    gamemap->player[player].vehicleList.push_back ( this );
00066    
00067    networkid = gamemap->getNewNetworkID();
00068    gamemap->registerUnitNetworkID( this );
00069 }
00070 
00071 Vehicle :: Vehicle ( const Vehicletype* t, GameMap* actmap, int player, int networkID )
00072           : ContainerBase ( t, actmap, player ), repairEfficiency ( repairEfficiencyVehicle ), typ ( t ), reactionfire ( this )
00073 {
00074    viewOnMap = false;
00075 
00076    if ( player > 8 )
00077       fatalError ( "Vehicle :: Vehicle ; invalid player ");
00078 
00079    init();
00080 
00081    gamemap->player[player].vehicleList.push_back ( this );
00082    if ( networkID == -1 ) {
00083       networkid = gamemap->getNewNetworkID();
00084    } else
00085       if ( networkID >= 0 ) {
00086          networkid = networkID;
00087       }
00088 
00089    if ( networkID >= 0 )
00090       gamemap->registerUnitNetworkID( this );
00091 }
00092 
00093 
00094 Vehicle :: ~Vehicle (  )
00095 {
00096    #ifndef karteneditor
00097    if ( !typ->wreckageObject.empty() && gamemap && gamemap->state != GameMap::Destruction && !cleanRemove) {
00098       tfield* fld = getMap()->getField(getPosition());
00099       if ( fld->vehicle ==  this ) {
00100          for ( vector<int>::const_iterator i = typ->wreckageObject.begin(); i != typ->wreckageObject.end(); ++i ) {
00101             ObjectType* obj = getMap()->getobjecttype_byid( *i );
00102             if ( obj ) {
00103                getMap()->getField(getPosition()) -> addobject ( obj );
00104                Object* o = getMap()->getField(getPosition())->checkforobject(obj);
00105                if ( o )
00106                   o->setdir( direction );
00107             }
00108          }
00109       }
00110    }
00111    #endif
00112 
00113    for ( int i = 0; i < 8; i++ ) {
00114       delete aiparam[i];
00115       aiparam[i] = NULL;
00116    }
00117       
00118 
00119    
00120    if ( viewOnMap && gamemap && gamemap->state != GameMap::Destruction ) {
00121       removeview();
00122       viewOnMap = false;
00123    }
00124 
00125    if ( gamemap ) {
00126       int c = color/8;
00127 
00128       Player::VehicleList::iterator i = find ( gamemap->player[c].vehicleList.begin(), gamemap->player[c].vehicleList.end(), this );
00129       if ( i != gamemap->player[c].vehicleList.end() )
00130          gamemap->player[c].vehicleList.erase ( i );
00131 
00132       gamemap->unregisterUnitNetworkID(this);
00133    }
00134 
00135    tfield* fld = gamemap->getField( xpos, ypos);
00136    if ( fld && fld->vehicle  == this )
00137        fld->vehicle = NULL;
00138    
00139    if ( getCarrier() )
00140       getCarrier()->removeUnitFromCargo( this, true );
00141 }
00142 
00143 
00144 
00145 
00146 void Vehicle :: init ( void )
00147 {
00148    xpos = -1;
00149    ypos = -1;
00150 
00151    for ( int i = 0; i < 16; i++ ) {
00152       weapstrength[i] = 0;
00153       ammo[i] = 0;
00154    }
00155 
00156    damage = 0;
00157 
00158    experience = 0;
00159    attacked = false;
00160 
00161    if ( typ ) {
00162       height = 1 << log2( typ->height );
00163 
00164       // These are the preferred levels of height
00165       if (typ->height & chfahrend )
00166          height = chfahrend;
00167       else
00168          if (typ->height & chschwimmend )
00169             height = chschwimmend;
00170 
00171       for ( int m = 0; m < typ->weapons.count ; m++)
00172          weapstrength[m] = typ->weapons.weapon[m].maxstrength;
00173 
00174       setMovement ( typ->movement[log2(height)], 0 );
00175    } else {
00176       height = 0;
00177 
00178       setMovement ( 0, 0 );
00179    }
00180 
00181    direction = 0;
00182    xpos = -1;
00183    ypos = -1;
00184    connection = 0;
00185    networkid = -1;
00186 
00187    if ( typ && typ->hasFunction(ContainerBaseType::MoveWithReactionFire))
00188       reactionfire.status = ReactionFire::ready;
00189    else
00190       reactionfire.status = ReactionFire::off;
00191 
00192    generatoractive = 0;
00193 
00194    cleanRemove = false;
00195 
00196    for ( int a = 0; a < 8 ; a++ )
00197       aiparam[a] = NULL;
00198 }
00199 
00200 
00201 bool Vehicle :: canRepair( const ContainerBase* item ) const
00202 {
00203    return typ->hasFunction( ContainerBaseType::InternalUnitRepair  ) ||
00204           typ->hasFunction( ContainerBaseType::ExternalRepair  ) ||
00205           (item == this && typ->autorepairrate ) ;
00206 }
00207 
00208 int Vehicle :: putResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00209 {
00210    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00211 
00212    if ( amount < 0 ) {
00213       return -getResource( -amount, resourcetype, queryonly, scope, player );
00214    } else {
00215       if ( resourcetype == 0 )  // no energy storable
00216          return 0;
00217       
00218       if ( player < 0 )
00219          player = getMap()->actplayer;
00220       
00221       if ( player != getOwner() && !(getMap()->getPlayer(getOwner()).diplomacy.getState(player) >= PEACE)  )
00222          return 0;
00223 
00224       int spaceAvail = getStorageCapacity().resource( resourcetype ) - tank.resource(resourcetype);
00225       if ( spaceAvail < 0 )
00226          spaceAvail = 0;
00227 
00228       int tostore = min ( spaceAvail, amount);
00229       if ( !queryonly ) {
00230          tank.resource(resourcetype) += tostore;
00231          // if ( tostore > 0 )
00232          //   resourceChanged();
00233       }
00234 
00235       return tostore;
00236    }
00237 }
00238 
00239 int Vehicle :: getResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00240 {
00241    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00242 
00243    if ( amount < 0 ) {
00244       return -putResource( -amount, resourcetype, queryonly, scope, player );
00245    } else {
00246       if ( resourcetype == 0 && !getGeneratorStatus() )
00247          return 0;
00248 
00249       if ( player < 0 )
00250          player = getMap()->actplayer;
00251       
00252       if ( player != getOwner() && !getMap()->getPlayer(getOwner()).diplomacy.isAllied(player) && !queryonly)
00253          return 0;
00254       
00255       
00256       int toget = min ( tank.resource(resourcetype), amount);
00257       if ( !queryonly ) {
00258          tank.resource(resourcetype) -= toget;
00259          // if ( toget > 0 )
00260          //   resourceChanged();
00261       }
00262 
00263       return toget;
00264    }
00265 }
00266 
00267 int Vehicle :: getAvailableResource ( int amount, int resourcetype, int scope ) const
00268 {
00269    //  if units start using/storing resources that will not be stored in the unit itself, the replays will fail !
00270 
00271    if ( resourcetype == 0 && !getGeneratorStatus() )
00272       return 0;
00273 
00274    int toget = min ( tank.resource(resourcetype), amount);
00275    return toget;
00276 }
00277 
00278 
00279 Resources Vehicle::getResource ( const Resources& res ) const
00280 {
00281    Resources got;
00282    for ( int r = 0; r < 3; ++r )
00283       got.resource(r) = getAvailableResource( res.resource(r), r);
00284         
00285    return got;
00286 }
00287 
00288 
00289 Resources Vehicle::getTank() const
00290 {
00291    return tank;
00292 }
00293 
00294 
00295 
00296 void Vehicle :: setGeneratorStatus ( bool status )
00297 {
00298    if ( typ->hasFunction( ContainerBaseType::MatterConverter )) {
00299       generatoractive = status;
00300    } else
00301      generatoractive = 0;
00302 }
00303 
00304 
00305 
00306 int Vehicle::weight( void ) const
00307 {
00308    return typ->weight + cargoWeight();
00309 }
00310 
00311 int Vehicle::size ( void )
00312 {
00313    return typ->weight;
00314 }
00315 
00316 void Vehicle::transform ( const Vehicletype* type )
00317 {
00318    if ( !type )
00319       return;
00320    
00321    typ = type;
00322 
00323    tank = getStorageCapacity();
00324    tank.energy = 0;
00325    
00326    generatoractive = 0;
00327 
00328    for ( int m = 0; m < typ->weapons.count ; m++) {
00329       ammo[m] = typ->weapons.weapon[m].count;
00330       weapstrength[m] = typ->weapons.weapon[m].maxstrength;
00331    }
00332 }
00333 
00334 void Vehicle :: postRepair ( int oldDamage )
00335 {
00336    for ( int i = 0; i < experienceDecreaseDamageBoundaryNum; i++)
00337       if ( oldDamage > experienceDecreaseDamageBoundaries[i] && damage < experienceDecreaseDamageBoundaries[i] )
00338          if ( experience > 0 )
00339             experience-=1;
00340 }
00341 
00342 
00343 /*
00344 void Vehicle :: repairunit(Vehicle* vehicle, int maxrepair )
00345 {
00346    if ( vehicle->damage  &&  tank.fuel  &&  tank.material ) {
00347 
00348       int orgdam = vehicle->damage;
00349 
00350       int dam;
00351       if ( vehicle->damage > maxrepair )
00352          dam = maxrepair;
00353       else
00354          dam = vehicle->damage;
00355 
00356       int fkost = dam * vehicle->typ->productionCost.energy / (100 * repairefficiency_unit );
00357       int mkost = dam * vehicle->typ->productionCost.material / (100 * repairefficiency_unit );
00358       int w;
00359 
00360       if (mkost <= material)
00361          w = 10000;
00362       else
00363          w = 10000 * material / mkost;
00364 
00365       if (fkost > fuel)
00366          if (10000 * fuel / fkost < w)
00367             w = 10000 * fuel / fkost;
00368 
00369 
00370       vehicle->damage -= dam * w / 10000;
00371 
00372       for ( int i = 0; i < experienceDecreaseDamageBoundaryNum; i++)
00373          if ( orgdam > experienceDecreaseDamageBoundaries[i] && vehicle->damage < experienceDecreaseDamageBoundaries[i] )
00374             if ( vehicle->experience > 0 )
00375                vehicle->experience-=1;
00376 
00377 
00378       if ( vehicle != this ) {
00379          if ( vehicle->getMovement() > movement_cost_for_repaired_unit )
00380             vehicle->setMovement ( vehicle->getMovement() -  movement_cost_for_repaired_unit );
00381          else
00382             vehicle->setMovement ( 0 );
00383 
00384          if ( !attack_after_repair )
00385             vehicle->attacked = 0;
00386 
00387          int unitloaded = 0;
00388          for ( int i = 0; i < 32; i++ )
00389             if ( loading[i] == vehicle )
00390                unitloaded = 1;
00391 
00392          if ( !unitloaded )
00393             if ( getMovement() > movement_cost_for_repairing_unit )
00394                setMovement ( getMovement() - movement_cost_for_repairing_unit );
00395             else
00396                setMovement ( 0 );
00397       }
00398 
00399       material -= w * mkost / 10000;
00400       fuel -= w * fkost / 10000;
00401 
00402    }
00403 }
00404 */
00405 
00406 void Vehicle :: endRound ( void )
00407 {
00408    ContainerBase::endRound();
00409    if ( typ->hasFunction( ContainerBaseType::MatterConverter )) {
00410       int endiff = getStorageCapacity().energy - tank.energy;
00411       if ( tank.fuel < endiff * generatortruckefficiency )
00412          endiff = tank.fuel / generatortruckefficiency;
00413 
00414       tank.energy += endiff;
00415       tank.fuel -= endiff * generatortruckefficiency ;
00416    }
00417 }
00418 
00419 void Vehicle :: endAnyTurn()
00420 {
00421    ContainerBase::endAnyTurn();
00422    reactionfire.endAnyTurn();
00423 }
00424 
00425 
00426 void Vehicle :: endOwnTurn()
00427 {
00428    ContainerBase::endOwnTurn();
00429    
00430    if ( typ->autorepairrate > 0 )
00431       if ( damage )
00432          repairItem ( this, max ( damage - typ->autorepairrate, 0 ) );
00433 
00434    reactionfire.endOwnTurn();
00435 
00436    if ( !gamemap->getField(getPosition())->unitHere(this)) {
00437       int mx = -1;
00438       for ( int h = 0; h < 8; h++ )
00439          if ( typ->height & ( 1 << h))
00440             if ( typ->movement[h] > mx ) {
00441                mx = typ->movement[h];
00442                height = 1 << h;
00443             }
00444    }
00445 
00446    for ( int w = 0; w < typ->weapons.count; w++ )
00447       if ( typ->weapons.weapon[w].gettype() & cwlaserb ) {
00448          int cnt = 0;
00449          while ( cnt < typ->weapons.weapon[w].laserRechargeRate  && ammo[w] < typ->weapons.weapon[w].count ) {
00450             for ( int r = 0; r < 3; r++ )
00451                if ( typ->weapons.weapon[w].laserRechargeCost.resource(r) < 0 )
00452                   fatalError (" negative Laser recharge cost !");
00453             if ( ! (ContainerBase::getResource( typ->weapons.weapon[w].laserRechargeCost, 1 ) < typ->weapons.weapon[w].laserRechargeCost )) {
00454                ContainerBase::getResource( typ->weapons.weapon[w].laserRechargeCost, 0 );
00455                ammo[w] += 1;
00456             }
00457             ++cnt;
00458          }
00459       }
00460 
00461    resetMovement();
00462    attacked = false;
00463 
00464 }
00465 
00466 void Vehicle :: resetMovement ( void )
00467 {
00468     int move = typ->movement[log2(height)];
00469     setMovement ( move, 0 );
00470     /*
00471     if (actvehicle->typ->fuelconsumption == 0)
00472        actvehicle->movement = 0;
00473     else {
00474        if ((actvehicle->fuel << 3) / actvehicle->typ->fuelconsumption < move)
00475           actvehicle->movement = (actvehicle->fuel << 3) / actvehicle->typ->fuelconsumption ;
00476        else
00477           actvehicle->movement = move;
00478     }
00479     */
00480 }
00481 
00482 
00483 void Vehicle :: setNewHeight( int bitmappedheight )
00484 {
00485   if ( maxMovement() ) {
00486      float oldperc = float(getMovement ( false )) / float(maxMovement());
00487      height = bitmappedheight;
00488      setMovement ( int(floor(maxMovement() * oldperc + 0.5)) , 0 );
00489   } else {
00490      height = bitmappedheight;
00491      warning("Internal error: unit has invalid height");
00492   }
00493 }
00494 
00495 
00496 void Vehicle :: setMovement ( int newmove, double cargoDivisor )
00497 {
00498 
00499    if ( cargoDivisor < 0 )
00500       cargoDivisor = typ->cargoMovementDivisor;
00501 
00502    if ( newmove < 0 )
00503       newmove = 0;
00504 
00505    if ( cargoDivisor > 0 && typ)
00506       if ( typ->movement[ log2 ( height ) ] ) {
00507          double diff = _movement - newmove;
00508          double perc = diff / typ->movement[ log2 ( height ) ] ;
00509          for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00510             if ( *i ) {
00511                double lperc = perc;
00512                if ( cargoDivisor && cargoNestingDepth() == 0 )
00513                   lperc /= cargoDivisor;
00514 
00515                (*i)->decreaseMovement ( max( 1, int( ceil( lperc * double( (*i)->typ->movement[ log2 ( (*i)->height)] )))));
00516             }
00517    }
00518    _movement = newmove;
00519 }
00520 
00521 bool Vehicle::hasMoved ( void ) const
00522 {
00523    return _movement != typ->movement[ log2 ( height )];
00524 }
00525 
00526 
00527 int Vehicle :: getMovement ( bool checkFuel, bool checkRF ) const
00528 {
00529    if ( checkRF && !reactionfire.canMove() )
00530       return 0;
00531 
00532    if ( typ->fuelConsumption && checkFuel ) {
00533       if ( tank.fuel * minmalq / typ->fuelConsumption < _movement )
00534          return tank.fuel * minmalq / typ->fuelConsumption;
00535       else
00536          return _movement;
00537    } else
00538       return _movement;
00539 }
00540 
00541 void Vehicle :: decreaseMovement ( int amount )
00542 {
00543   int newMovement = _movement - amount;
00544   if ( newMovement < 0 )
00545     newMovement = 0;
00546   if ( newMovement > typ->movement[log2(height)] )
00547     newMovement = typ->movement[log2(height)];
00548   setMovement ( newMovement );
00549 }
00550 
00551 bool Vehicle::movementLeft() const
00552 {
00553    int mv  = getMovement();
00554    if ( mv <= 0 )
00555       return false;
00556    if ( mv >= 10 )
00557       return true;
00558 
00559    if ( height & ( chtieffliegend |chfliegend | chhochfliegend )) {
00560       WindMovement wm ( this );
00561       for ( int i = 0; i < 6; i++ )
00562          if ( 10 - wm.getDist( i ) <= mv )
00563             return true;
00564    }
00565    return false;
00566 }
00567 
00568 bool Vehicle :: canMove ( void ) const
00569 {
00570    // if ( attacked && !(typ->functions & cf_moveafterattack))
00571    //   return false;
00572 
00573    if ( movementLeft() && reactionfire.canMove() ) {
00574       tfield* fld = gamemap->getField ( getPosition() );
00575       if ( fld->unitHere ( this ) ) {
00576          if ( terrainaccessible ( fld, this ) || actmap->getgameparameter( cgp_movefrominvalidfields))
00577             return true;
00578       } else {
00579          ContainerBase* cnt = fld->getContainer();
00580          if ( cnt )
00581             for ( Cargo::const_iterator i = cnt->getCargo().begin(); i != cnt->getCargo().end(); ++i )
00582                if ( *i == this ) 
00583                   if ( cnt->vehicleUnloadable( typ ) > 0 || cnt->vehicleDocking( this, true ) > 0 )
00584                      return true;
00585       }
00586    }
00587    return false;
00588 }
00589 
00590 bool Vehicle::spawnMoveObjects( const MapCoordinate& start, const MapCoordinate& dest )
00591 {
00592    if ( start == dest )
00593       return false;
00594       
00595    bool result = false;
00596    
00597    if ( typ->objectLayedByMovement.size() && (height == chfahrend || height == chschwimmend))  {
00598       int dir = getdirection( start, dest );
00599 
00600       tfield* startField = gamemap->getField(start);
00601       tfield* destField = gamemap->getField(dest);
00602 
00603       for ( int i = 0; i < typ->objectLayedByMovement.size(); i++ ) 
00604          for ( int id = typ->objectLayedByMovement[i].from; id <= typ->objectLayedByMovement[i].to; ++id ) {
00605             ObjectType* object = objectTypeRepository.getObject_byID( id );
00606             if ( object ) 
00607                if ( startField->addobject ( object, 1 << dir ))
00608                   result = true;
00609          }
00610            
00611       dir = (dir + sidenum/2) % sidenum;
00612 
00613       for ( int i = 0; i < typ->objectLayedByMovement.size(); i++ ) 
00614          for ( int id = typ->objectLayedByMovement[i].from; id <= typ->objectLayedByMovement[i].to; ++id ) {
00615             ObjectType* object = objectTypeRepository.getObject_byID( id );
00616             if ( object ) 
00617                if ( destField->addobject ( object, 1 << dir ))
00618                   result = true;
00619          }
00620    }
00621    
00622    return result;
00623 }
00624 
00625 
00626 Vehicle::ReactionFire::ReactionFire ( Vehicle* _unit ) : unit ( _unit )
00627 {
00628    weaponShots.resize(unit->typ->weapons.count);
00629    resetShotCount();
00630 }
00631 
00632 
00633 void Vehicle::ReactionFire::checkData ( )
00634 {
00635    // 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
00636    if ( weaponShots.size() < unit->typ->weapons.count ) {
00637       size_t oldsize = weaponShots.size();
00638       weaponShots.resize( unit->typ->weapons.count );
00639       for ( size_t i = oldsize; i < unit->typ->weapons.count; ++i )
00640          weaponShots[i] = unit->typ->weapons.weapon[i].reactionFireShots;
00641    }
00642 }
00643 
00644 
00645 
00646 void Vehicle::ReactionFire::resetShotCount()
00647 {
00648    assertOrThrow( unit->typ->weapons.count <= weaponShots.size() );
00649    for ( int i = 0; i < unit->typ->weapons.count; ++i ) 
00650       weaponShots[i] = unit->typ->weapons.weapon[i].reactionFireShots;
00651 }
00652 
00653 
00654 int Vehicle::ReactionFire::enable ( void )
00655 {
00656    if ( unit->typ->hasFunction( ContainerBaseType::NoReactionfire  ) )
00657       return -216;
00658       
00659    #ifdef karteneditor
00660    status = ready;
00661    #else
00662    if ( status == off ) {
00663       int weaponCount = 0;
00664       int shootableWeaponCount = 0;
00665       for ( int w = 0; w < unit->typ->weapons.count; w++ )
00666          if ( unit->typ->weapons.weapon[w].shootable() ) {
00667               weaponCount++;
00668               if ( unit->typ->weapons.weapon[w].sourceheight & unit->height )
00669                  shootableWeaponCount++;
00670          }
00671 
00672       if ( weaponCount == 0 )
00673          return -214;
00674 
00675       if ( shootableWeaponCount == 0 )
00676          return -213;
00677 
00678       if ( unit->typ->wait ) {
00679          // if ( unit->hasMoved())
00680          //    status = init1a;
00681          // else
00682             status = init2;
00683       } else {
00684          status = init2;
00685       }
00686    }
00687    #endif
00688    return 0;
00689 }
00690 
00691 void Vehicle::ReactionFire::disable ( void )
00692 {
00693    if ( status != off ) {
00694       if ( status != init1a && status != init2 && !unit->typ->hasFunction(ContainerBaseType::MoveWithReactionFire)  ) {
00695           unit->setMovement ( 0, 0 );
00696        }
00697        status = off;
00698    }
00699 }
00700 
00701 void Vehicle::ReactionFire::endAnyTurn()
00702 {
00703    resetShotCount();
00704 }
00705 
00706 
00707 void Vehicle::ReactionFire::endOwnTurn()
00708 {
00709    if ( status != off ) {
00710       if ( status == init1a )
00711          status = init1b;
00712       else
00713          if ( status == init2 || status == init1b )
00714             status = ready;
00715    }
00716    nonattackableUnits.clear();
00717 }
00718 
00719 bool Vehicle::ReactionFire::canMove() const
00720 {
00721    if ( unit->typ->hasFunction( ContainerBaseType::MoveWithReactionFire  ))
00722       return true;
00723    if ( status == off )
00724       return true;
00725    return false;
00726 }
00727 
00728 bool Vehicle::ReactionFire:: canPerformAttack( Vehicle* target )
00729 {
00730    if ( !unit->getCarrier() )
00731       if ( unit->getMap()->getPlayer(unit).diplomacy.isHostile( actmap->actplayer))
00732          if ( getStatus() >= ready )
00733             if ( find ( nonattackableUnits.begin(), nonattackableUnits.end(), target->networkid) == nonattackableUnits.end() ) 
00734                // if ( enemiesAttackable & ( 1 << target->getOwner() ))
00735                   return true;
00736    
00737    return false;
00738 }
00739 
00740 
00741 
00742 const Vehicletype::HeightChangeMethod* Vehicle::getHeightChange( int dir, int height ) const
00743 {
00744    if ( !reactionfire.canMove() )
00745       return NULL;
00746 
00747    if ( height == 0 )
00748       height = this->height;
00749 
00750    for ( int i = 0; i < typ->heightChangeMethodNum; i++ )
00751       if ( typ->heightChangeMethod[i].startHeight & height )
00752          if ( ( dir > 0 && typ->heightChangeMethod[i].heightDelta > 0) || ( dir < 0 && typ->heightChangeMethod[i].heightDelta < 0))
00753             if ( (1 << (log2(height) + typ->heightChangeMethod[i].heightDelta)) & typ->height )
00754                return &typ->heightChangeMethod[i];
00755 
00756    return NULL;
00757 }
00758 
00759 
00760 
00761 bool Vehicle :: weapexist( void )
00762 {
00763    if ( typ->weapons.count > 0)
00764       for ( int b = 0; b < typ->weapons.count ; b++)
00765          if ( typ->weapons.weapon[b].shootable() )
00766             if ( typ->weapons.weapon[b].offensive() )
00767                if ( ammo[b] )
00768                   return 1;
00769     return 0;
00770 }
00771 
00772 
00773 void Vehicle :: setnewposition ( int x , int y )
00774 {
00775    xpos = x;
00776    ypos = y;
00777    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00778       if ( *i ) 
00779          (*i)->setnewposition ( x , y );
00780 }
00781 
00782 void Vehicle :: setnewposition ( const MapCoordinate& mc )
00783 {
00784    setnewposition ( mc.x, mc.y );
00785 }
00786 
00787 /*
00788 int Vehicle :: getstrongestweapon( int aheight, int distance)
00789 {
00790    int str = 0;
00791    int hw = 255;   //  error-wert  ( keine waffe gefunden )
00792    for ( int i = 0; i < typ->weapons.count; i++) {
00793 
00794       if (( ammo[i]) &&
00795           ( typ->weapons.weapon[i].mindistance <= distance) &&
00796           ( typ->weapons.weapon[i].maxdistance >= distance) &&
00797           ( typ->weapons.weapon[i].targ & aheight ) &&
00798           ( typ->weapons.weapon[i].sourceheight & height )) {
00799             int astr = int( weapstrength[i] * weapDist.getWeapStrength( &typ->weapons.weapon[i], distance, height, aheight));
00800             if ( astr > str ) {
00801                str = astr;
00802                hw  = i;
00803             }
00804        }
00805    }
00806    return hw;
00807 }
00808 */
00809 
00810 void Vehicle::convert ( int col )
00811 {
00812   if ( col > 8)
00813      fatalError("convertvehicle: \n color mu�im bereich 0..8 sein ",2);
00814 
00815    int oldcol = getOwner();
00816 
00817    Player::VehicleList::iterator i = find ( gamemap->player[oldcol].vehicleList.begin(), gamemap->player[oldcol].vehicleList.end(), this );
00818    if ( i != gamemap->player[oldcol].vehicleList.end())
00819       gamemap->player[oldcol].vehicleList.erase ( i );
00820 
00821    gamemap->player[col].vehicleList.push_back( this );
00822 
00823    color = col << 3;
00824 
00825    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00826       if ( *i ) 
00827          (*i)->convert( col );
00828 
00829    // emit signal
00830    conquered();
00831    anyContainerConquered(this);
00832 }
00833 
00834 Vehicle* Vehicle :: constructvehicle ( Vehicletype* tnk, int x, int y )
00835 {
00836    if ( gamemap && vehicleconstructable( tnk, x, y )) {
00837       Vehicle* v = new Vehicle( tnk, gamemap, color/8 );
00838       v->xpos = x;
00839       v->ypos = y;
00840       v->addview();
00841    
00842       for ( int j = 0; j < 8; j++ ) {
00843          int a = int(height) << j;
00844          int b = int(height) >> j;
00845          if ( v->typ->height & a ) {
00846             v->height = a;
00847             break;
00848          }
00849          if ( v->typ->height & b ) {
00850             v->height = b;
00851             break;
00852          }
00853       }
00854       v->setMovement ( 0 );
00855 
00856 
00857       gamemap->getField ( x, y )->vehicle = v;
00858       tank.material -= tnk->productionCost.material;
00859       tank.fuel -= tnk->productionCost.energy;
00860 
00861       decreaseMovement( maxMovement() * typ->unitConstructionMoveCostPercentage/100);
00862       /*
00863       int refuel = 0;
00864       for ( int i = 0; i < typ->weapons.count; i++ )
00865          if ( typ->weapons.weapon[i].service()  )
00866             for ( int j = 0; j < typ->weapons.count ; j++) {
00867                if ( typ->weapons.weapon[j].canRefuel() )
00868                   refuel = 1;
00869                if ( typ->functions & (cffuelref | cfmaterialref) )
00870                   refuel = 1;
00871             }
00872       */
00873       v->attacked = 1;
00874       return v;
00875    } else
00876       return NULL;
00877 }
00878 
00879 bool  Vehicle :: vehicleconstructable ( Vehicletype* tnk, int x, int y )
00880 {
00881    if ( gamemap->getgameparameter(cgp_produceOnlyResearchedStuffExternally) && 
00882        !tnk->techDependency.available ( gamemap->player[getOwner()].research))
00883       return 0;
00884    
00885    if ( getMovement() < maxMovement() * typ->unitConstructionMoveCostPercentage / 100 )
00886       return 0;
00887 
00888 //              if ( getheightdelta( log2(tnk->height), log2(height) ) == 0 )
00889    if( (tnk->height & height ) || (( tnk->height & (chfahrend | chschwimmend)) && (height & (chfahrend | chschwimmend)))) {
00890       int hgt = height;
00891       if ( !(tnk->height & height))
00892          hgt = 1 << log2(tnk->height);
00893       if ( terrainaccessible2( gamemap->getField(x,y), tnk->terrainaccess, hgt ) > 0 )
00894    //   tnk->terrainaccess.accessible ( gamemap->getField(x,y)->bdt ) > 0 || height >= chtieffliegend)
00895          if ( tnk->productionCost.material <= tank.material &&
00896               tnk->productionCost.energy   <= tank.fuel  )
00897               if ( beeline (x, y, xpos, ypos) <= maxmalq )
00898                  return 1;
00899 
00900    }
00901    return 0;
00902 }
00903 
00904 
00905 bool Vehicle :: buildingconstructable ( const BuildingType* building, bool checkResources )
00906 {
00907    if ( !building )
00908       return 0;
00909 
00910 
00911    if ( gamemap->getgameparameter(cgp_forbid_building_construction) )
00912        return 0;
00913 
00914    if ( !building->techDependency.available ( gamemap->player[getOwner()].research))
00915       return 0;
00916 
00917 
00918    int mf = gamemap->getgameparameter ( cgp_building_material_factor );
00919    int ff = gamemap->getgameparameter ( cgp_building_fuel_factor );
00920 
00921    if ( !mf )
00922       mf = 100;
00923    if ( !ff )
00924       ff = 100;
00925 
00926    int hd = getheightdelta ( log2 ( height ), log2 ( building->buildingheight ));
00927 
00928    if ( hd != 0 ) // && !(hd ==-1 && (height == chschwimmend || height == chfahrend)) ???  what was that ??
00929       return 0;
00930 
00931 
00932    if ( (building->productionCost.material * mf / 100 <= tank.material   &&   building->productionCost.fuel * ff / 100 <= tank.fuel) || !checkResources ) {
00933       if ( typ->hasFunction( ContainerBaseType::ConstructBuildings) )
00934          for ( int i = 0; i < typ->buildingsBuildable.size(); i++ )
00935             if ( typ->buildingsBuildable[i].from <= building->id &&
00936                  typ->buildingsBuildable[i].to   >= building->id )
00937                return true;
00938    }
00939    
00940    return false;
00941 }
00942 
00943 int Vehicle :: searchstackforfreeweight ( Vehicle* searchedInnerVehicle )
00944 {
00945    if ( searchedInnerVehicle == this ) {
00946       return typ->maxLoadableWeight - cargoWeight();
00947    } else {
00948       int currentFreeWeight = typ->maxLoadableWeight - cargoWeight();
00949       int innerFreeWeight = -1;
00950       for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
00951          if ( *i ) {
00952             int w = (*i)->searchstackforfreeweight ( searchedInnerVehicle );
00953             if ( w >= 0 )
00954                innerFreeWeight = w;
00955          }
00956 
00957       if ( innerFreeWeight != -1 )
00958          return min ( currentFreeWeight, innerFreeWeight );
00959       else
00960          return -1;
00961    }
00962 }
00963 
00964 
00965 int Vehicle :: freeWeight ()
00966 {
00967    tfield* fld = gamemap->getField ( xpos, ypos );
00968    if ( fld->vehicle )
00969       return fld->vehicle->searchstackforfreeweight ( this );
00970    else
00971       if ( fld->building ) {
00972          for ( Cargo::const_iterator i = fld->building->getCargo().begin(); i != fld->building->getCargo().end(); ++i )
00973             if ( *i ) {
00974                int w3 = (*i)->searchstackforfreeweight ( this );
00975                if ( w3 >= 0 )
00976                   return w3;
00977             }
00978       }
00979 
00980    return -2;
00981 }
00982 
00983 
00984 void Vehicle :: addview ()
00985 {
00986    if ( viewOnMap )
00987       fatalError ("void Vehicle :: addview - the vehicle is already viewing the map");
00988 
00989    viewOnMap = true;
00990    tcomputevehicleview bes ( gamemap );
00991    bes.init( this, +1 );
00992    bes.startsearch();
00993 }
00994 
00995 void Vehicle :: removeview ()
00996 {
00997    if ( !viewOnMap )
00998       fatalError ("void Vehicle :: removeview - the vehicle is not viewing the map");
00999 
01000    tcomputevehicleview bes ( gamemap );
01001    bes.init( this, -1 );
01002    bes.startsearch();
01003 
01004    viewOnMap = false;
01005 }
01006 
01007 
01008 void Vehicle :: postAttack( bool reactionFire )
01009 {
01010    if ( !reactionFire )
01011       attacked = true;
01012    
01013    if ( typ->hasFunction( ContainerBaseType::MoveAfterAttack  ) )
01014       decreaseMovement ( maxMovement() * attackmovecost / 100 );
01015    else
01016       if ( reactionfire.getStatus() == ReactionFire::off )
01017          setMovement ( 0 );
01018 }
01019 
01020 
01021 void Vehicle::setAttacked()
01022 {
01023    attacked = true;
01024    for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
01025       if ( *i ) 
01026          (*i)->setAttacked();
01027 }
01028 
01029 
01030 class tsearchforminablefields: public SearchFields {
01031       int shareview;
01032     public:
01033       int numberoffields;
01034       int run ( const Vehicle*     eht );
01035       virtual void testfield ( const MapCoordinate& mc );
01036       tsearchforminablefields ( GameMap* _gamemap ) : SearchFields ( _gamemap ) {};
01037   };
01038 
01039   /*
01040 bool Vehicle::searchForMineralResourcesAvailable()
01041 {
01042    return tsearchforminablefields::available( this );
01043 }
01044   */
01045 
01046 
01047 void         tsearchforminablefields::testfield( const MapCoordinate& mc )
01048 {
01049     tfield* fld = gamemap->getField ( mc );
01050     if ( !fld->building  ||  fld->building->color == gamemap->actplayer*8  ||  fld->building->color == 8*8)
01051        if ( !fld->vehicle  ||  fld->vehicle->color == gamemap->actplayer*8 ||  fld->vehicle->color == 8*8) {
01052           if ( !fld->resourceview )
01053              fld->resourceview = new tfield::Resourceview;
01054 
01055           for ( int c = 0; c < 8; c++ )
01056              if ( shareview & (1 << c) ) {
01057                 fld->resourceview->visible |= ( 1 << c );
01058                 fld->resourceview->fuelvisible[c] = fld->fuel;
01059                 fld->resourceview->materialvisible[c] = fld->material;
01060              }
01061        }
01062 }
01063 
01064 
01065 
01066 
01067 int  tsearchforminablefields::run( const Vehicle* eht )
01068 {
01069    if ( !eht->typ->hasFunction( ContainerBaseType::DetectsMineralResources  ) )
01070       return -311;
01071 
01072 
01073    shareview = 1 << ( eht->color / 8);
01074    for ( int i = 0; i < 8; i++ )
01075       if ( i*8 != eht->color )
01076          if ( gamemap->player[i].exist() )
01077             if ( gamemap->getPlayer(eht).diplomacy.sharesView(i) )
01078                shareview += 1 << i;
01079 
01080    numberoffields = 0;
01081    initsearch( eht->getPosition(), eht->typ->digrange, 0 );
01082    if ( eht->typ->digrange )
01083       startsearch();
01084 
01085 //   if ( (eht->typ->functions & cfmanualdigger) && !(eht->typ->functions & cfautodigger) )
01086 //      eht->setMovement ( eht->getMovement() - searchforresorcesmovedecrease );
01087 
01088 //   if ( !gamemap->mineralResourcesDisplayed && (eht->typ->functions & cfmanualdigger) && !(eht->typ->functions & cfautodigger))
01089 //      gamemap->mineralResourcesDisplayed = 1;
01090 
01091    return 1;
01092 }
01093 
01094 
01095 int Vehicle::maxMovement ( void ) const
01096 {
01097    return typ->movement[log2(height)];
01098 }
01099 
01100 int Vehicle::searchForMineralResources ( void ) const
01101 {
01102     tsearchforminablefields sfmf ( gamemap );
01103     return sfmf.run( this );
01104 }
01105 
01106 
01107 void Vehicle :: fillMagically( bool ammunition, bool resources )
01108 {
01109    if ( resources )
01110       tank = getStorageCapacity();
01111 
01112    if ( ammunition ) 
01113       for ( int m = 0; m < typ->weapons.count ; m++) {
01114          ammo[m] = typ->weapons.weapon[m].count;
01115          weapstrength[m] = typ->weapons.weapon[m].maxstrength;
01116       }
01117 }
01118 
01119 
01120 
01121 
01122 #define cem_experience    0x1
01123 #define cem_damage        0x2
01124 #define cem_fuel          0x4
01125 #define cem_ammunition    0x8
01126 #define cem_weapstrength  0x10
01127 #define cem_loading       0x20
01128 #define cem_attacked      0x40
01129 #define cem_height        0x80
01130 #define cem_movement      0x100
01131 #define cem_direction     0x200
01132 #define cem_material      0x400
01133 #define cem_energy        0x800
01134 #define cem_class         0x1000
01135 #define cem_networkid     0x2000
01136 #define cem_name          0x4000
01137 #define cem_armor         0x8000
01138 #define cem_reactionfire  0x10000
01139 #define cem_reactionfire2 0x20000
01140 #define cem_poweron       0x40000
01141 #define cem_weapstrength2 0x80000
01142 #define cem_ammunition2   0x100000
01143 #define cem_energyUsed    0x200000
01144 #define cem_position      0x400000
01145 #define cem_aiparam       0x800000
01146 #define cem_version       0x1000000
01147 
01148 
01149 
01150 
01151 const int vehicleVersion = 6;
01152 
01153 void   Vehicle::write ( tnstream& stream, bool includeLoadedUnits )
01154 {
01155     stream.writeWord ( 0 );
01156     stream.writeInt( min( vehicleVersion, UNITVERSIONLIMIT ) );
01157     stream.writeInt( typ->id );
01158 
01159     stream.writeChar ( color );
01160 
01161     int bm = cem_version;
01162 
01163     if ( experience )
01164        bm |= cem_experience;
01165     if ( damage    )
01166        bm |= cem_damage;
01167 
01168     bm |= cem_energy;
01169     bm |= cem_fuel;
01170     bm |= cem_material;
01171 
01172     if ( typ->weapons.count )
01173        for (int m = 0; m < typ->weapons.count ; m++) {
01174           if ( ammo[m] < typ->weapons.weapon[m].count )
01175              bm |= cem_ammunition2;
01176           if ( weapstrength[m] != typ->weapons.weapon[m].maxstrength )
01177              bm |= cem_weapstrength2;
01178        }
01179 
01180     if ( includeLoadedUnits )
01181        for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
01182           if ( *i ) 
01183              bm |= cem_loading;
01184 
01185     if ( attacked  )
01186        bm |= cem_attacked;
01187     if ( height != chfahrend )
01188        bm |= cem_height;
01189 
01190 //    if ( _movement < typ->movement[log2(height)] )
01191     bm |= cem_movement;
01192 
01193     if ( direction )
01194        bm |= cem_direction;
01195 
01196 
01197     if ( networkid )
01198        bm |= cem_networkid;
01199 
01200     if ( !name.empty() )
01201        bm |= cem_name;
01202 
01203     if ( reactionfire.status )
01204        bm |= cem_reactionfire;
01205 
01206     if ( generatoractive )
01207        bm |= cem_poweron;
01208 
01209     if ( xpos != 0 || ypos != 0 )
01210        bm |= cem_position;
01211 
01212     for ( int i = 0; i < 8; i++ )
01213        if ( aiparam[i] )
01214           bm |= cem_aiparam;
01215 
01216 
01217 
01218     stream.writeInt( bm );
01219 
01220     stream.writeInt( min( vehicleVersion, UNITVERSIONLIMIT )  );
01221 
01222     if ( bm & cem_experience )
01223          stream.writeChar ( experience );
01224 
01225     if ( bm & cem_damage )
01226          stream.writeChar ( damage );
01227 
01228     if ( bm & cem_fuel )
01229          stream.writeInt ( tank.fuel );
01230 
01231     if ( bm & cem_ammunition2 )
01232        for ( int j= 0; j < 16; j++ )
01233          stream.writeInt ( ammo[j] );
01234 
01235     if ( bm & cem_weapstrength2 )
01236        for ( int j = 0; j < 16; j++ )
01237          stream.writeInt ( weapstrength[j] );
01238 
01239     if ( bm & cem_loading ) {
01240        int c=0;
01241        for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
01242           if ( *i ) 
01243              ++c;
01244 
01245        if ( UNITVERSIONLIMIT > 3 ) 
01246          stream.writeInt ( c );
01247        else
01248          stream.writeChar ( c );
01249 
01250        for ( Cargo::iterator i = cargo.begin(); i != cargo.end(); ++i )
01251           if ( *i ) 
01252              (*i)->write ( stream );
01253     }
01254 
01255     if ( bm & cem_height )
01256          stream.writeChar ( height );
01257 
01258     if ( bm & cem_movement )
01259          stream.writeChar ( _movement );
01260 
01261     if ( bm & cem_direction )
01262          stream.writeChar ( direction );
01263 
01264     if ( bm & cem_material )
01265          stream.writeInt ( tank.material );
01266 
01267     if ( bm & cem_energy )
01268          stream.writeInt ( tank.energy );
01269 
01270     if ( bm & cem_networkid )
01271          stream.writeInt ( networkid );
01272 
01273     if ( bm & cem_attacked )
01274          stream.writeChar ( attacked );
01275 
01276     if ( bm & cem_name     )
01277          stream.writeString ( name );
01278 
01279     if ( bm & cem_reactionfire )
01280        stream.writeChar ( reactionfire.status );
01281 
01282     if ( bm & cem_poweron )
01283        stream.writeInt ( generatoractive );
01284 
01285     if ( bm & cem_position ) {
01286        stream.writeInt ( xpos );
01287        stream.writeInt ( ypos );
01288     }
01289 
01290     if ( bm & cem_aiparam ) {
01291        stream.writeInt( 0x23451234 );
01292        for ( int i = 0; i < 8; i++ )
01293           stream.writeInt ( aiparam[i] != NULL );
01294 
01295        for ( int i = 0; i < 8; i++ )
01296           if ( aiparam[i] )
01297              aiparam[i]->write ( stream );
01298 
01299        stream.writeInt( 0x23451234 );
01300     }
01301 
01302     writeClassContainer( reactionfire.weaponShots, stream );
01303     writeClassContainer( reactionfire.nonattackableUnits, stream );
01304 
01305     if ( UNITVERSIONLIMIT >= 5 ) {
01306       stream.writeInt( internalUnitProduction.size() );
01307       for ( int i = 0; i < internalUnitProduction.size(); ++i )
01308          stream.writeInt( internalUnitProduction[i]->id );
01309     }
01310 
01311     if ( UNITVERSIONLIMIT >= 6 ) {
01312       stream.writeInt( maxresearchpoints );
01313       stream.writeInt( researchpoints );
01314       plus.write( stream );
01315       maxplus.write( stream );
01316       // actstorage.write( stream );
01317       bi_resourceplus.write( stream );
01318     }
01319 }
01320 
01321 void   Vehicle::read ( tnstream& stream )
01322 {
01323     int _id = stream.readWord ();
01324     int version = 0;
01325     if ( _id == 0 ) {
01326        version = stream.readInt();
01327        _id = stream.readInt();
01328     }
01329 
01330     stream.readChar (); // color
01331     if ( _id != typ->id )
01332        fatalError ( "Vehicle::read - trying to read a unit of different type" );
01333 
01334     readData ( stream );
01335 }
01336 
01337 void   Vehicle::readData ( tnstream& stream )
01338 {
01339 
01340     int bm = stream.readInt();
01341 
01342     int version = 0;
01343     if ( bm & cem_version )
01344        version = stream.readInt();
01345 
01346     if ( bm & cem_experience )
01347        experience = stream.readChar();
01348     else
01349