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 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
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
00211
00212 if ( amount < 0 ) {
00213 return -getResource( -amount, resourcetype, queryonly, scope, player );
00214 } else {
00215 if ( resourcetype == 0 )
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
00232
00233 }
00234
00235 return tostore;
00236 }
00237 }
00238
00239 int Vehicle :: getResource ( int amount, int resourcetype, bool queryonly, int scope, int player )
00240 {
00241
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
00260
00261 }
00262
00263 return toget;
00264 }
00265 }
00266
00267 int Vehicle :: getAvailableResource ( int amount, int resourcetype, int scope ) const
00268 {
00269
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
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
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
00472
00473
00474
00475
00476
00477
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
00571
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
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
00680
00681
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
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
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
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
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
00864
00865
00866
00867
00868
00869
00870
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
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
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 )
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
01041
01042
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
01086
01087
01088
01089
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
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
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 ();
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