00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
00200
00201 if ( amount < 0 ) {
00202 return -getResource( -amount, resourcetype, queryonly, scope, player );
00203 } else {
00204 if ( resourcetype == 0 )
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
00221
00222 }
00223
00224 return tostore;
00225 }
00226 }
00227
00228 int Vehicle :: getResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00229 {
00230
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
00249
00250 }
00251
00252 return toget;
00253 }
00254 }
00255
00256 int Vehicle :: getAvailableResource ( int amount, int resourcetype, int scope ) const
00257 {
00258
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
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
00499
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
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
00612
00613
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
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
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
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
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
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
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 ();
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
01262
01263
01264
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();
01308
01309 if ( bm & cem_armor )
01310 stream.readWord();
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 ) {
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
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
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
01499
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
01532
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
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 }