PageRenderTime 58ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/source/vehicletype.cpp

https://bitbucket.org/val_haris/asc-ai
C++ | 1328 lines | 1011 code | 255 blank | 62 comment | 249 complexity | 882b0e86322cbe407165b7232cc285e6 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0
  1. /***************************************************************************
  2. vehicletype.cpp - description
  3. -------------------
  4. begin : Fri Sep 29 2000
  5. copyright : (C) 2000 by Martin Bickel
  6. email : bickel@asc-hq.org
  7. ***************************************************************************/
  8. /*! \file vehicletype.cpp
  9. \brief The #Vehicletype class
  10. */
  11. /***************************************************************************
  12. * *
  13. * This program is free software; you can redistribute it and/or modify *
  14. * it under the terms of the GNU General Public License as published by *
  15. * the Free Software Foundation; either version 2 of the License, or *
  16. * (at your option) any later version. *
  17. * *
  18. ***************************************************************************/
  19. #include <algorithm>
  20. #include "global.h"
  21. #include "vehicletype.h"
  22. #include "sgstream.h"
  23. #include "graphicset.h"
  24. #include "terraintype.h"
  25. #include "objecttype.h"
  26. #include "textfileparser.h"
  27. #include "textfiletags.h"
  28. #include "textfile_evaluation.h"
  29. #include "graphics/blitter.h"
  30. #include "graphics/ColorTransform_PlayerColor.h"
  31. #include "unitcostcalculator-standard.h"
  32. /*
  33. const char* cvehiclefunctions[cvehiclefunctionsnum+1] = {
  34. "sonar",
  35. "paratrooper",
  36. "mine-layer",
  37. "cruiser landing",
  38. "repair vehicle",
  39. "conquer buildings",
  40. "move after attack",
  41. "view satellites",
  42. "construct ALL buildings",
  43. "view mines",
  44. "construct vehicles",
  45. "construct specific buildings",
  46. "refuel units",
  47. "icebreaker",
  48. "cannot be refuelled in air",
  49. "refuels material",
  50. "! (unused) !",
  51. "makes tracks",
  52. "drill for mineral resources manually",
  53. "no reactionfire",
  54. "auto repair",
  55. "generator",
  56. "search for mineral resources automatically",
  57. "Kamikaze only",
  58. "immune to mines",
  59. "refuels energy",
  60. "jams only own field",
  61. "move with reaction fire on",
  62. "only move to and from transports",
  63. NULL };
  64. */
  65. const char* cwaffentypen[weaponTypeNum] = {"cruise missile", "mine", "bomb", "large missile", "small missile", "torpedo", "machine gun",
  66. "cannon", "service", "ammunition refuel", "laser", "shootable", "object placement"
  67. };
  68. const int ammoProductionCost[weaponTypeNum][3] = { { 1500,1500,1500 }, // cruise missile
  69. {10, 10, 10}, // mine
  70. {40, 40, 40}, // bomb
  71. {200, 200, 200}, // big missile
  72. {50, 50, 50}, // small missile
  73. {20, 30, 40}, // torpedo
  74. {1, 1, 1}, // machine gun
  75. {5, 5, 1}, // cannon
  76. {0, 0, 0}, // service
  77. {0, 0, 0}, // ammo refuel
  78. {0, 0, 0}, // laser
  79. {0, 0, 0}, // shootable
  80. {0, 0, 0}
  81. }
  82. ; // objectPlacement
  83. const bool weaponAmmo[weaponTypeNum] = {
  84. true, true, true, true, true, true, true, true, false, false, false, false, false
  85. };
  86. VehicleType :: VehicleType ( void )
  87. {
  88. recommendedAIJob = AiParameter::job_undefined;
  89. int i;
  90. armor = 0;
  91. height = 0;
  92. cargoMovementDivisor = 2;
  93. wait = 0;
  94. fuelConsumption = 0;
  95. movement.resize(8);
  96. for ( i = 0; i < 8; i++ )
  97. movement[i] = 0;
  98. movemalustyp = 0;
  99. maxwindspeedonwater = 0;
  100. digrange = 0;
  101. initiative = 0;
  102. weight = 0;
  103. bipicture = -1;
  104. autorepairrate = 0;
  105. heightChangeMethodNum = 0;
  106. for ( i = 0; i < 8; i++ )
  107. aiparam[i] = NULL;
  108. unitConstructionMoveCostPercentage = 50;
  109. unitConstructionMinDistance = 1;
  110. unitConstructionMaxDistance = 1;
  111. }
  112. int VehicleType::maxsize ( void ) const
  113. {
  114. return weight;
  115. }
  116. const int vehicle_version = 33;
  117. void VehicleType::setupRemovableObjectsFromOldFileLayout ( )
  118. {
  119. for ( vector<IntRange>::iterator i = objectsBuildable.begin(); i != objectsBuildable.end(); )
  120. if ( i->to < 0 && i->from < 0 ) {
  121. int mi = min ( -i->from, -i->to );
  122. int ma = max ( -i->from, -i->to );
  123. objectsRemovable.push_back ( IntRange ( mi, ma ));
  124. i = objectsBuildable.erase ( i );
  125. } else {
  126. objectsRemovable.push_back ( *i );
  127. i++;
  128. }
  129. }
  130. void VehicleType :: read ( tnstream& stream )
  131. {
  132. int version = stream.readInt();
  133. if ( version > vehicle_version || version < 1)
  134. throw tinvalidversion ( stream.getLocation(), vehicle_version, version );
  135. int j;
  136. bool ___load_name = stream.readInt();
  137. bool ___load_description = stream.readInt();
  138. bool ___load_infotext = stream.readInt();
  139. if ( version <= 2 ) {
  140. weapons.count = stream.readChar();
  141. for ( j = 0; j< 8; j++ ) {
  142. weapons.weapon[j].set ( stream.readWord() );
  143. weapons.weapon[j].targ = stream.readChar();
  144. weapons.weapon[j].sourceheight = stream.readChar();
  145. weapons.weapon[j].maxdistance = stream.readWord();
  146. weapons.weapon[j].mindistance = stream.readWord();
  147. weapons.weapon[j].count = stream.readChar();
  148. weapons.weapon[j].maxstrength = stream.readChar();
  149. weapons.weapon[j].minstrength = stream.readChar();
  150. for ( int k = 0; k < 13; k++ )
  151. weapons.weapon[j].efficiency[k] = 100;
  152. }
  153. }
  154. if ( version <= 27 ) {
  155. productionCost.energy = stream.readWord();
  156. productionCost.material = stream.readWord();
  157. } else
  158. productionCost.read( stream );
  159. armor = stream.readWord();
  160. bool picture[8];
  161. for ( j = 0; j < 8; j++ )
  162. if ( version <= 18 )
  163. picture[j] = stream.readInt();
  164. else
  165. picture[j] = false;
  166. height = stream.readChar();
  167. stream.readWord(); // was: researchID
  168. int _terrain = 0;
  169. int _terrainreq = 0;
  170. int _terrainkill = 0;
  171. if ( version <= 2 ) {
  172. _terrain = stream.readInt();
  173. _terrainreq = stream.readInt();
  174. _terrainkill = stream.readInt();
  175. }
  176. stream.readChar(); // steigung
  177. jamming = stream.readChar();
  178. view = stream.readWord();
  179. wait = stream.readChar();
  180. if ( version <= 2 )
  181. stream.readChar (); // dummy
  182. stream.readWord(); // dummy
  183. stream.readWord(); // dummy
  184. stream.readChar(); // dummy
  185. stream.readChar(); // dummy
  186. stream.readChar(); // dummy
  187. if ( version <= 18 )
  188. id = stream.readWord();
  189. else
  190. id = stream.readInt();
  191. if ( version < 24 ) {
  192. bi_mode_tank.resource(2) = bi_mode_tank.resource(2) = stream.readInt();
  193. fuelConsumption = stream.readWord();
  194. bi_mode_tank.resource(0) = bi_mode_tank.resource(0) = stream.readInt();
  195. bi_mode_tank.resource(1) = bi_mode_tank.resource(1) = stream.readInt();
  196. } else
  197. fuelConsumption = stream.readInt();
  198. if ( version <= 22 ) {
  199. int functions = stream.readInt();
  200. features = convertOldFunctions ( functions, stream.getLocation() );
  201. }
  202. for ( j = 0; j < 8; j++ )
  203. if ( version <= 4 )
  204. movement[j] = stream.readChar();
  205. else
  206. movement[j] = stream.readInt();
  207. movemalustyp = stream.readChar();
  208. if ( movemalustyp >= cmovemalitypenum )
  209. movemalustyp = cmovemalitypenum -1;
  210. if ( version <= 2 )
  211. for ( j = 0; j < 9; j++ )
  212. stream.readWord( ); // dummy1
  213. bool ___load_classnames[8];
  214. if ( version <= 15 ) {
  215. stream.readChar(); // classnum
  216. for ( j = 0; j < 8; j++ )
  217. ___load_classnames[j] = stream.readInt();
  218. for ( j = 0; j < 8; j++ ) {
  219. int k;
  220. for ( k = 0; k < 8; k++ )
  221. stream.readWord();
  222. if ( version <= 2 )
  223. stream.readWord ( ); // dummy2
  224. stream.readWord();
  225. stream.readWord();
  226. for ( k = 0; k < 4; k++ )
  227. stream.readWord();
  228. stream.readChar();
  229. stream.readInt();
  230. if ( version <= 2 )
  231. stream.readChar( ); // dummy
  232. }
  233. }
  234. maxwindspeedonwater = stream.readChar();
  235. digrange = stream.readChar();
  236. initiative = stream.readInt();
  237. int _terrainnot = 0;
  238. if ( version <= 4 ) {
  239. _terrainnot = stream.readInt();
  240. stream.readInt(); // _terrainreq1
  241. }
  242. int objectsbuildablenum = stream.readInt();
  243. if ( version <= 4 )
  244. stream.readInt(); // objectsbuildableid = (int*)
  245. weight = stream.readInt();
  246. bool ___loadterrainaccess = false;
  247. if ( version <= 4 )
  248. ___loadterrainaccess = stream.readInt();
  249. bipicture = stream.readInt();
  250. int vehiclesbuildablenum = stream.readInt();
  251. if ( version <= 4 )
  252. stream.readInt(); // vehiclesbuildableid = (int*)
  253. if ( version <= 4 )
  254. stream.readInt(); // buildicon
  255. int buildingsbuildablenum = stream.readInt();
  256. if ( version <= 4 )
  257. stream.readInt(); // buildingsbuildable = (Vehicletype::tbuildrange*)
  258. bool load_weapons = stream.readInt();
  259. autorepairrate = stream.readInt();
  260. if ( version >= 15 )
  261. readClassContainer ( wreckageObject, stream );
  262. else
  263. if ( version >= 8 ) {
  264. wreckageObject.clear();
  265. wreckageObject.push_back ( stream.readInt() );
  266. }
  267. if ( version <= 2 )
  268. stream.readInt( ); // dummy
  269. if ( version >= 4 )
  270. stream.readInt(); // dummy
  271. if (___load_name)
  272. name = stream.readString( true );
  273. if (___load_description)
  274. description = stream.readString( true );
  275. if (___load_infotext)
  276. infotext = stream.readString ( true );
  277. int i;
  278. if ( version <= 15 )
  279. for ( i=0;i<8 ;i++ )
  280. if ( ___load_classnames[i] )
  281. stream.readString ( true );
  282. if ( hasFunction( AutoRepair ) )
  283. if ( !autorepairrate )
  284. autorepairrate = 10;
  285. if ( version <= 18 ) {
  286. if ( picture[0] )
  287. image.read ( stream );
  288. for ( int i=1;i<8 ;i++ ) {
  289. if ( picture[i] ) {
  290. Surface s;
  291. s.read ( stream );
  292. bipicture = 0;
  293. }
  294. }
  295. } else {
  296. image.read ( stream );
  297. }
  298. if ( objectsbuildablenum )
  299. for ( i = 0; i < objectsbuildablenum; i++ ) {
  300. int from = stream.readInt ( );
  301. int to;
  302. if ( version <= 4 )
  303. to = from;
  304. else
  305. to = stream.readInt();
  306. objectsBuildable.push_back ( IntRange ( from, to ));
  307. }
  308. if ( version >= 6 ) {
  309. int num = stream.readInt();
  310. for ( int i = 0; i < num; i++ ) {
  311. int from = stream.readInt();
  312. int to = stream.readInt();
  313. objectsRemovable.push_back ( IntRange ( from, to ));
  314. }
  315. } else
  316. setupRemovableObjectsFromOldFileLayout();
  317. if ( version >= 8 ) {
  318. int num = stream.readInt();
  319. for ( int i = 0; i < num; i++ ) {
  320. int from = stream.readInt();
  321. int to = stream.readInt();
  322. objectGroupsBuildable.push_back ( IntRange ( from, to ));
  323. }
  324. num = stream.readInt();
  325. for ( int i = 0; i < num; i++ ) {
  326. int from = stream.readInt();
  327. int to = stream.readInt();
  328. objectGroupsRemovable.push_back ( IntRange ( from, to ));
  329. }
  330. }
  331. if ( vehiclesbuildablenum )
  332. for ( i = 0; i < vehiclesbuildablenum; i++ ) {
  333. int from = stream.readInt ( );
  334. int to;
  335. if ( version <= 4 )
  336. to = from;
  337. else
  338. to = stream.readInt();
  339. vehiclesBuildable.push_back ( IntRange ( from, to ));
  340. }
  341. if ( load_weapons && version > 1) {
  342. weapons.count = stream.readInt();
  343. for ( j = 0; j< 16; j++ ) {
  344. weapons.weapon[j].set ( stream.readInt() );
  345. weapons.weapon[j].targ = stream.readInt();
  346. weapons.weapon[j].sourceheight = stream.readInt();
  347. weapons.weapon[j].maxdistance = stream.readInt();
  348. weapons.weapon[j].mindistance = stream.readInt();
  349. weapons.weapon[j].count = stream.readInt();
  350. weapons.weapon[j].maxstrength = stream.readInt();
  351. weapons.weapon[j].minstrength = stream.readInt();
  352. if ( version >= 17 )
  353. weapons.weapon[j].reactionFireShots = stream.readInt();
  354. if ( version >= 22 )
  355. weapons.weapon[j].name = stream.readString();
  356. /*
  357. if ( weapons.weapon[j].getScalarWeaponType() == cwminen )
  358. if ( weapons.weapon[j].mindistance < 8 )
  359. warningMessage ( ASCString("unit with id ") + strrr ( id ) + " has invalid mine weapon range ");
  360. */
  361. for ( int k = 0; k < 13; k++ )
  362. weapons.weapon[j].efficiency[k] = stream.readInt();
  363. if ( version <= 6 ) {
  364. int targets_not_hittable = stream.readInt();
  365. for ( int i = 0; i < cmovemalitypenum; i++ )
  366. if ( targets_not_hittable & ( 1 << i))
  367. weapons.weapon[j].targetingAccuracy[i] = 0;
  368. } else {
  369. int num = stream.readInt();
  370. for ( int i = 0; i < num; i++ )
  371. weapons.weapon[j].targetingAccuracy[i] = stream.readInt();
  372. }
  373. if ( version <= 2 )
  374. for ( int l = 0; l < 9; l++ )
  375. stream.readInt(); // dummy
  376. if ( version >= 11 ) {
  377. weapons.weapon[j].laserRechargeRate = stream.readInt();
  378. weapons.weapon[j].laserRechargeCost.read( stream );
  379. }
  380. if ( version >= 20 )
  381. weapons.weapon[j].soundLabel = stream.readString();
  382. }
  383. if ( version <= 2 )
  384. for ( int m = 0; m< 10; m++ )
  385. stream.readInt(); // dummy
  386. }
  387. if ( ___loadterrainaccess || version >= 5 )
  388. terrainaccess.read ( stream );
  389. else {
  390. terrainaccess.terrain.setInt ( _terrain, 0 );
  391. terrainaccess.terrainreq.setInt ( _terrainreq, 0 );
  392. terrainaccess.terrainnot.setInt ( _terrainnot, 0 );
  393. terrainaccess.terrainkill.setInt ( _terrainkill, 0 );
  394. }
  395. if ( buildingsbuildablenum )
  396. for ( i = 0; i < buildingsbuildablenum; i++ ) {
  397. int from = stream.readInt();
  398. int to = stream.readInt();
  399. buildingsBuildable.push_back ( IntRange ( from, to ));
  400. }
  401. filename = stream.getDeviceName();
  402. location = stream.getLocation();
  403. if ( version >= 9 )
  404. ContainerBaseType::read ( stream );
  405. if ( version >= 10 ) {
  406. heightChangeMethodNum = stream.readInt();
  407. heightChangeMethod.resize(heightChangeMethodNum );
  408. for ( int i = 0; i < heightChangeMethodNum; i++ )
  409. heightChangeMethod[i].read( stream );
  410. } else
  411. heightChangeMethodNum = 0;
  412. if ( version >= 12 )
  413. techDependency.read( stream );
  414. if ( version >= 13 && version <= 24 ) {
  415. Surface s;
  416. s.read( stream );
  417. }
  418. if ( version >= 14 && version < 18)
  419. cargoMovementDivisor = stream.readInt();
  420. else
  421. if ( version >= 18 )
  422. cargoMovementDivisor = stream.readFloat();
  423. if ( version >= 20 ) {
  424. movementSoundLabel = stream.readString();
  425. killSoundLabel = stream.readString();
  426. }
  427. if ( version >= 22 )
  428. readClassContainer( guideSortHelp, stream );
  429. if ( version >= 24 ) {
  430. efficiencyfuel = stream.readInt( );
  431. efficiencymaterial = stream.readInt( );
  432. asc_mode_tank.read( stream );
  433. bi_mode_tank.read( stream );
  434. maxresearchpoints = stream.readInt();
  435. defaultMaxResearchpoints = stream.readInt();
  436. nominalresearchpoints = stream.readInt();
  437. maxplus.read( stream );
  438. defaultProduction.read( stream );
  439. }
  440. for ( int w = 0; w < weapons.count; ++w )
  441. if ( weapons.weapon[w].canRefuel() )
  442. setFunction( ExternalAmmoTransfer );
  443. if ( version >= 26 ) {
  444. jumpDrive.height = stream.readInt();
  445. jumpDrive.targetterrain.read( stream );
  446. jumpDrive.consumption.read( stream );
  447. jumpDrive.maxDistance = stream.readInt();
  448. }
  449. if ( version >= 27 ) {
  450. int num = stream.readInt();
  451. for ( int i = 0; i < num; i++ ) {
  452. int from = stream.readInt();
  453. int to = stream.readInt();
  454. objectLayedByMovement.push_back ( IntRange ( from, to ));
  455. }
  456. }
  457. if ( version >= 29 )
  458. unitConstructionMoveCostPercentage = stream.readInt();
  459. if ( version >= 30 ) {
  460. unitConstructionMinDistance = stream.readInt();
  461. unitConstructionMaxDistance = stream.readInt();
  462. }
  463. if ( version >= 31 )
  464. imageFilename = stream.readString();
  465. if ( version >= 32 )
  466. recommendedAIJob = AiParameter::Job(stream.readInt());
  467. if ( version >= 33 )
  468. costCalculator = stream.readString();
  469. }
  470. void VehicleType:: write ( tnstream& stream ) const
  471. {
  472. int i,j;
  473. stream.writeInt ( vehicle_version );
  474. if ( !name.empty() )
  475. stream.writeInt( 1 );
  476. else
  477. stream.writeInt( 0 );
  478. if ( !description.empty() )
  479. stream.writeInt( 1 );
  480. else
  481. stream.writeInt( 0 );
  482. if ( !infotext.empty() )
  483. stream.writeInt( 1 );
  484. else
  485. stream.writeInt( 0 );
  486. productionCost.write( stream );
  487. stream.writeWord( armor );
  488. stream.writeChar( height );
  489. stream.writeWord(0); // researchid
  490. stream.writeChar(0); // steigung
  491. stream.writeChar(jamming);
  492. stream.writeWord(view);
  493. stream.writeChar(wait);
  494. stream.writeWord(0);
  495. stream.writeWord(0);
  496. stream.writeChar(0);
  497. stream.writeChar(0);
  498. stream.writeChar(0);
  499. stream.writeInt(id );
  500. stream.writeInt(fuelConsumption );
  501. for ( j = 0; j < 8; j++ )
  502. stream.writeInt( movement[j] );
  503. stream.writeChar(movemalustyp );
  504. stream.writeChar(maxwindspeedonwater );
  505. stream.writeChar(digrange );
  506. stream.writeInt(initiative );
  507. stream.writeInt( objectsBuildable.size() );
  508. stream.writeInt(weight );
  509. stream.writeInt(bipicture );
  510. stream.writeInt(vehiclesBuildable.size() );
  511. stream.writeInt( buildingsBuildable.size() );
  512. stream.writeInt( 1 ); // weapons
  513. stream.writeInt( autorepairrate );
  514. writeClassContainer( wreckageObject, stream );
  515. stream.writeInt( 0 );
  516. if ( !name.empty() )
  517. stream.writeString( name );
  518. if ( !description.empty() )
  519. stream.writeString( description );
  520. if ( !infotext.empty() )
  521. stream.writeString( infotext );
  522. image.write( stream );
  523. for ( i = 0; i < objectsBuildable.size(); i++ ) {
  524. stream.writeInt ( objectsBuildable[i].from );
  525. stream.writeInt ( objectsBuildable[i].to );
  526. }
  527. stream.writeInt ( objectsRemovable.size() );
  528. for ( i = 0; i < objectsRemovable.size(); i++ ) {
  529. stream.writeInt ( objectsRemovable[i].from );
  530. stream.writeInt ( objectsRemovable[i].to );
  531. }
  532. stream.writeInt ( objectGroupsBuildable.size() );
  533. for ( i = 0; i < objectGroupsBuildable.size(); i++ ) {
  534. stream.writeInt ( objectGroupsBuildable[i].from );
  535. stream.writeInt ( objectGroupsBuildable[i].to );
  536. }
  537. stream.writeInt ( objectGroupsRemovable.size() );
  538. for ( i = 0; i < objectGroupsRemovable.size(); i++ ) {
  539. stream.writeInt ( objectGroupsRemovable[i].from );
  540. stream.writeInt ( objectGroupsRemovable[i].to );
  541. }
  542. for ( i = 0; i < vehiclesBuildable.size(); i++ ) {
  543. stream.writeInt ( vehiclesBuildable[i].from );
  544. stream.writeInt ( vehiclesBuildable[i].to );
  545. }
  546. stream.writeInt(weapons.count );
  547. for ( j = 0; j< 16; j++ ) {
  548. stream.writeInt(weapons.weapon[j].gettype( ));
  549. stream.writeInt(weapons.weapon[j].targ);
  550. stream.writeInt(weapons.weapon[j].sourceheight );
  551. stream.writeInt(weapons.weapon[j].maxdistance );
  552. stream.writeInt(weapons.weapon[j].mindistance );
  553. stream.writeInt(weapons.weapon[j].count );
  554. stream.writeInt(weapons.weapon[j].maxstrength );
  555. stream.writeInt(weapons.weapon[j].minstrength );
  556. stream.writeInt(weapons.weapon[j].reactionFireShots );
  557. stream.writeString( weapons.weapon[j].name );
  558. for ( int k = 0; k < 13; k++ )
  559. stream.writeInt(weapons.weapon[j].efficiency[k] );
  560. stream.writeInt ( cmovemalitypenum );
  561. for ( int i = 0; i < cmovemalitypenum; i++ )
  562. stream.writeInt(weapons.weapon[j].targetingAccuracy[i] );
  563. stream.writeInt( weapons.weapon[j].laserRechargeRate );
  564. weapons.weapon[j].laserRechargeCost.write( stream );
  565. stream.writeString( weapons.weapon[j].soundLabel );
  566. }
  567. terrainaccess.write ( stream );
  568. for ( i = 0; i < buildingsBuildable.size(); i++ ) {
  569. stream.writeInt( buildingsBuildable[i].from );
  570. stream.writeInt( buildingsBuildable[i].to );
  571. }
  572. ContainerBaseType::write ( stream );
  573. stream.writeInt( heightChangeMethodNum );
  574. for ( i = 0; i < heightChangeMethodNum; i++ )
  575. heightChangeMethod[i].write( stream );
  576. techDependency.write( stream );
  577. stream.writeFloat ( cargoMovementDivisor );
  578. stream.writeString( movementSoundLabel );
  579. stream.writeString( killSoundLabel );
  580. writeClassContainer( guideSortHelp, stream );
  581. stream.writeInt( efficiencyfuel );
  582. stream.writeInt( efficiencymaterial );
  583. asc_mode_tank.write( stream );
  584. bi_mode_tank.write( stream );
  585. stream.writeInt( maxresearchpoints );
  586. stream.writeInt( defaultMaxResearchpoints );
  587. stream.writeInt( nominalresearchpoints );
  588. maxplus.write ( stream );
  589. defaultProduction.write( stream );
  590. stream.writeInt( jumpDrive.height );
  591. jumpDrive.targetterrain.write( stream );
  592. jumpDrive.consumption.write( stream );
  593. stream.writeInt( jumpDrive.maxDistance );
  594. stream.writeInt ( objectLayedByMovement.size() );
  595. for ( i = 0; i < objectLayedByMovement.size(); i++ ) {
  596. stream.writeInt ( objectLayedByMovement[i].from );
  597. stream.writeInt ( objectLayedByMovement[i].to );
  598. }
  599. stream.writeInt( unitConstructionMoveCostPercentage );
  600. stream.writeInt( unitConstructionMinDistance );
  601. stream.writeInt( unitConstructionMaxDistance );
  602. stream.writeString( imageFilename );
  603. stream.writeInt( recommendedAIJob );
  604. stream.writeString( costCalculator );
  605. }
  606. ASCString VehicleType::getName( ) const
  607. {
  608. if ( !name.empty() )
  609. return name;
  610. else
  611. return description;
  612. }
  613. int VehicleType :: maxSpeed ( ) const
  614. {
  615. int maxUnitMovement = 0;
  616. for ( int i = 0; i < 8; i++ )
  617. maxUnitMovement = max ( maxUnitMovement, movement[i] );
  618. return maxUnitMovement;
  619. }
  620. int VehicleType::getMemoryFootprint() const
  621. {
  622. return sizeof(*this) + image.getMemoryFootprint();
  623. }
  624. VehicleType :: ~VehicleType ( )
  625. {
  626. for ( int i = 0; i < 8; i++ )
  627. if ( aiparam[i] ) {
  628. delete aiparam[i];
  629. aiparam[i] = NULL;
  630. }
  631. }
  632. SingleWeapon::SingleWeapon()
  633. {
  634. typ = 0;
  635. targ = 0;
  636. sourceheight = 0;
  637. maxdistance = 0;
  638. mindistance = 0;
  639. count = 0;
  640. maxstrength= 0;
  641. minstrength = 0;
  642. laserRechargeRate = 0;
  643. for ( int i = 0; i < 13; i++ )
  644. efficiency[i] = 0;
  645. for ( int i = 0; i < cmovemalitypenum; i++ )
  646. targetingAccuracy[i] = 100;
  647. reactionFireShots = 1;
  648. }
  649. /* Translate the weapon/mine/service bit pattern into scalar
  650. * weapon number for use in fetching UI resources.
  651. */
  652. int SingleWeapon::getScalarWeaponType(void) const
  653. {
  654. if ( typ & (cwweapon | cwmineb) )
  655. return getFirstBit ( typ & (cwweapon | cwmineb) );
  656. else
  657. return -1;
  658. }
  659. bool SingleWeapon::requiresAmmo(void) const
  660. {
  661. if ( typ & cwlaserb )
  662. return false;
  663. else
  664. return typ & ( cwweapon | cwmineb );
  665. }
  666. bool SingleWeapon::shootable( void ) const
  667. {
  668. return typ & cwshootableb;
  669. }
  670. bool SingleWeapon::offensive( void ) const
  671. {
  672. return typ & cwweapon;
  673. }
  674. bool SingleWeapon::service( void ) const
  675. {
  676. return typ & cwserviceb;
  677. }
  678. bool SingleWeapon::placeObjects( ) const
  679. {
  680. return typ & cwobjectplacementb;
  681. }
  682. bool SingleWeapon::canRefuel( void ) const
  683. {
  684. return typ & cwammunitionb;
  685. }
  686. void SingleWeapon::set
  687. ( int type )
  688. {
  689. typ = type;
  690. }
  691. ASCString SingleWeapon::getName ( void ) const
  692. {
  693. if ( !name.empty() )
  694. return name;
  695. ASCString s;
  696. int k = getScalarWeaponType();
  697. if ( k < weaponTypeNum && k >= 0 )
  698. s = cwaffentypen[k];
  699. else
  700. if ( service() || placeObjects() )
  701. s = cwaffentypen[cwservicen];
  702. else
  703. s = "undefined";
  704. return s;
  705. }
  706. ASCString SingleWeapon::getIconFileName( int numerical )
  707. {
  708. switch ( numerical ) {
  709. case cwcruisemissile:
  710. return "weap-cruisemissile";
  711. case cwbombn:
  712. return "weap-bomb";
  713. case cwlargemissilen:
  714. return "weap-bigmissile";
  715. case cwsmallmissilen:
  716. return "weap-smallmissile";
  717. case cwtorpedon:
  718. return "weap-torpedo";
  719. case cwmachinegunn:
  720. return "weap-machinegun";
  721. case cwcannonn:
  722. return "weap-cannon";
  723. case cwminen:
  724. return "weap-mine";
  725. case cwservicen:
  726. return "weap-service";
  727. case cwlasern:
  728. return "weap-laser";
  729. default:
  730. return "weap-undefined";
  731. };
  732. }
  733. bool SingleWeapon::equals( const SingleWeapon* otherWeapon ) const
  734. {
  735. if (
  736. otherWeapon->targ == this->targ &&
  737. otherWeapon->sourceheight == this->sourceheight &&
  738. otherWeapon->maxdistance == this->maxdistance &&
  739. otherWeapon->mindistance == this->mindistance &&
  740. otherWeapon->count == this->count &&
  741. otherWeapon->maxstrength == this->maxstrength &&
  742. otherWeapon->minstrength == this->minstrength &&
  743. otherWeapon->gettype() == this->gettype()
  744. ) {
  745. bool equal = true;
  746. for ( int i=0; i<13; i++ ) {
  747. if ( otherWeapon->efficiency[ i ] != this->efficiency[ i ] ) {
  748. equal = false;
  749. }
  750. }
  751. if ( equal ) return true;
  752. }
  753. return false;
  754. }
  755. UnitWeapon :: UnitWeapon ( void )
  756. {
  757. count = 0;
  758. }
  759. void VehicleType::runTextIO ( PropertyContainer& pc )
  760. {
  761. ContainerBaseType::runTextIO ( pc );
  762. pc.addInteger( "Armor", armor );
  763. ASCString fn;
  764. if ( filename.empty() ) {
  765. fn += "vehicle";
  766. fn += strrr(id);
  767. } else
  768. fn = extractFileName_withoutSuffix( filename );
  769. pc.addImage( "Picture", image, fn, true );
  770. if ( pc.isReading() )
  771. imageFilename = fn;
  772. else
  773. pc.addString("OriginalImageFilename", imageFilename );
  774. if ( image.w() < fieldsizex || image.h() < fieldsizey )
  775. image.strech( fieldsizex, fieldsizey );
  776. image.assignDefaultPalette();
  777. pc.addTagInteger( "Height", height, choehenstufennum, heightTags );
  778. pc.addBool ( "WaitForAttack", wait );
  779. if ( bi_mode_tank == Resources(0,0,0) && asc_mode_tank == Resources(0,0,0)) {
  780. pc.openBracket( "Tank" );
  781. Resources tank;
  782. tank.runTextIO ( pc );
  783. pc.closeBracket();
  784. bi_mode_tank = tank;
  785. asc_mode_tank = tank;
  786. }
  787. pc.addInteger( "FuelConsumption", fuelConsumption );
  788. if ( !pc.find("Features") && pc.isReading() ) {
  789. int abilities;
  790. pc.addTagInteger ( "Abilities", abilities, legacyVehicleFunctionNum, vehicleAbilities );
  791. features = convertOldFunctions ( abilities, pc.getFileName() );
  792. } else
  793. pc.addTagArray ( "Features", features, functionNum, containerFunctionTags );
  794. pc.addIntegerArray ( "Movement", movement );
  795. while ( movement.size() < 8 )
  796. movement.push_back(0);
  797. for ( vector<int>::iterator i = movement.begin(); i != movement.end(); i++ )
  798. if ( *i > 350 )
  799. *i = 350;
  800. pc.addNamedInteger ( "Category", movemalustyp, cmovemalitypenum, unitCategoryTags );
  801. pc.addInteger("MaxSurvivableStorm", maxwindspeedonwater, 255 );
  802. pc.addInteger("ResourceDrillingRange", digrange, 0 );
  803. pc.addInteger("SelfRepairRate", autorepairrate, 0 );
  804. if ( pc.find ( "WreckageObject" ) || !pc.isReading() )
  805. pc.addIntegerArray("WreckageObject", wreckageObject );
  806. if ( pc.find( "CargoMovementDivisor" ))
  807. pc.addDFloat("CargoMovementDivisor", cargoMovementDivisor);
  808. else {
  809. pc.openBracket( "Transportation" );
  810. pc.addDFloat("CargoMovementDivisor", cargoMovementDivisor, 2 );
  811. pc.closeBracket();
  812. }
  813. pc.addInteger("Weight", weight);
  814. pc.openBracket("TerrainAccess" );
  815. terrainaccess.runTextIO ( pc );
  816. pc.closeBracket();
  817. pc.openBracket ( "Construction" );
  818. pc.addIntRangeArray ( "Buildings", buildingsBuildable );
  819. pc.addIntRangeArray ( "Vehicles", vehiclesBuildable );
  820. pc.addIntRangeArray ( "Objects", objectsBuildable );
  821. if ( pc.isReading() ) {
  822. if ( pc.find ( "ObjectsRemovable" ))
  823. pc.addIntRangeArray ( "ObjectsRemovable", objectsRemovable );
  824. else
  825. setupRemovableObjectsFromOldFileLayout();
  826. } else
  827. pc.addIntRangeArray ( "ObjectsRemovable", objectsRemovable );
  828. pc.addIntRangeArray ( "ObjectGroupsBuildable", objectGroupsBuildable, false );
  829. pc.addIntRangeArray ( "ObjectGroupsRemovable", objectGroupsRemovable, false );
  830. pc.addInteger("UnitConstructionMoveCostPercentage", unitConstructionMoveCostPercentage, 50);
  831. pc.addInteger("UnitConstructionMinDistance", unitConstructionMinDistance, 1 );
  832. pc.addInteger("UnitConstructionMaxDistance", unitConstructionMaxDistance, 1 );
  833. pc.closeBracket();
  834. pc.openBracket ( "Weapons");
  835. // pc.addInteger("Initiative", initiative );
  836. pc.addInteger("Number", weapons.count );
  837. for ( int i = 0; i < weapons.count; i++ ) {
  838. pc.openBracket ( ASCString("Weapon")+strrr(i) );
  839. weapons.weapon[i].runTextIO( pc );
  840. pc.closeBracket();
  841. if ( hasFunction( NoReactionfire ) )
  842. weapons.weapon[i].reactionFireShots = 0;
  843. }
  844. pc.closeBracket();
  845. int job = recommendedAIJob;
  846. pc.addNamedInteger ( "AIJobOverride", job, AiParameter::jobNum, AIjobs, AiParameter::job_undefined );
  847. recommendedAIJob = AiParameter::Job(job);
  848. pc.addString("MovementSound", movementSoundLabel, "" );
  849. pc.addString("KillSound", killSoundLabel, "" );
  850. pc.addInteger("HeightChangeMethodNum", heightChangeMethodNum, 0 );
  851. heightChangeMethod.resize( heightChangeMethodNum );
  852. for ( int i = 0; i < heightChangeMethodNum; i++ ) {
  853. pc.openBracket( ASCString("HeightChangeMethod")+strrr(i) );
  854. heightChangeMethod[i].runTextIO ( pc );
  855. pc.closeBracket();
  856. }
  857. techDependency.runTextIO( pc, strrr(id) );
  858. pc.openBracket ( "ConstructionCost" );
  859. productionCost.runTextIO ( pc );
  860. int costCalcMethod = 0;
  861. pc.addNamedInteger( "CalculationMethod", costCalcMethod, productionCostCalculationMethodNum, productionCostCalculationMethod, 0 );
  862. if ( pc.isReading() ) {
  863. if ( !pc.find ( "material" ) && costCalcMethod == 0)
  864. costCalcMethod = 1;
  865. }
  866. pc.addString( "Calculator", costCalculator, "standard" );
  867. if ( costCalcMethod == 1 )
  868. productionCost = calcProductionCost();
  869. if ( costCalcMethod == 2 )
  870. productionCost += calcProductionCost();
  871. if ( costCalcMethod != 0 ) {
  872. 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 );
  873. }
  874. pc.closeBracket ();
  875. if ( pc.find( "guideSortHelp") || !pc.isReading() )
  876. pc.addIntegerArray("guideSortHelp", guideSortHelp );
  877. bool hasService = false;
  878. for ( int w = 0; w < weapons.count; ++w ) {
  879. if ( weapons.weapon[w].canRefuel() )
  880. setFunction( ExternalAmmoTransfer );
  881. if ( weapons.weapon[w].service() )
  882. hasService = true;
  883. }
  884. if ( !hasService ) {
  885. features.reset( ExternalRepair );
  886. features.reset( ExternalEnergyTransfer );
  887. features.reset( ExternalMaterialTransfer );
  888. features.reset( ExternalFuelTransfer );
  889. features.reset( ExternalAmmoTransfer );
  890. }
  891. pc.openBracket ( "JumpDrive" );
  892. pc.addTagInteger( "Height", jumpDrive.height, choehenstufennum, heightTags, 0 );
  893. pc.openBracket ( "consumption" );
  894. jumpDrive.consumption.runTextIO ( pc, Resources(0,0,0) );
  895. pc.closeBracket();
  896. if ( jumpDrive.height || !pc.isReading() )
  897. jumpDrive.targetterrain.runTextIO ( pc );
  898. pc.addInteger( "MaxDistance", jumpDrive.maxDistance, maxint );
  899. pc.closeBracket();
  900. if ( jumpDrive.height && view )
  901. pc.error( "only units without radar may have a jump drive." );
  902. if ( !pc.isReading() || pc.find ( "ObjectsLayedByMovement" ))
  903. pc.addIntRangeArray ( "ObjectsLayedByMovement", objectLayedByMovement );
  904. else
  905. objectLayedByMovement.clear();
  906. if ( hasFunction( ContainerBaseType::IceBreaker ))
  907. objectLayedByMovement.push_back ( 6 );
  908. if ( hasFunction( ContainerBaseType::MakesTracks ))
  909. objectLayedByMovement.push_back ( 7 );
  910. }
  911. BitSet VehicleType::convertOldFunctions( int abilities, const ASCString& location )
  912. {
  913. BitSet features;
  914. if ( abilities & 1 )
  915. features.set( Sonar );
  916. if ( abilities & 2 )
  917. features.set( Paratrooper );
  918. if ( abilities & 4 )
  919. features.set( PlaceMines );
  920. if ( abilities & 8 )
  921. features.set( CruiserLanding );
  922. if ( abilities & 16 ) {
  923. features.set( ExternalRepair );
  924. features.set( InternalUnitRepair );
  925. }
  926. if ( abilities & 32 )
  927. features.set( ConquerBuildings );
  928. if ( abilities & 64 )
  929. features.set( MoveAfterAttack );
  930. if ( abilities & 128 )
  931. features.set( SatelliteView );
  932. if ( abilities & 256 )
  933. errorMessage( location + ": The features construct_ALL_buildings is not supported any more");
  934. if ( abilities & 512 )
  935. features.set( MineView );
  936. if ( abilities & 1024 )
  937. features.set( ExternalVehicleProduction );
  938. if ( abilities & 2048 )
  939. features.set( ConstructBuildings );
  940. if ( abilities & 4096 )
  941. features.set( ExternalFuelTransfer );
  942. if ( abilities & 8192 )
  943. features.set( IceBreaker );
  944. if ( abilities & 16384 )
  945. features.set( NoInairRefuelling );
  946. if ( abilities & 32768 )
  947. features.set( ExternalMaterialTransfer );
  948. if ( abilities & (1 << 17) )
  949. features.set( MakesTracks );
  950. if ( abilities & (1 << 18) )
  951. features.set( DetectsMineralResources );
  952. if ( abilities & (1 << 19) )
  953. features.set( NoReactionfire );
  954. if ( abilities & (1 << 20) )
  955. features.set( AutoRepair );
  956. if ( abilities & (1 << 21) )
  957. features.set( MatterConverter );
  958. // we probably need to setup the conversion matrix at this point
  959. if ( abilities & (1 << 22) )
  960. features.set( DetectsMineralResources );
  961. if ( abilities & (1 << 23) )
  962. features.set( KamikazeOnly );
  963. if ( abilities & (1 << 24) )
  964. features.set( ImmuneToMines );
  965. if ( abilities & (1 << 25) )
  966. features.set( ExternalEnergyTransfer );
  967. if ( abilities & (1 << 26) )
  968. features.set( JamsOnlyOwnField );
  969. if ( abilities & (1 << 27) )
  970. features.set( MoveWithReactionFire );
  971. if ( abilities & (1 << 28) )
  972. features.set( OnlyMoveToAndFromTransports );
  973. return features;
  974. }
  975. void VehicleType::paint ( Surface& s, SPoint pos, const PlayerColor& player, int direction ) const
  976. {
  977. megaBlitter<ColorTransform_PlayerCol,ColorMerger_AlphaOverwrite,SourcePixelSelector_Plain,TargetPixelSelector_All>( getImage(), s, pos, player, nullParam, nullParam, nullParam );
  978. }
  979. void VehicleType::paint ( Surface& s, SPoint pos ) const
  980. {
  981. megaBlitter<ColorTransform_None,ColorMerger_AlphaOverwrite,SourcePixelSelector_Plain,TargetPixelSelector_All>( getImage(), s, pos, nullParam, nullParam, nullParam, nullParam );
  982. }
  983. void SingleWeapon::runTextIO ( PropertyContainer& pc )
  984. {
  985. pc.addTagInteger( "Type", typ, weaponTypeNum, weaponTags );
  986. pc.addTagInteger( "targets", targ, choehenstufennum, heightTags );
  987. pc.addTagInteger( "shotFrom", sourceheight, choehenstufennum, heightTags );
  988. pc.addInteger("MaxRange", maxdistance );
  989. pc.addInteger("MinRange", mindistance );
  990. pc.addInteger("Ammo", count );
  991. pc.addInteger("Punch@MaxRange", minstrength );
  992. pc.addInteger("Punch@MinRange", maxstrength );
  993. pc.addString("Sound", soundLabel, "");
  994. pc.addInteger("LaserRechargeRate", laserRechargeRate, 0 );
  995. pc.openBracket( "laserRechargeCost" );
  996. laserRechargeCost.runTextIO ( pc, Resources(0,0,0) );
  997. pc.closeBracket();
  998. pc.addInteger("ReactionFireShots", reactionFireShots, 1 );
  999. if ( getScalarWeaponType() == cwminen && reactionFireShots > 0 )
  1000. warningMessage(pc.getFileName() + " has a mine with Reactionfire. This doesn't make sense.");
  1001. pc.openBracket("HitAccuracy" );
  1002. {
  1003. for ( int j = 0; j < 13; j++ )
  1004. if ( j < 6 )
  1005. pc.addInteger( ASCString("d")+strrr(abs(j-6)), efficiency[j] );
  1006. else
  1007. if ( j == 6 )
  1008. pc.addInteger( "0", efficiency[j] );
  1009. else
  1010. pc.addInteger( ASCString("u")+strrr(j-6), efficiency[j] );
  1011. }
  1012. pc.closeBracket();
  1013. if ( pc.isReading() ) {
  1014. if ( pc.find ( "cantHit" )) {
  1015. int targets_not_hittable;
  1016. pc.addTagInteger( "cantHit", targets_not_hittable, cmovemalitypenum, unitCategoryTags );
  1017. for ( int i = 0; i < cmovemalitypenum; i++ )
  1018. if ( targets_not_hittable & ( 1 << i ))
  1019. targetingAccuracy[i] = 0;
  1020. }
  1021. pc.openBracket("WeaponEffectiveness" );
  1022. for ( int i = 0; i < cmovemalitypenum; i++ ) {
  1023. if ( pc.find ( unitCategoryTags[i] ))
  1024. pc.addInteger( unitCategoryTags[i], targetingAccuracy[i] );
  1025. }
  1026. pc.closeBracket();
  1027. } else {
  1028. pc.openBracket("WeaponEffectiveness" );
  1029. for ( int i = 0; i < cmovemalitypenum; i++ )
  1030. if ( targetingAccuracy[i] != 100 )
  1031. pc.addInteger( unitCategoryTags[i], targetingAccuracy[i] );
  1032. pc.closeBracket();
  1033. }
  1034. pc.addString("name", name, "");
  1035. }
  1036. void VehicleType :: HeightChangeMethod :: runTextIO ( PropertyContainer& pc )
  1037. {
  1038. pc.addTagInteger( "StartHeight", startHeight, choehenstufennum, heightTags );
  1039. pc.addInteger("HeightDelta", heightDelta );
  1040. pc.addInteger("MoveCost", moveCost, 0 );
  1041. pc.addBool ( "CanAttack", canAttack );
  1042. pc.addInteger("Dist", dist );
  1043. }
  1044. const int vehicleHeightChangeMethodVersion = 1;
  1045. void VehicleType :: HeightChangeMethod :: read ( tnstream& stream )
  1046. {
  1047. int version = stream.readInt();
  1048. if ( version > vehicleHeightChangeMethodVersion || version < 1 ) {
  1049. ASCString s = "invalid version for reading VehicleType :: HeightChangeMethod : ";
  1050. s += strrr ( version );
  1051. throw ASCmsgException ( s );
  1052. }
  1053. startHeight = stream.readInt();
  1054. heightDelta = stream.readInt();
  1055. moveCost = stream.readInt();
  1056. canAttack = stream.readInt();
  1057. dist = stream.readInt();
  1058. }
  1059. void VehicleType :: HeightChangeMethod :: write ( tnstream& stream ) const
  1060. {
  1061. stream.writeInt ( vehicleHeightChangeMethodVersion );
  1062. stream.writeInt ( startHeight );
  1063. stream.writeInt ( heightDelta );
  1064. stream.writeInt ( moveCost );
  1065. stream.writeInt ( canAttack );
  1066. stream.writeInt ( dist );
  1067. }
  1068. Resources VehicleType :: calcProductionCost()
  1069. {
  1070. static UnitCostCalculator* standard = new StandardUnitCostCalculator();
  1071. // static UnitCostCalculator* pbp2 = new UnitCostCalculator2
  1072. return standard->productionCost( this );
  1073. }