00001
00002
00003
00004
00005
00006
00007
00008
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <algorithm>
00024 #include "global.h"
00025 #include "vehicletype.h"
00026 #include "sgstream.h"
00027 #include "graphicset.h"
00028 #include "terraintype.h"
00029 #include "objecttype.h"
00030 #include "textfileparser.h"
00031 #include "textfiletags.h"
00032 #include "textfile_evaluation.h"
00033 #include "graphics/blitter.h"
00034 #include "graphics/ColorTransform_PlayerColor.h"
00035 #include "unitcostcalculator-standard.h"
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 const char* cwaffentypen[weaponTypeNum] = {"cruise missile", "mine", "bomb", "large missile", "small missile", "torpedo", "machine gun",
00071 "cannon", "service", "ammunition refuel", "laser", "shootable", "object placement"
00072 };
00073
00074 const int ammoProductionCost[weaponTypeNum][3] = { { 1500,1500,1500 },
00075 {10, 10, 10},
00076 {40, 40, 40},
00077 {200, 200, 200},
00078 {50, 50, 50},
00079 {20, 30, 40},
00080 {1, 1, 1},
00081 {5, 5, 1},
00082 {0, 0, 0},
00083 {0, 0, 0},
00084 {0, 0, 0},
00085 {0, 0, 0},
00086 {0, 0, 0}
00087 }
00088 ;
00089
00090
00091 const bool weaponAmmo[weaponTypeNum] = {
00092 true, true, true, true, true, true, true, true, false, false, false, false, false
00093 };
00094
00095 VehicleType :: VehicleType ( void )
00096 {
00097 recommendedAIJob = AiParameter::job_undefined;
00098
00099 int i;
00100
00101 armor = 0;
00102
00103 height = 0;
00104 cargoMovementDivisor = 2;
00105 wait = 0;
00106 fuelConsumption = 0;
00107
00108 movement.resize(8);
00109 for ( i = 0; i < 8; i++ )
00110 movement[i] = 0;
00111 movemalustyp = 0;
00112
00113 maxwindspeedonwater = 0;
00114 digrange = 0;
00115 initiative = 0;
00116
00117 weight = 0;
00118 bipicture = -1;
00119
00120 autorepairrate = 0;
00121
00122 heightChangeMethodNum = 0;
00123
00124 for ( i = 0; i < 8; i++ )
00125 aiparam[i] = NULL;
00126
00127 unitConstructionMoveCostPercentage = 50;
00128
00129 unitConstructionMinDistance = 1;
00130 unitConstructionMaxDistance = 1;
00131 }
00132
00133
00134 int VehicleType::maxsize ( void ) const
00135 {
00136 return weight;
00137 }
00138
00139
00140 const int vehicle_version = 33;
00141
00142
00143
00144 void VehicleType::setupRemovableObjectsFromOldFileLayout ( )
00145 {
00146 for ( vector<IntRange>::iterator i = objectsBuildable.begin(); i != objectsBuildable.end(); )
00147 if ( i->to < 0 && i->from < 0 ) {
00148 int mi = min ( -i->from, -i->to );
00149 int ma = max ( -i->from, -i->to );
00150 objectsRemovable.push_back ( IntRange ( mi, ma ));
00151 i = objectsBuildable.erase ( i );
00152 } else {
00153 objectsRemovable.push_back ( *i );
00154 i++;
00155 }
00156 }
00157
00158 void VehicleType :: read ( tnstream& stream )
00159 {
00160 int version = stream.readInt();
00161 if ( version > vehicle_version || version < 1)
00162 throw tinvalidversion ( stream.getLocation(), vehicle_version, version );
00163
00164 int j;
00165
00166 bool ___load_name = stream.readInt();
00167 bool ___load_description = stream.readInt();
00168 bool ___load_infotext = stream.readInt();
00169
00170 if ( version <= 2 ) {
00171 weapons.count = stream.readChar();
00172 for ( j = 0; j< 8; j++ ) {
00173 weapons.weapon[j].set ( stream.readWord() );
00174 weapons.weapon[j].targ = stream.readChar();
00175 weapons.weapon[j].sourceheight = stream.readChar();
00176 weapons.weapon[j].maxdistance = stream.readWord();
00177 weapons.weapon[j].mindistance = stream.readWord();
00178 weapons.weapon[j].count = stream.readChar();
00179 weapons.weapon[j].maxstrength = stream.readChar();
00180 weapons.weapon[j].minstrength = stream.readChar();
00181 for ( int k = 0; k < 13; k++ )
00182 weapons.weapon[j].efficiency[k] = 100;
00183 }
00184 }
00185
00186 if ( version <= 27 ) {
00187 productionCost.energy = stream.readWord();
00188 productionCost.material = stream.readWord();
00189 } else
00190 productionCost.read( stream );
00191
00192 armor = stream.readWord();
00193
00194 bool picture[8];
00195 for ( j = 0; j < 8; j++ )
00196 if ( version <= 18 )
00197 picture[j] = stream.readInt();
00198 else
00199 picture[j] = false;
00200
00201 height = stream.readChar();
00202 stream.readWord();
00203 int _terrain = 0;
00204 int _terrainreq = 0;
00205 int _terrainkill = 0;
00206 if ( version <= 2 ) {
00207 _terrain = stream.readInt();
00208 _terrainreq = stream.readInt();
00209 _terrainkill = stream.readInt();
00210 }
00211 stream.readChar();
00212 jamming = stream.readChar();
00213 view = stream.readWord();
00214 wait = stream.readChar();
00215
00216 if ( version <= 2 )
00217 stream.readChar ();
00218
00219 stream.readWord();
00220 stream.readWord();
00221 stream.readChar();
00222 stream.readChar();
00223 stream.readChar();
00224 if ( version <= 18 )
00225 id = stream.readWord();
00226 else
00227 id = stream.readInt();
00228
00229 if ( version < 24 ) {
00230 bi_mode_tank.resource(2) = bi_mode_tank.resource(2) = stream.readInt();
00231 fuelConsumption = stream.readWord();
00232 bi_mode_tank.resource(0) = bi_mode_tank.resource(0) = stream.readInt();
00233 bi_mode_tank.resource(1) = bi_mode_tank.resource(1) = stream.readInt();
00234 } else
00235 fuelConsumption = stream.readInt();
00236
00237 if ( version <= 22 ) {
00238 int functions = stream.readInt();
00239 features = convertOldFunctions ( functions, stream.getLocation() );
00240 }
00241
00242 for ( j = 0; j < 8; j++ )
00243 if ( version <= 4 )
00244 movement[j] = stream.readChar();
00245 else
00246 movement[j] = stream.readInt();
00247
00248
00249 movemalustyp = stream.readChar();
00250 if ( movemalustyp >= cmovemalitypenum )
00251 movemalustyp = cmovemalitypenum -1;
00252
00253 if ( version <= 2 )
00254 for ( j = 0; j < 9; j++ )
00255 stream.readWord( );
00256
00257 bool ___load_classnames[8];
00258 if ( version <= 15 ) {
00259 stream.readChar();
00260
00261 for ( j = 0; j < 8; j++ )
00262 ___load_classnames[j] = stream.readInt();
00263
00264 for ( j = 0; j < 8; j++ ) {
00265 int k;
00266 for ( k = 0; k < 8; k++ )
00267 stream.readWord();
00268
00269 if ( version <= 2 )
00270 stream.readWord ( );
00271
00272 stream.readWord();
00273 stream.readWord();
00274 for ( k = 0; k < 4; k++ )
00275 stream.readWord();
00276
00277 stream.readChar();
00278 stream.readInt();
00279 if ( version <= 2 )
00280 stream.readChar( );
00281 }
00282 }
00283
00284 maxwindspeedonwater = stream.readChar();
00285 digrange = stream.readChar();
00286 initiative = stream.readInt();
00287 int _terrainnot = 0;
00288 if ( version <= 4 ) {
00289 _terrainnot = stream.readInt();
00290 stream.readInt();
00291 }
00292 int objectsbuildablenum = stream.readInt();
00293 if ( version <= 4 )
00294 stream.readInt();
00295
00296 weight = stream.readInt();
00297
00298 bool ___loadterrainaccess = false;
00299 if ( version <= 4 )
00300 ___loadterrainaccess = stream.readInt();
00301
00302 bipicture = stream.readInt();
00303 int vehiclesbuildablenum = stream.readInt();
00304 if ( version <= 4 )
00305 stream.readInt();
00306
00307 if ( version <= 4 )
00308 stream.readInt();
00309
00310 int buildingsbuildablenum = stream.readInt();
00311 if ( version <= 4 )
00312 stream.readInt();
00313
00314 bool load_weapons = stream.readInt();
00315 autorepairrate = stream.readInt();
00316 if ( version >= 15 )
00317 readClassContainer ( wreckageObject, stream );
00318 else
00319 if ( version >= 8 ) {
00320 wreckageObject.clear();
00321 wreckageObject.push_back ( stream.readInt() );
00322 }
00323
00324
00325 if ( version <= 2 )
00326 stream.readInt( );
00327
00328 if ( version >= 4 )
00329 stream.readInt();
00330
00331 if (___load_name)
00332 name = stream.readString( true );
00333
00334 if (___load_description)
00335 description = stream.readString( true );
00336
00337 if (___load_infotext)
00338 infotext = stream.readString ( true );
00339
00340 int i;
00341 if ( version <= 15 )
00342 for ( i=0;i<8 ;i++ )
00343 if ( ___load_classnames[i] )
00344 stream.readString ( true );
00345
00346 if ( hasFunction( AutoRepair ) )
00347 if ( !autorepairrate )
00348 autorepairrate = 10;
00349
00350 if ( version <= 18 ) {
00351 if ( picture[0] )
00352 image.read ( stream );
00353
00354 for ( int i=1;i<8 ;i++ ) {
00355 if ( picture[i] ) {
00356 Surface s;
00357 s.read ( stream );
00358 bipicture = 0;
00359 }
00360 }
00361 } else {
00362 image.read ( stream );
00363 }
00364
00365
00366 if ( objectsbuildablenum )
00367 for ( i = 0; i < objectsbuildablenum; i++ ) {
00368 int from = stream.readInt ( );
00369 int to;
00370 if ( version <= 4 )
00371 to = from;
00372 else
00373 to = stream.readInt();
00374
00375 objectsBuildable.push_back ( IntRange ( from, to ));
00376 }
00377
00378 if ( version >= 6 ) {
00379 int num = stream.readInt();
00380 for ( int i = 0; i < num; i++ ) {
00381 int from = stream.readInt();
00382 int to = stream.readInt();
00383
00384 objectsRemovable.push_back ( IntRange ( from, to ));
00385 }
00386 } else
00387 setupRemovableObjectsFromOldFileLayout();
00388
00389 if ( version >= 8 ) {
00390 int num = stream.readInt();
00391 for ( int i = 0; i < num; i++ ) {
00392 int from = stream.readInt();
00393 int to = stream.readInt();
00394
00395 objectGroupsBuildable.push_back ( IntRange ( from, to ));
00396 }
00397
00398 num = stream.readInt();
00399 for ( int i = 0; i < num; i++ ) {
00400 int from = stream.readInt();
00401 int to = stream.readInt();
00402
00403 objectGroupsRemovable.push_back ( IntRange ( from, to ));
00404 }
00405 }
00406
00407 if ( vehiclesbuildablenum )
00408 for ( i = 0; i < vehiclesbuildablenum; i++ ) {
00409 int from = stream.readInt ( );
00410 int to;
00411 if ( version <= 4 )
00412 to = from;
00413 else
00414 to = stream.readInt();
00415
00416 vehiclesBuildable.push_back ( IntRange ( from, to ));
00417 }
00418
00419 if ( load_weapons && version > 1) {
00420 weapons.count = stream.readInt();
00421 for ( j = 0; j< 16; j++ ) {
00422 weapons.weapon[j].set ( stream.readInt() );
00423 weapons.weapon[j].targ = stream.readInt();
00424 weapons.weapon[j].sourceheight = stream.readInt();
00425 weapons.weapon[j].maxdistance = stream.readInt();
00426 weapons.weapon[j].mindistance = stream.readInt();
00427 weapons.weapon[j].count = stream.readInt();
00428 weapons.weapon[j].maxstrength = stream.readInt();
00429 weapons.weapon[j].minstrength = stream.readInt();
00430
00431 if ( version >= 17 )
00432 weapons.weapon[j].reactionFireShots = stream.readInt();
00433
00434 if ( version >= 22 )
00435 weapons.weapon[j].name = stream.readString();
00436
00437
00438
00439
00440
00441
00442
00443 for ( int k = 0; k < 13; k++ )
00444 weapons.weapon[j].efficiency[k] = stream.readInt();
00445
00446 if ( version <= 6 ) {
00447 int targets_not_hittable = stream.readInt();
00448 for ( int i = 0; i < cmovemalitypenum; i++ )
00449 if ( targets_not_hittable & ( 1 << i))
00450 weapons.weapon[j].targetingAccuracy[i] = 0;
00451 } else {
00452 int num = stream.readInt();
00453 for ( int i = 0; i < num; i++ )
00454 weapons.weapon[j].targetingAccuracy[i] = stream.readInt();
00455 }
00456
00457
00458 if ( version <= 2 )
00459 for ( int l = 0; l < 9; l++ )
00460 stream.readInt();
00461
00462 if ( version >= 11 ) {
00463 weapons.weapon[j].laserRechargeRate = stream.readInt();
00464 weapons.weapon[j].laserRechargeCost.read( stream );
00465 }
00466 if ( version >= 20 )
00467 weapons.weapon[j].soundLabel = stream.readString();
00468
00469 }
00470
00471 if ( version <= 2 )
00472 for ( int m = 0; m< 10; m++ )
00473 stream.readInt();
00474
00475 }
00476
00477 if ( ___loadterrainaccess || version >= 5 )
00478 terrainaccess.read ( stream );
00479 else {
00480 terrainaccess.terrain.setInt ( _terrain, 0 );
00481 terrainaccess.terrainreq.setInt ( _terrainreq, 0 );
00482 terrainaccess.terrainnot.setInt ( _terrainnot, 0 );
00483 terrainaccess.terrainkill.setInt ( _terrainkill, 0 );
00484 }
00485
00486 if ( buildingsbuildablenum )
00487 for ( i = 0; i < buildingsbuildablenum; i++ ) {
00488 int from = stream.readInt();
00489 int to = stream.readInt();
00490 buildingsBuildable.push_back ( IntRange ( from, to ));
00491 }
00492
00493
00494 filename = stream.getDeviceName();
00495 location = stream.getLocation();
00496
00497 if ( version >= 9 )
00498 ContainerBaseType::read ( stream );
00499
00500 if ( version >= 10 ) {
00501 heightChangeMethodNum = stream.readInt();
00502 heightChangeMethod.resize(heightChangeMethodNum );
00503 for ( int i = 0; i < heightChangeMethodNum; i++ )
00504 heightChangeMethod[i].read( stream );
00505 } else
00506 heightChangeMethodNum = 0;
00507
00508 if ( version >= 12 )
00509 techDependency.read( stream );
00510
00511 if ( version >= 13 && version <= 24 ) {
00512 Surface s;
00513 s.read( stream );
00514 }
00515
00516 if ( version >= 14 && version < 18)
00517 cargoMovementDivisor = stream.readInt();
00518 else
00519 if ( version >= 18 )
00520 cargoMovementDivisor = stream.readFloat();
00521
00522 if ( version >= 20 ) {
00523 movementSoundLabel = stream.readString();
00524 killSoundLabel = stream.readString();
00525 }
00526
00527 if ( version >= 22 )
00528 readClassContainer( guideSortHelp, stream );
00529
00530 if ( version >= 24 ) {
00531 efficiencyfuel = stream.readInt( );
00532 efficiencymaterial = stream.readInt( );
00533 asc_mode_tank.read( stream );
00534 bi_mode_tank.read( stream );
00535
00536
00537 maxresearchpoints = stream.readInt();
00538 defaultMaxResearchpoints = stream.readInt();
00539 nominalresearchpoints = stream.readInt();
00540 maxplus.read( stream );
00541 defaultProduction.read( stream );
00542 }
00543
00544 for ( int w = 0; w < weapons.count; ++w )
00545 if ( weapons.weapon[w].canRefuel() )
00546 setFunction( ExternalAmmoTransfer );
00547
00548 if ( version >= 26 ) {
00549 jumpDrive.height = stream.readInt();
00550 jumpDrive.targetterrain.read( stream );
00551 jumpDrive.consumption.read( stream );
00552 jumpDrive.maxDistance = stream.readInt();
00553 }
00554
00555 if ( version >= 27 ) {
00556 int num = stream.readInt();
00557 for ( int i = 0; i < num; i++ ) {
00558 int from = stream.readInt();
00559 int to = stream.readInt();
00560 objectLayedByMovement.push_back ( IntRange ( from, to ));
00561 }
00562 }
00563
00564 if ( version >= 29 )
00565 unitConstructionMoveCostPercentage = stream.readInt();
00566
00567 if ( version >= 30 ) {
00568 unitConstructionMinDistance = stream.readInt();
00569 unitConstructionMaxDistance = stream.readInt();
00570 }
00571
00572 if ( version >= 31 )
00573 imageFilename = stream.readString();
00574
00575 if ( version >= 32 )
00576 recommendedAIJob = AiParameter::Job(stream.readInt());
00577
00578 if ( version >= 33 )
00579 costCalculator = stream.readString();
00580 }
00581
00582
00583
00584
00585 void VehicleType:: write ( tnstream& stream ) const
00586 {
00587 int i,j;
00588
00589 stream.writeInt ( vehicle_version );
00590
00591 if ( !name.empty() )
00592 stream.writeInt( 1 );
00593 else
00594 stream.writeInt( 0 );
00595
00596 if ( !description.empty() )
00597 stream.writeInt( 1 );
00598 else
00599 stream.writeInt( 0 );
00600
00601 if ( !infotext.empty() )
00602 stream.writeInt( 1 );
00603 else
00604 stream.writeInt( 0 );
00605
00606 productionCost.write( stream );
00607 stream.writeWord( armor );
00608 stream.writeChar( height );
00609 stream.writeWord(0);
00610 stream.writeChar(0);
00611 stream.writeChar(jamming);
00612 stream.writeWord(view);
00613 stream.writeChar(wait);
00614 stream.writeWord(0);
00615 stream.writeWord(0);
00616 stream.writeChar(0);
00617 stream.writeChar(0);
00618 stream.writeChar(0);
00619 stream.writeInt(id );
00620 stream.writeInt(fuelConsumption );
00621 for ( j = 0; j < 8; j++ )
00622 stream.writeInt( movement[j] );
00623
00624
00625 stream.writeChar(movemalustyp );
00626
00627 stream.writeChar(maxwindspeedonwater );
00628 stream.writeChar(digrange );
00629 stream.writeInt(initiative );
00630 stream.writeInt( objectsBuildable.size() );
00631
00632 stream.writeInt(weight );
00633
00634 stream.writeInt(bipicture );
00635 stream.writeInt(vehiclesBuildable.size() );
00636 stream.writeInt( buildingsBuildable.size() );
00637 stream.writeInt( 1 );
00638
00639 stream.writeInt( autorepairrate );
00640 writeClassContainer( wreckageObject, stream );
00641
00642 stream.writeInt( 0 );
00643
00644 if ( !name.empty() )
00645 stream.writeString( name );
00646
00647 if ( !description.empty() )
00648 stream.writeString( description );
00649
00650 if ( !infotext.empty() )
00651 stream.writeString( infotext );
00652
00653 image.write( stream );
00654
00655 for ( i = 0; i < objectsBuildable.size(); i++ ) {
00656 stream.writeInt ( objectsBuildable[i].from );
00657 stream.writeInt ( objectsBuildable[i].to );
00658 }
00659
00660 stream.writeInt ( objectsRemovable.size() );
00661 for ( i = 0; i < objectsRemovable.size(); i++ ) {
00662 stream.writeInt ( objectsRemovable[i].from );
00663 stream.writeInt ( objectsRemovable[i].to );
00664 }
00665
00666 stream.writeInt ( objectGroupsBuildable.size() );
00667 for ( i = 0; i < objectGroupsBuildable.size(); i++ ) {
00668 stream.writeInt ( objectGroupsBuildable[i].from );
00669 stream.writeInt ( objectGroupsBuildable[i].to );
00670 }
00671
00672 stream.writeInt ( objectGroupsRemovable.size() );
00673 for ( i = 0; i < objectGroupsRemovable.size(); i++ ) {
00674 stream.writeInt ( objectGroupsRemovable[i].from );
00675 stream.writeInt ( objectGroupsRemovable[i].to );
00676 }
00677
00678
00679 for ( i = 0; i < vehiclesBuildable.size(); i++ ) {
00680 stream.writeInt ( vehiclesBuildable[i].from );
00681 stream.writeInt ( vehiclesBuildable[i].to );
00682 }
00683
00684 stream.writeInt(weapons.count );
00685 for ( j = 0; j< 16; j++ ) {
00686 stream.writeInt(weapons.weapon[j].gettype( ));
00687 stream.writeInt(weapons.weapon[j].targ);
00688 stream.writeInt(weapons.weapon[j].sourceheight );
00689 stream.writeInt(weapons.weapon[j].maxdistance );
00690 stream.writeInt(weapons.weapon[j].mindistance );
00691 stream.writeInt(weapons.weapon[j].count );
00692 stream.writeInt(weapons.weapon[j].maxstrength );
00693 stream.writeInt(weapons.weapon[j].minstrength );
00694 stream.writeInt(weapons.weapon[j].reactionFireShots );
00695 stream.writeString( weapons.weapon[j].name );
00696
00697 for ( int k = 0; k < 13; k++ )
00698 stream.writeInt(weapons.weapon[j].efficiency[k] );
00699
00700 stream.writeInt ( cmovemalitypenum );
00701 for ( int i = 0; i < cmovemalitypenum; i++ )
00702 stream.writeInt(weapons.weapon[j].targetingAccuracy[i] );
00703
00704 stream.writeInt( weapons.weapon[j].laserRechargeRate );
00705 weapons.weapon[j].laserRechargeCost.write( stream );
00706 stream.writeString( weapons.weapon[j].soundLabel );
00707 }
00708
00709 terrainaccess.write ( stream );
00710
00711 for ( i = 0; i < buildingsBuildable.size(); i++ ) {
00712 stream.writeInt( buildingsBuildable[i].from );
00713 stream.writeInt( buildingsBuildable[i].to );
00714 }
00715
00716 ContainerBaseType::write ( stream );
00717
00718
00719 stream.writeInt( heightChangeMethodNum );
00720 for ( i = 0; i < heightChangeMethodNum; i++ )
00721 heightChangeMethod[i].write( stream );
00722
00723 techDependency.write( stream );
00724
00725 stream.writeFloat ( cargoMovementDivisor );
00726
00727 stream.writeString( movementSoundLabel );
00728 stream.writeString( killSoundLabel );
00729 writeClassContainer( guideSortHelp, stream );
00730
00731 stream.writeInt( efficiencyfuel );
00732 stream.writeInt( efficiencymaterial );
00733
00734 asc_mode_tank.write( stream );
00735 bi_mode_tank.write( stream );
00736
00737 stream.writeInt( maxresearchpoints );
00738 stream.writeInt( defaultMaxResearchpoints );
00739 stream.writeInt( nominalresearchpoints );
00740 maxplus.write ( stream );
00741 defaultProduction.write( stream );
00742
00743 stream.writeInt( jumpDrive.height );
00744 jumpDrive.targetterrain.write( stream );
00745 jumpDrive.consumption.write( stream );
00746 stream.writeInt( jumpDrive.maxDistance );
00747
00748 stream.writeInt ( objectLayedByMovement.size() );
00749 for ( i = 0; i < objectLayedByMovement.size(); i++ ) {
00750 stream.writeInt ( objectLayedByMovement[i].from );
00751 stream.writeInt ( objectLayedByMovement[i].to );
00752 }
00753
00754 stream.writeInt( unitConstructionMoveCostPercentage );
00755 stream.writeInt( unitConstructionMinDistance );
00756 stream.writeInt( unitConstructionMaxDistance );
00757 stream.writeString( imageFilename );
00758 stream.writeInt( recommendedAIJob );
00759
00760 stream.writeString( costCalculator );
00761 }
00762
00763
00764 ASCString VehicleType::getName( ) const
00765 {
00766 if ( !name.empty() )
00767 return name;
00768 else
00769 return description;
00770 }
00771
00772 int VehicleType :: maxSpeed ( ) const
00773 {
00774 int maxUnitMovement = 0;
00775 for ( int i = 0; i < 8; i++ )
00776 maxUnitMovement = max ( maxUnitMovement, movement[i] );
00777 return maxUnitMovement;
00778 }
00779
00780 int VehicleType::getMemoryFootprint() const
00781 {
00782 return sizeof(*this) + image.getMemoryFootprint();
00783 }
00784
00785
00786 VehicleType :: ~VehicleType ( )
00787 {
00788 for ( int i = 0; i < 8; i++ )
00789 if ( aiparam[i] ) {
00790 delete aiparam[i];
00791 aiparam[i] = NULL;
00792 }
00793 }
00794
00795
00796
00797 SingleWeapon::SingleWeapon()
00798 {
00799 typ = 0;
00800 targ = 0;
00801 sourceheight = 0;
00802 maxdistance = 0;
00803 mindistance = 0;
00804 count = 0;
00805 maxstrength= 0;
00806 minstrength = 0;
00807 laserRechargeRate = 0;
00808 for ( int i = 0; i < 13; i++ )
00809 efficiency[i] = 0;
00810 for ( int i = 0; i < cmovemalitypenum; i++ )
00811 targetingAccuracy[i] = 100;
00812
00813 reactionFireShots = 1;
00814 }
00815
00816
00817
00818
00819
00820 int SingleWeapon::getScalarWeaponType(void) const
00821 {
00822 if ( typ & (cwweapon | cwmineb) )
00823 return getFirstBit ( typ & (cwweapon | cwmineb) );
00824 else
00825 return -1;
00826 }
00827
00828
00829 bool SingleWeapon::requiresAmmo(void) const
00830 {
00831 if ( typ & cwlaserb )
00832 return false;
00833 else
00834 return typ & ( cwweapon | cwmineb );
00835 }
00836
00837 bool SingleWeapon::shootable( void ) const
00838 {
00839 return typ & cwshootableb;
00840 }
00841
00842 bool SingleWeapon::offensive( void ) const
00843 {
00844 return typ & cwweapon;
00845 }
00846
00847 bool SingleWeapon::service( void ) const
00848 {
00849 return typ & cwserviceb;
00850 }
00851
00852 bool SingleWeapon::placeObjects( ) const
00853 {
00854 return typ & cwobjectplacementb;
00855 }
00856
00857
00858 bool SingleWeapon::canRefuel( void ) const
00859 {
00860 return typ & cwammunitionb;
00861 }
00862
00863 void SingleWeapon::set
00864 ( int type )
00865 {
00866 typ = type;
00867 }
00868
00869 ASCString SingleWeapon::getName ( void ) const
00870 {
00871 if ( !name.empty() )
00872 return name;
00873
00874 ASCString s;
00875
00876 int k = getScalarWeaponType();
00877 if ( k < weaponTypeNum && k >= 0 )
00878 s = cwaffentypen[k];
00879 else
00880 if ( service() || placeObjects() )
00881 s = cwaffentypen[cwservicen];
00882 else
00883 s = "undefined";
00884
00885 return s;
00886 }
00887
00888 ASCString SingleWeapon::getIconFileName( int numerical )
00889 {
00890 switch ( numerical ) {
00891 case cwcruisemissile:
00892 return "weap-cruisemissile";
00893 case cwbombn:
00894 return "weap-bomb";
00895 case cwlargemissilen:
00896 return "weap-bigmissile";
00897 case cwsmallmissilen:
00898 return "weap-smallmissile";
00899 case cwtorpedon:
00900 return "weap-torpedo";
00901 case cwmachinegunn:
00902 return "weap-machinegun";
00903 case cwcannonn:
00904 return "weap-cannon";
00905 case cwminen:
00906 return "weap-mine";
00907 case cwservicen:
00908 return "weap-service";
00909 case cwlasern:
00910 return "weap-laser";
00911 default:
00912 return "weap-undefined";
00913 };
00914 }
00915
00916
00917 bool SingleWeapon::equals( const SingleWeapon* otherWeapon ) const
00918 {
00919 if (
00920 otherWeapon->targ == this->targ &&
00921 otherWeapon->sourceheight == this->sourceheight &&
00922 otherWeapon->maxdistance == this->maxdistance &&
00923 otherWeapon->mindistance == this->mindistance &&
00924 otherWeapon->count == this->count &&
00925 otherWeapon->maxstrength == this->maxstrength &&
00926 otherWeapon->minstrength == this->minstrength &&
00927 otherWeapon->gettype() == this->gettype()
00928 ) {
00929 bool equal = true;
00930 for ( int i=0; i<13; i++ ) {
00931 if ( otherWeapon->efficiency[ i ] != this->efficiency[ i ] ) {
00932 equal = false;
00933 }
00934 }
00935 if ( equal ) return true;
00936 }
00937
00938 return false;
00939 }
00940
00941
00942 UnitWeapon :: UnitWeapon ( void )
00943 {
00944 count = 0;
00945 }
00946
00947
00948
00949
00950 void VehicleType::runTextIO ( PropertyContainer& pc )
00951 {
00952 ContainerBaseType::runTextIO ( pc );
00953
00954 pc.addInteger( "Armor", armor );
00955
00956 ASCString fn;
00957 if ( filename.empty() ) {
00958 fn += "vehicle";
00959 fn += strrr(id);
00960 } else
00961 fn = extractFileName_withoutSuffix( filename );
00962
00963 pc.addImage( "Picture", image, fn, true );
00964 if ( pc.isReading() )
00965 imageFilename = fn;
00966 else
00967 pc.addString("OriginalImageFilename", imageFilename );
00968
00969 if ( image.w() < fieldsizex || image.h() < fieldsizey )
00970 image.strech( fieldsizex, fieldsizey );
00971
00972 image.assignDefaultPalette();
00973
00974 pc.addTagInteger( "Height", height, choehenstufennum, heightTags );
00975 pc.addBool ( "WaitForAttack", wait );
00976
00977 if ( bi_mode_tank == Resources(0,0,0) && asc_mode_tank == Resources(0,0,0)) {
00978 pc.openBracket( "Tank" );
00979 Resources tank;
00980 tank.runTextIO ( pc );
00981 pc.closeBracket();
00982 bi_mode_tank = tank;
00983 asc_mode_tank = tank;
00984 }
00985
00986 pc.addInteger( "FuelConsumption", fuelConsumption );
00987 if ( !pc.find("Features") && pc.isReading() ) {
00988 int abilities;
00989 pc.addTagInteger ( "Abilities", abilities, legacyVehicleFunctionNum, vehicleAbilities );
00990
00991 features = convertOldFunctions ( abilities, pc.getFileName() );
00992
00993 } else
00994 pc.addTagArray ( "Features", features, functionNum, containerFunctionTags );
00995
00996
00997 pc.addIntegerArray ( "Movement", movement );
00998 while ( movement.size() < 8 )
00999 movement.push_back(0);
01000 for ( vector<int>::iterator i = movement.begin(); i != movement.end(); i++ )
01001 if ( *i > 350 )
01002 *i = 350;
01003
01004 pc.addNamedInteger ( "Category", movemalustyp, cmovemalitypenum, unitCategoryTags );
01005 pc.addInteger("MaxSurvivableStorm", maxwindspeedonwater, 255 );
01006 pc.addInteger("ResourceDrillingRange", digrange, 0 );
01007 pc.addInteger("SelfRepairRate", autorepairrate, 0 );
01008 if ( pc.find ( "WreckageObject" ) || !pc.isReading() )
01009 pc.addIntegerArray("WreckageObject", wreckageObject );
01010
01011
01012 if ( pc.find( "CargoMovementDivisor" ))
01013 pc.addDFloat("CargoMovementDivisor", cargoMovementDivisor);
01014 else {
01015 pc.openBracket( "Transportation" );
01016 pc.addDFloat("CargoMovementDivisor", cargoMovementDivisor, 2 );
01017 pc.closeBracket();
01018 }
01019
01020
01021 pc.addInteger("Weight", weight);
01022 pc.openBracket("TerrainAccess" );
01023 terrainaccess.runTextIO ( pc );
01024 pc.closeBracket();
01025
01026 pc.openBracket ( "Construction" );
01027 pc.addIntRangeArray ( "Buildings", buildingsBuildable );
01028 pc.addIntRangeArray ( "Vehicles", vehiclesBuildable );
01029 pc.addIntRangeArray ( "Objects", objectsBuildable );
01030 if ( pc.isReading() ) {
01031 if ( pc.find ( "ObjectsRemovable" ))
01032 pc.addIntRangeArray ( "ObjectsRemovable", objectsRemovable );
01033 else
01034 setupRemovableObjectsFromOldFileLayout();
01035 } else
01036 pc.addIntRangeArray ( "ObjectsRemovable", objectsRemovable );
01037
01038 pc.addIntRangeArray ( "ObjectGroupsBuildable", objectGroupsBuildable, false );
01039 pc.addIntRangeArray ( "ObjectGroupsRemovable", objectGroupsRemovable, false );
01040 pc.addInteger("UnitConstructionMoveCostPercentage", unitConstructionMoveCostPercentage, 50);
01041 pc.addInteger("UnitConstructionMinDistance", unitConstructionMinDistance, 1 );
01042 pc.addInteger("UnitConstructionMaxDistance", unitConstructionMaxDistance, 1 );
01043 pc.closeBracket();
01044
01045 pc.openBracket ( "Weapons");
01046
01047 pc.addInteger("Number", weapons.count );
01048 for ( int i = 0; i < weapons.count; i++ ) {
01049 pc.openBracket ( ASCString("Weapon")+strrr(i) );
01050 weapons.weapon[i].runTextIO( pc );
01051 pc.closeBracket();
01052 if ( hasFunction( NoReactionfire ) )
01053 weapons.weapon[i].reactionFireShots = 0;
01054
01055 }
01056
01057 pc.closeBracket();
01058
01059 int job = recommendedAIJob;
01060 pc.addNamedInteger ( "AIJobOverride", job, AiParameter::jobNum, AIjobs, AiParameter::job_undefined );
01061 recommendedAIJob = AiParameter::Job(job);
01062
01063 pc.addString("MovementSound", movementSoundLabel, "" );
01064 pc.addString("KillSound", killSoundLabel, "" );
01065
01066 pc.addInteger("HeightChangeMethodNum", heightChangeMethodNum, 0 );
01067 heightChangeMethod.resize( heightChangeMethodNum );
01068 for ( int i = 0; i < heightChangeMethodNum; i++ ) {
01069 pc.openBracket( ASCString("HeightChangeMethod")+strrr(i) );
01070 heightChangeMethod[i].runTextIO ( pc );
01071 pc.closeBracket();
01072 }
01073
01074 techDependency.runTextIO( pc, strrr(id) );
01075
01076
01077
01078 pc.openBracket ( "ConstructionCost" );
01079 productionCost.runTextIO ( pc );
01080 int costCalcMethod = 0;
01081 pc.addNamedInteger( "CalculationMethod", costCalcMethod, productionCostCalculationMethodNum, productionCostCalculationMethod, 0 );
01082 if ( pc.isReading() ) {
01083 if ( !pc.find ( "material" ) && costCalcMethod == 0)
01084 costCalcMethod = 1;
01085 }
01086
01087 pc.addString( "Calculator", costCalculator, "standard" );
01088
01089 if ( costCalcMethod == 1 )
01090 productionCost = calcProductionCost();
01091
01092 if ( costCalcMethod == 2 )
01093 productionCost += calcProductionCost();
01094
01095 if ( costCalcMethod != 0 ) {
01096 displayLogMessage ( 4, "unit %s id %d has a production cost of %d E; %d M; %d F \n", name.c_str(), id, productionCost.energy, productionCost.material, productionCost.fuel );
01097 }
01098
01099 pc.closeBracket ();
01100
01101 if ( pc.find( "guideSortHelp") || !pc.isReading() )
01102 pc.addIntegerArray("guideSortHelp", guideSortHelp );
01103
01104 bool hasService = false;
01105 for ( int w = 0; w < weapons.count; ++w ) {
01106 if ( weapons.weapon[w].canRefuel() )
01107 setFunction( ExternalAmmoTransfer );
01108 if ( weapons.weapon[w].service() )
01109 hasService = true;
01110 }
01111
01112 if ( !hasService ) {
01113 features.reset( ExternalRepair );
01114 features.reset( ExternalEnergyTransfer );
01115 features.reset( ExternalMaterialTransfer );
01116 features.reset( ExternalFuelTransfer );
01117 features.reset( ExternalAmmoTransfer );
01118 }
01119
01120
01121
01122 pc.openBracket ( "JumpDrive" );
01123 pc.addTagInteger( "Height", jumpDrive.height, choehenstufennum, heightTags, 0 );
01124 pc.openBracket ( "consumption" );
01125 jumpDrive.consumption.runTextIO ( pc, Resources(0,0,0) );
01126 pc.closeBracket();
01127 if ( jumpDrive.height || !pc.isReading() )
01128 jumpDrive.targetterrain.runTextIO ( pc );
01129 pc.addInteger( "MaxDistance", jumpDrive.maxDistance, maxint );
01130 pc.closeBracket();
01131
01132 if ( jumpDrive.height && view )
01133 pc.error( "only units without radar may have a jump drive." );
01134
01135 if ( !pc.isReading() || pc.find ( "ObjectsLayedByMovement" ))
01136 pc.addIntRangeArray ( "ObjectsLayedByMovement", objectLayedByMovement );
01137 else
01138 objectLayedByMovement.clear();
01139
01140 if ( hasFunction( ContainerBaseType::IceBreaker ))
01141 objectLayedByMovement.push_back ( 6 );
01142
01143 if ( hasFunction( ContainerBaseType::MakesTracks ))
01144 objectLayedByMovement.push_back ( 7 );
01145
01146
01147 }
01148
01149 BitSet VehicleType::convertOldFunctions( int abilities, const ASCString& location )
01150 {
01151 BitSet features;
01152 if ( abilities & 1 )
01153 features.set( Sonar );
01154 if ( abilities & 2 )
01155 features.set( Paratrooper );
01156 if ( abilities & 4 )
01157 features.set( PlaceMines );
01158 if ( abilities & 8 )
01159 features.set( CruiserLanding );
01160 if ( abilities & 16 ) {
01161 features.set( ExternalRepair );
01162 features.set( InternalUnitRepair );
01163 }
01164 if ( abilities & 32 )
01165 features.set( ConquerBuildings );
01166 if ( abilities & 64 )
01167 features.set( MoveAfterAttack );
01168 if ( abilities & 128 )
01169 features.set( SatelliteView );
01170 if ( abilities & 256 )
01171 errorMessage( location + ": The features construct_ALL_buildings is not supported any more");
01172 if ( abilities & 512 )
01173 features.set( MineView );
01174 if ( abilities & 1024 )
01175 features.set( ExternalVehicleProduction );
01176 if ( abilities & 2048 )
01177 features.set( ConstructBuildings );
01178 if ( abilities & 4096 )
01179 features.set( ExternalFuelTransfer );
01180 if ( abilities & 8192 )
01181 features.set( IceBreaker );
01182 if ( abilities & 16384 )
01183 features.set( NoInairRefuelling );
01184 if ( abilities & 32768 )
01185 features.set( ExternalMaterialTransfer );
01186 if ( abilities & (1 << 17) )
01187 features.set( MakesTracks );
01188 if ( abilities & (1 << 18) )
01189 features.set( DetectsMineralResources );
01190 if ( abilities & (1 << 19) )
01191 features.set( NoReactionfire );
01192 if ( abilities & (1 << 20) )
01193 features.set( AutoRepair );
01194 if ( abilities & (1 << 21) )
01195 features.set( MatterConverter );
01196
01197
01198 if ( abilities & (1 << 22) )
01199 features.set( DetectsMineralResources );
01200 if ( abilities & (1 << 23) )
01201 features.set( KamikazeOnly );
01202 if ( abilities & (1 << 24) )
01203 features.set( ImmuneToMines );
01204 if ( abilities & (1 << 25) )
01205 features.set( ExternalEnergyTransfer );
01206 if ( abilities & (1 << 26) )
01207 features.set( JamsOnlyOwnField );
01208 if ( abilities & (1 << 27) )
01209 features.set( MoveWithReactionFire );
01210 if ( abilities & (1 << 28) )
01211 features.set( OnlyMoveToAndFromTransports );
01212 return features;
01213 }
01214
01215 void VehicleType::paint ( Surface& s, SPoint pos, const PlayerColor& player, int direction ) const
01216 {
01217 megaBlitter<ColorTransform_PlayerCol,ColorMerger_AlphaOverwrite,SourcePixelSelector_Plain,TargetPixelSelector_All>( getImage(), s, pos, player, nullParam, nullParam, nullParam );
01218 }
01219
01220 void VehicleType::paint ( Surface& s, SPoint pos ) const
01221 {
01222 megaBlitter<ColorTransform_None,ColorMerger_AlphaOverwrite,SourcePixelSelector_Plain,TargetPixelSelector_All>( getImage(), s, pos, nullParam, nullParam, nullParam, nullParam );
01223 }
01224
01225
01226 void SingleWeapon::runTextIO ( PropertyContainer& pc )
01227 {
01228 pc.addTagInteger( "Type", typ, weaponTypeNum, weaponTags );
01229 pc.addTagInteger( "targets", targ, choehenstufennum, heightTags );
01230 pc.addTagInteger( "shotFrom", sourceheight, choehenstufennum, heightTags );
01231 pc.addInteger("MaxRange", maxdistance );
01232 pc.addInteger("MinRange", mindistance );
01233 pc.addInteger("Ammo", count );
01234 pc.addInteger("Punch@MaxRange", minstrength );
01235 pc.addInteger("Punch@MinRange", maxstrength );
01236 pc.addString("Sound", soundLabel, "");
01237 pc.addInteger("LaserRechargeRate", laserRechargeRate, 0 );
01238 pc.openBracket( "laserRechargeCost" );
01239 laserRechargeCost.runTextIO ( pc, Resources(0,0,0) );
01240 pc.closeBracket();
01241
01242 pc.addInteger("ReactionFireShots", reactionFireShots, 1 );
01243 if ( getScalarWeaponType() == cwminen && reactionFireShots > 0 )
01244 warningMessage(pc.getFileName() + " has a mine with Reactionfire. This doesn't make sense.");
01245
01246 pc.openBracket("HitAccuracy" );
01247 {
01248 for ( int j = 0; j < 13; j++ )
01249 if ( j < 6 )
01250 pc.addInteger( ASCString("d")+strrr(abs(j-6)), efficiency[j] );
01251 else
01252 if ( j == 6 )
01253 pc.addInteger( "0", efficiency[j] );
01254 else
01255 pc.addInteger( ASCString("u")+strrr(j-6), efficiency[j] );
01256 }
01257 pc.closeBracket();
01258
01259 if ( pc.isReading() ) {
01260 if ( pc.find ( "cantHit" )) {
01261 int targets_not_hittable;
01262 pc.addTagInteger( "cantHit", targets_not_hittable, cmovemalitypenum, unitCategoryTags );
01263 for ( int i = 0; i < cmovemalitypenum; i++ )
01264 if ( targets_not_hittable & ( 1 << i ))
01265 targetingAccuracy[i] = 0;
01266 }
01267 pc.openBracket("WeaponEffectiveness" );
01268 for ( int i = 0; i < cmovemalitypenum; i++ ) {
01269 if ( pc.find ( unitCategoryTags[i] ))
01270 pc.addInteger( unitCategoryTags[i], targetingAccuracy[i] );
01271 }
01272 pc.closeBracket();
01273 } else {
01274 pc.openBracket("WeaponEffectiveness" );
01275 for ( int i = 0; i < cmovemalitypenum; i++ )
01276 if ( targetingAccuracy[i] != 100 )
01277 pc.addInteger( unitCategoryTags[i], targetingAccuracy[i] );
01278 pc.closeBracket();
01279 }
01280 pc.addString("name", name, "");
01281 }
01282
01283
01284 void VehicleType :: HeightChangeMethod :: runTextIO ( PropertyContainer& pc )
01285 {
01286 pc.addTagInteger( "StartHeight", startHeight, choehenstufennum, heightTags );
01287 pc.addInteger("HeightDelta", heightDelta );
01288 pc.addInteger("MoveCost", moveCost, 0 );
01289 pc.addBool ( "CanAttack", canAttack );
01290 pc.addInteger("Dist", dist );
01291 }
01292
01293
01294
01295 const int vehicleHeightChangeMethodVersion = 1;
01296
01297
01298 void VehicleType :: HeightChangeMethod :: read ( tnstream& stream )
01299 {
01300 int version = stream.readInt();
01301 if ( version > vehicleHeightChangeMethodVersion || version < 1 ) {
01302 ASCString s = "invalid version for reading VehicleType :: HeightChangeMethod : ";
01303 s += strrr ( version );
01304 throw ASCmsgException ( s );
01305 }
01306 startHeight = stream.readInt();
01307 heightDelta = stream.readInt();
01308 moveCost = stream.readInt();
01309 canAttack = stream.readInt();
01310 dist = stream.readInt();
01311 }
01312
01313 void VehicleType :: HeightChangeMethod :: write ( tnstream& stream ) const
01314 {
01315 stream.writeInt ( vehicleHeightChangeMethodVersion );
01316 stream.writeInt ( startHeight );
01317 stream.writeInt ( heightDelta );
01318 stream.writeInt ( moveCost );
01319 stream.writeInt ( canAttack );
01320 stream.writeInt ( dist );
01321 }
01322
01323 Resources VehicleType :: calcProductionCost()
01324 {
01325 static UnitCostCalculator* standard = new StandardUnitCostCalculator();
01326
01327 return standard->productionCost( this );
01328 }