damage.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/damage.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 6/21/02 2:42p $*
  29. * *
  30. * $Revision:: 114 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. /*
  36. ** Includes
  37. */
  38. #include "damage.h"
  39. #include "assets.h"
  40. #include "debug.h"
  41. #include "smartgameobj.h"
  42. #include "combat.h"
  43. #include "slist.h"
  44. #include "wwpacket.h"
  45. #include "playerdata.h"
  46. #include "soldier.h"
  47. #include "weapons.h"
  48. #include "diaglog.h"
  49. #include "playertype.h"
  50. #include <string.h>
  51. #include <stdio.h>
  52. #include "wwmath.h"
  53. #include "encoderlist.h"
  54. #include "bitpackids.h"
  55. #include "gametype.h"
  56. #include "csdamageevent.h"
  57. #ifdef WWDEBUG
  58. bool DefenseObjectClass::OneShotKills = false;
  59. #endif // _WWDEBUG
  60. ArmorWarheadManager ArmorManagerObject;
  61. DynamicVectorClass<StringClass> ArmorNames;
  62. DynamicVectorClass<StringClass> WarheadNames;
  63. DynamicVectorClass<int> ArmorSaveIDs;
  64. DynamicVectorClass<int> WarheadSaveIDs;
  65. DynamicVectorClass<bool> SoftArmorTable;
  66. // For each Warhead
  67. DynamicVectorClass<int> SpecialDamageTypes;
  68. DynamicVectorClass<float> SpecialDamageProbability;
  69. DynamicVectorClass<float> VisceroidProbability;
  70. // For each special damage type
  71. int SpecialDamageWarhead[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
  72. float SpecialDamageDuration[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
  73. float SpecialDamageScale[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
  74. StringClass SpecialDamageExplosion[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
  75. // Skins that are imprevious to special damage
  76. DynamicVectorClass<ArmorType> ImperviousSkins[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
  77. safe_float * ArmorWarheadManager::Multipliers = NULL;
  78. safe_float * ArmorWarheadManager::Absorbsion = NULL;
  79. #define ARMOR_INI_FILENAME "armor.ini"
  80. #define SECTION_SOFT_ARMOR_TYPES "Soft_Armor_Types"
  81. #define SECTION_HARD_ARMOR_TYPES "Hard_Armor_Types"
  82. #define SECTION_ARMOR_TYPES "Armor_Types"
  83. #define SECTION_WARHEAD_TYPES "Warhead_Types"
  84. #define SECTION_SCALE "Scale_%s"
  85. #define SECTION_SHIELD "Shield_%s"
  86. #define SECTION_ARMOR_SAVE_IDS "Armor_Save_IDs"
  87. #define SECTION_WARHEAD_SAVE_IDS "Warhead_Save_IDs"
  88. #define SECTION_SOFT_ARMOR "Soft_Armor"
  89. #define SECTION_SPECIAL_DAMAGE_TYPE "Special_Damage_Type"
  90. #define SECTION_SPECIAL_DAMAGE_PROBABILITY "Special_Damage_Probability"
  91. #define SECTION_VISCEROID_PROBABILITY "Visceroid_Probability"
  92. #define ENTRY_WARHEAD "Warhead"
  93. #define ENTRY_DURATION "Duration"
  94. #define ENTRY_SCALE "Scale"
  95. #define ENTRY_EXPLOSION "Explosion"
  96. /*
  97. **
  98. */
  99. void ArmorWarheadManager::Init( void )
  100. {
  101. Shutdown();
  102. INIClass * armorINI = Get_INI( ARMOR_INI_FILENAME );
  103. if (armorINI != NULL) {
  104. WWASSERT( armorINI && armorINI->Section_Count() > 0 );
  105. int count, entry;
  106. // Load Armor Types
  107. count = armorINI->Entry_Count( SECTION_ARMOR_TYPES );
  108. for ( entry = 0; entry < count; entry++ ) {
  109. char entry_name[80];
  110. armorINI->Get_String( SECTION_ARMOR_TYPES,
  111. armorINI->Get_Entry( SECTION_ARMOR_TYPES, entry),
  112. "", entry_name, sizeof( entry_name ) );
  113. ArmorNames.Add( entry_name );
  114. ArmorSaveIDs.Add( entry );
  115. SoftArmorTable.Add( false );
  116. }
  117. // Load Warhead Types
  118. count = armorINI->Entry_Count( SECTION_WARHEAD_TYPES );
  119. for ( entry = 0; entry < count; entry++ ) {
  120. char entry_name[80];
  121. armorINI->Get_String( SECTION_WARHEAD_TYPES,
  122. armorINI->Get_Entry( SECTION_WARHEAD_TYPES, entry),
  123. "", entry_name, sizeof( entry_name ) );
  124. WarheadNames.Add( entry_name );
  125. WarheadSaveIDs.Add( entry );
  126. }
  127. int armor_num;
  128. // Load Armor Save IDs
  129. for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
  130. int id = armorINI->Get_Int( SECTION_ARMOR_SAVE_IDS, ArmorNames[armor_num], -100 );
  131. if ( id == -100 ) {
  132. Debug_Say(( "Missing Armor_Save_ID for %s\n", ArmorNames[armor_num] ));
  133. }
  134. ArmorSaveIDs[ armor_num ] = id;
  135. }
  136. // Load Warhead Save IDs
  137. int warhead_num;
  138. for ( warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
  139. int id = armorINI->Get_Int( SECTION_WARHEAD_SAVE_IDS, WarheadNames[warhead_num], -100 );
  140. if ( id == -100 ) {
  141. Debug_Say(( "Missing Warhead_Save_ID for %s\n", WarheadNames[warhead_num] ));
  142. }
  143. WarheadSaveIDs[ warhead_num ] = id;
  144. }
  145. // Load Soft Armor Table
  146. count = armorINI->Entry_Count( SECTION_SOFT_ARMOR );
  147. for ( entry = 0; entry < count; entry++ ) {
  148. char entry_name[80];
  149. armorINI->Get_String( SECTION_SOFT_ARMOR,
  150. armorINI->Get_Entry( SECTION_SOFT_ARMOR, entry),
  151. "", entry_name, sizeof( entry_name ) );
  152. ArmorType type = Get_Armor_Type( entry_name );
  153. SoftArmorTable[ type ] = true;
  154. }
  155. // Load Multipliers
  156. Multipliers = new safe_float[ Get_Num_Armor_Types() * Get_Num_Warhead_Types() ];
  157. for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
  158. char section_name[80];
  159. sprintf( section_name, SECTION_SCALE, ArmorNames[armor_num] );
  160. for ( int warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
  161. Multipliers[ armor_num * Get_Num_Warhead_Types() + warhead_num ] =
  162. armorINI->Get_Float( section_name, WarheadNames[warhead_num], 1.0f );
  163. }
  164. }
  165. // Load Shield Absorbsion
  166. Absorbsion = new safe_float[ Get_Num_Armor_Types() * Get_Num_Warhead_Types() ];
  167. for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
  168. char section_name[80];
  169. sprintf( section_name, SECTION_SHIELD, ArmorNames[armor_num] );
  170. for ( int warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
  171. Absorbsion[ armor_num * Get_Num_Warhead_Types() + warhead_num ] =
  172. armorINI->Get_Float( section_name, WarheadNames[warhead_num], 0.0f );
  173. }
  174. }
  175. // Load Warhead Special Damage
  176. for ( warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
  177. SpecialDamageTypes.Add( SPECIAL_DAMAGE_TYPE_NONE );
  178. SpecialDamageProbability.Add( 0 );
  179. VisceroidProbability.Add( 0 );
  180. StringClass special_damage_type(0,true);
  181. armorINI->Get_String(special_damage_type, SECTION_SPECIAL_DAMAGE_TYPE, WarheadNames[warhead_num] );
  182. if ( !special_damage_type.Is_Empty() ) {
  183. if ( !stricmp( special_damage_type, "FIRE" ) ) {
  184. SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_FIRE;
  185. }
  186. if ( !stricmp( special_damage_type, "CHEM" ) ) {
  187. SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CHEM;
  188. }
  189. if ( !stricmp( special_damage_type, "CNC_FIRE" ) ) {
  190. SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CNC_FIRE;
  191. }
  192. if ( !stricmp( special_damage_type, "CNC_CHEM" ) ) {
  193. SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CNC_CHEM;
  194. }
  195. if ( !stricmp( special_damage_type, "ELECTRIC" ) ) {
  196. SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_ELECTRIC;
  197. }
  198. }
  199. SpecialDamageProbability[warhead_num] =
  200. armorINI->Get_Float( SECTION_SPECIAL_DAMAGE_PROBABILITY, WarheadNames[warhead_num], 0.0f );
  201. VisceroidProbability[warhead_num] =
  202. armorINI->Get_Float( SECTION_VISCEROID_PROBABILITY, WarheadNames[warhead_num], 0.0f );
  203. // Debug_Say(( "Warhead %s %d %f\n", WarheadNames[warhead_num],
  204. // SpecialDamageTypes[warhead_num], SpecialDamageProbability[warhead_num] ));
  205. }
  206. int i;
  207. for ( i = 0; i < NUM_SPECIAL_DAMAGE_TYPES; i++ ) {
  208. SpecialDamageWarhead[i] = 0;
  209. SpecialDamageDuration[i] = 0;
  210. SpecialDamageScale[i] = 0;
  211. SpecialDamageExplosion[i] = "";
  212. }
  213. StringClass temp_string(0,true);
  214. for ( i = SPECIAL_DAMAGE_TYPE_FIRE; i < NUM_SPECIAL_DAMAGE_TYPES; i++ ) {
  215. const char * SpecialDamageSectionNames[NUM_SPECIAL_DAMAGE_TYPES] = {
  216. "Special_Damage_None",
  217. "Special_Damage_Fire",
  218. "Special_Damage_Chem",
  219. "Special_Damage_Electric",
  220. "Special_Damage_CNC_Fire",
  221. "Special_Damage_CNC_Chem",
  222. };
  223. StringClass section(SpecialDamageSectionNames[i],true);
  224. SpecialDamageWarhead[i] = Get_Warhead_Type( armorINI->Get_String( temp_string, section, ENTRY_WARHEAD ) );
  225. SpecialDamageDuration[i] = armorINI->Get_Float( section, ENTRY_DURATION, 0 );
  226. SpecialDamageScale[i] = armorINI->Get_Float( section, ENTRY_SCALE, 1 );
  227. SpecialDamageExplosion[i] = armorINI->Get_String( temp_string, section, ENTRY_EXPLOSION );
  228. }
  229. //
  230. // Copy fire into super fire
  231. //
  232. SpecialDamageWarhead[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageWarhead[SPECIAL_DAMAGE_TYPE_FIRE];
  233. SpecialDamageDuration[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageDuration[SPECIAL_DAMAGE_TYPE_FIRE];
  234. SpecialDamageScale[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageScale[SPECIAL_DAMAGE_TYPE_FIRE];
  235. SpecialDamageExplosion[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageExplosion[SPECIAL_DAMAGE_TYPE_FIRE];
  236. // Load Impervious skin
  237. for ( int damage = SPECIAL_DAMAGE_TYPE_FIRE; damage < NUM_SPECIAL_DAMAGE_TYPES; damage++ ) {
  238. const char * ImperviousSectionNames[NUM_SPECIAL_DAMAGE_TYPES] = {
  239. "Impervious_None",
  240. "Impervious_Fire",
  241. "Impervious_Chem",
  242. "Impervious_Electric",
  243. "Impervious_CNC_Fire",
  244. "Impervious_CNC_Chem",
  245. };
  246. StringClass section = ImperviousSectionNames[damage];
  247. int count = armorINI->Entry_Count( section );
  248. for ( entry = 0; entry < count; entry++ ) {
  249. StringClass entry_name = armorINI->Get_String( temp_string, section, armorINI->Get_Entry( section, entry) );
  250. if ( !entry_name.Is_Empty() ) {
  251. ImperviousSkins[damage].Add( Get_Armor_Type( entry_name ) );
  252. }
  253. }
  254. // for ( int i = 0; i < ImperviousSkins[damage].Count(); i++ ) {
  255. // Debug_Say(( "Loaded %s %d\n", (const char *)section, ImperviousSkins[damage][i] ));
  256. // }
  257. }
  258. Release_INI( armorINI );
  259. } else {
  260. Debug_Say(("ArmorWarheadManager::Init - Unable to load %s\n", ARMOR_INI_FILENAME));
  261. }
  262. }
  263. void ArmorWarheadManager::Shutdown( void )
  264. {
  265. if ( Multipliers != NULL ) {
  266. delete [] Multipliers;
  267. Multipliers = NULL;
  268. }
  269. if ( Absorbsion != NULL ) {
  270. delete [] Absorbsion;
  271. Absorbsion = NULL;
  272. }
  273. ArmorNames.Delete_All();
  274. WarheadNames.Delete_All();
  275. ArmorSaveIDs.Delete_All();
  276. WarheadSaveIDs.Delete_All();
  277. SoftArmorTable.Delete_All();
  278. SpecialDamageTypes.Delete_All();
  279. SpecialDamageProbability.Delete_All();
  280. VisceroidProbability.Delete_All();
  281. for (int i=0; i<ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES; i++) {
  282. ImperviousSkins[i].Delete_All();
  283. }
  284. }
  285. int ArmorWarheadManager::Get_Num_Armor_Types( void )
  286. {
  287. return ArmorNames.Count();
  288. }
  289. int ArmorWarheadManager::Get_Num_Warhead_Types( void )
  290. {
  291. return WarheadNames.Count();
  292. }
  293. ArmorType ArmorWarheadManager::Get_Armor_Type( const char *name )
  294. {
  295. for ( int index = 0; index < ArmorNames.Count(); index++ ) {
  296. if ( !stricmp( ArmorNames[index], name ) ) {
  297. return index;
  298. }
  299. }
  300. Debug_Say(( "Armor %s Not Found\n", name ));
  301. // WWASSERT( 0 ); //Armor type not found!
  302. return 0;
  303. }
  304. WarheadType ArmorWarheadManager::Get_Warhead_Type( const char *name )
  305. {
  306. for ( int index = 0; index < WarheadNames.Count(); index++ ) {
  307. if ( !stricmp( WarheadNames[index], name ) ) {
  308. return index;
  309. }
  310. }
  311. Debug_Say(( "Warhead %s Not Found\n", name ));
  312. // WWASSERT( 0 ); //Warhead type not found!
  313. return 0;
  314. }
  315. const char * ArmorWarheadManager::Get_Armor_Name( ArmorType type )
  316. {
  317. WWASSERT( (int)type >= 0 );
  318. WWASSERT( (int)type < Get_Num_Armor_Types() );
  319. return ArmorNames[type];
  320. }
  321. const char * ArmorWarheadManager::Get_Warhead_Name( WarheadType type )
  322. {
  323. WWASSERT( (int)type >= 0 );
  324. WWASSERT( (int)type < Get_Num_Warhead_Types() );
  325. return WarheadNames[type];
  326. }
  327. float ArmorWarheadManager::Get_Damage_Multiplier( ArmorType armor, WarheadType warhead )
  328. {
  329. WWASSERT( (int)armor >= 0 );
  330. WWASSERT( (int)armor < Get_Num_Armor_Types() );
  331. WWASSERT( (int)warhead >= 0 );
  332. WWASSERT( (int)warhead < Get_Num_Warhead_Types() );
  333. return( Multipliers[ (unsigned int)armor * Get_Num_Warhead_Types() + (unsigned int)warhead ] );
  334. /*
  335. int index = armor * Get_Num_Warhead_Types() + warhead;
  336. float multiplier = Multipliers[index];
  337. WWASSERT(WWMath::Is_Valid_Float(multiplier));
  338. Debug_Say(("index = %d, armor = %d, warhead = %d, multiplier = %5.2f\n",
  339. index, armor, warhead, multiplier));
  340. return multiplier;
  341. */
  342. }
  343. float ArmorWarheadManager::Get_Shield_Absorbsion( ArmorType armor, WarheadType warhead )
  344. {
  345. WWASSERT( (int)armor >= 0 );
  346. WWASSERT( (int)armor < Get_Num_Armor_Types() );
  347. WWASSERT( (int)warhead >= 0 );
  348. WWASSERT( (int)warhead < Get_Num_Warhead_Types() );
  349. return( Absorbsion[ (unsigned int)armor * Get_Num_Warhead_Types() + (unsigned int)warhead ] );
  350. }
  351. bool ArmorWarheadManager::Is_Armor_Soft( ArmorType armor )
  352. {
  353. return SoftArmorTable[armor];
  354. }
  355. int ArmorWarheadManager::Get_Armor_Save_ID( ArmorType type )
  356. {
  357. return ArmorSaveIDs[ type ];
  358. }
  359. ArmorType ArmorWarheadManager::Find_Armor_Save_ID( int id )
  360. {
  361. for ( int index = 0; index < ArmorSaveIDs.Count(); index++ ) {
  362. if ( ArmorSaveIDs[ index ] == id ) {
  363. return index;
  364. }
  365. }
  366. //Debug_Say(( "FAILED to find ARMOR_SAVE_ID for %d\n", id ));
  367. return 0;
  368. }
  369. int ArmorWarheadManager::Get_Warhead_Save_ID( WarheadType type )
  370. {
  371. return WarheadSaveIDs[ type ];
  372. }
  373. ArmorType ArmorWarheadManager::Find_Warhead_Save_ID( int id )
  374. {
  375. for ( int index = 0; index < WarheadSaveIDs.Count(); index++ ) {
  376. if ( WarheadSaveIDs[ index ] == id ) {
  377. return index;
  378. }
  379. }
  380. Debug_Say(( "FAILED to find WARHEAD_SAVE_ID for %d\n", id ));
  381. return 0;
  382. }
  383. ArmorWarheadManager::SpecialDamageType ArmorWarheadManager::Get_Special_Damage_Type( WarheadType type )
  384. {
  385. WWASSERT( (int)type >= 0 );
  386. WWASSERT( (int)type < Get_Num_Warhead_Types() );
  387. return (SpecialDamageType)SpecialDamageTypes[type];
  388. }
  389. float ArmorWarheadManager::Get_Special_Damage_Probability( WarheadType type )
  390. {
  391. WWASSERT( (int)type >= 0 );
  392. WWASSERT( (int)type < Get_Num_Warhead_Types() );
  393. return SpecialDamageProbability[type];
  394. }
  395. WarheadType ArmorWarheadManager::Get_Special_Damage_Warhead( SpecialDamageType type )
  396. {
  397. WWASSERT( (int)type >= 0 );
  398. WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
  399. return SpecialDamageWarhead[type];
  400. }
  401. float ArmorWarheadManager::Get_Special_Damage_Duration( SpecialDamageType type )
  402. {
  403. WWASSERT( (int)type >= 0 );
  404. WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
  405. return SpecialDamageDuration[type];
  406. }
  407. float ArmorWarheadManager::Get_Special_Damage_Scale( SpecialDamageType type )
  408. {
  409. WWASSERT( (int)type >= 0 );
  410. WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
  411. return SpecialDamageScale[type];
  412. }
  413. const char * ArmorWarheadManager::Get_Special_Damage_Explosion( SpecialDamageType type )
  414. {
  415. WWASSERT( (int)type >= 0 );
  416. WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
  417. return SpecialDamageExplosion[type];
  418. }
  419. float ArmorWarheadManager::Get_Visceroid_Probability( WarheadType type )
  420. {
  421. WWASSERT( (int)type >= 0 );
  422. WWASSERT( (int)type < Get_Num_Warhead_Types() );
  423. return VisceroidProbability[type];
  424. }
  425. bool ArmorWarheadManager::Is_Skin_Impervious( SpecialDamageType type, ArmorType skin )
  426. {
  427. WWASSERT( (int)type >= 0 );
  428. WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
  429. for ( int i = 0; i < ImperviousSkins[type].Count(); i++ ) {
  430. if ( ImperviousSkins[type][i] == skin ) {
  431. return true;
  432. }
  433. }
  434. return false;
  435. }
  436. /*
  437. **
  438. */
  439. OffenseObjectClass::OffenseObjectClass( const OffenseObjectClass & base ) :
  440. Damage( base.Damage ),
  441. Warhead( base.Warhead )
  442. {
  443. Set_Owner( base.Get_Owner() );
  444. }
  445. /*
  446. ** Save and Load
  447. */
  448. enum {
  449. CHUNKID_VARIABLES = 914991020,
  450. CHUNKID_OWNER,
  451. MICROCHUNKID_WARHEAD = 1,
  452. MICROCHUNKID_DAMAGE,
  453. MICROCHUNKID_HEALTH,
  454. MICROCHUNKID_HEALTH_MAX,
  455. MICROCHUNKID_SKIN,
  456. MICROCHUNKID_SHIELD_STRENGTH,
  457. MICROCHUNKID_SHIELD_STRENGTH_MAX,
  458. MICROCHUNKID_SHIELD_TYPE,
  459. XXXMICROCHUNKID_LAST_HEALTH,
  460. XXXMICROCHUNKID_LAST_SKIN,
  461. XXXMICROCHUNKID_LAST_SHIELD_STRENGTH,
  462. XXXMICROCHUNKID_LAST_SHIELD_TYPE,
  463. MICROCHUNKID_DAMAGE_POINTS,
  464. MICROCHUNKID_DEATH_POINTS,
  465. };
  466. bool OffenseObjectClass::Save( ChunkSaveClass & csave )
  467. {
  468. csave.Begin_Chunk( CHUNKID_VARIABLES );
  469. int save_id = ArmorWarheadManager::Get_Warhead_Save_ID( Warhead );
  470. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WARHEAD, save_id );
  471. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DAMAGE, Damage, float );
  472. csave.End_Chunk();
  473. csave.Begin_Chunk( CHUNKID_OWNER );
  474. Owner.Save( csave );
  475. csave.End_Chunk();
  476. return true;
  477. }
  478. bool OffenseObjectClass::Load( ChunkLoadClass &cload )
  479. {
  480. int save_id = -2;
  481. while (cload.Open_Chunk()) {
  482. switch(cload.Cur_Chunk_ID()) {
  483. case CHUNKID_VARIABLES:
  484. while (cload.Open_Micro_Chunk()) {
  485. switch(cload.Cur_Micro_Chunk_ID()) {
  486. READ_MICRO_CHUNK( cload, MICROCHUNKID_WARHEAD, save_id );
  487. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DAMAGE, Damage, float);
  488. default:
  489. Debug_Say(( "Unrecognized Offense Variable chunkID\n" ));
  490. break;
  491. }
  492. cload.Close_Micro_Chunk();
  493. }
  494. break;
  495. case CHUNKID_OWNER:
  496. Owner.Load( cload );
  497. break;
  498. default:
  499. Debug_Say(( "Unrecognized Offense chunkID\n" ));
  500. break;
  501. }
  502. cload.Close_Chunk();
  503. }
  504. Warhead = ArmorWarheadManager::Find_Warhead_Save_ID( save_id );
  505. return true;
  506. }
  507. void DefenseObjectClass::Init( const DefenseObjectDefClass & def, DamageableGameObj * owner )
  508. {
  509. Health = def.Health;
  510. HealthMax = def.HealthMax;
  511. Skin = def.Skin;
  512. ShieldStrength = def.ShieldStrength;
  513. ShieldStrengthMax = def.ShieldStrengthMax;
  514. ShieldType = def.ShieldType;
  515. DamagePoints = def.DamagePoints;
  516. DeathPoints = def.DeathPoints;
  517. Set_Owner( owner );
  518. }
  519. bool DefenseObjectClass::Save( ChunkSaveClass & csave )
  520. {
  521. csave.Begin_Chunk( CHUNKID_VARIABLES );
  522. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_HEALTH, Health, float );
  523. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_HEALTH_MAX, HealthMax, float );
  524. int skin_save_id = ArmorWarheadManager::Get_Armor_Save_ID( Skin );
  525. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SKIN, skin_save_id );
  526. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_STRENGTH, ShieldStrength, float );
  527. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_STRENGTH_MAX, ShieldStrengthMax, float );
  528. int shield_save_id = ArmorWarheadManager::Get_Armor_Save_ID( ShieldType );
  529. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_TYPE, shield_save_id );
  530. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DAMAGE_POINTS, DamagePoints, float );
  531. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DEATH_POINTS, DeathPoints, float );
  532. csave.End_Chunk();
  533. csave.Begin_Chunk( CHUNKID_OWNER );
  534. Owner.Save( csave );
  535. csave.End_Chunk();
  536. return true;
  537. }
  538. bool DefenseObjectClass::Load( ChunkLoadClass &cload )
  539. {
  540. int skin_save_id = -2;
  541. int shield_save_id = -2;
  542. while (cload.Open_Chunk()) {
  543. switch(cload.Cur_Chunk_ID()) {
  544. case CHUNKID_VARIABLES:
  545. while (cload.Open_Micro_Chunk()) {
  546. switch(cload.Cur_Micro_Chunk_ID()) {
  547. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_HEALTH, Health, float );
  548. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_HEALTH_MAX, HealthMax, float );
  549. READ_MICRO_CHUNK( cload, MICROCHUNKID_SKIN, skin_save_id );
  550. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_STRENGTH, ShieldStrength, float );
  551. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_STRENGTH_MAX, ShieldStrengthMax, float );
  552. READ_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_TYPE, shield_save_id );
  553. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DAMAGE_POINTS, DamagePoints, float );
  554. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DEATH_POINTS, DeathPoints, float );
  555. default:
  556. Debug_Say(( "Unrecognized Defense Variable chunkID\n" ));
  557. break;
  558. }
  559. cload.Close_Micro_Chunk();
  560. }
  561. break;
  562. case CHUNKID_OWNER:
  563. Owner.Load( cload );
  564. break;
  565. default:
  566. Debug_Say(( "Unrecognized Defense chunkID\n" ));
  567. break;
  568. }
  569. cload.Close_Chunk();
  570. }
  571. Skin = ArmorWarheadManager::Find_Armor_Save_ID( skin_save_id );
  572. ShieldType = ArmorWarheadManager::Find_Armor_Save_ID( shield_save_id );
  573. return true;
  574. }
  575. bool DefenseObjectClass::Is_Soft( void )
  576. {
  577. if ( !ArmorWarheadManager::Is_Armor_Soft( Skin ) ) {
  578. return false;
  579. }
  580. if ( ( (float)ShieldStrength > 0 ) && ( !ArmorWarheadManager::Is_Armor_Soft( ShieldType ) ) ) {
  581. return false;
  582. }
  583. return true;
  584. }
  585. /*
  586. **
  587. */
  588. float DefenseObjectClass::Apply_Damage( const OffenseObjectClass & offense, float scale, int alternate_skin )
  589. {
  590. #if 1
  591. // If singleplay or non trusted clients, just apply damage normally, if server
  592. bool normal = IS_SOLOPLAY || !cCsDamageEvent::Get_Are_Clients_Trusted();
  593. DamageableGameObj * owner = Get_Owner();
  594. // Buildings have no owner
  595. if ( owner == NULL || owner->As_BuildingGameObj() ) {
  596. normal = true;
  597. }
  598. if ( offense.ForceServerDamage || !offense.EnableClientDamage ) {
  599. normal = true;
  600. }
  601. if ( normal ) {
  602. if ( CombatManager::I_Am_Server() ) {
  603. return Do_Damage( offense, scale, alternate_skin );
  604. } else {
  605. return Health;
  606. }
  607. }
  608. // Client Damage
  609. // If I am a client, and damager is my guy, tell the server that damage should apply
  610. // If I am a server, and the damager has a client, ignore the damage
  611. bool has_client = false;
  612. bool my_client = false;
  613. if ( offense.Get_Owner() != NULL && offense.Get_Owner()->As_SmartGameObj() ) {
  614. int control_owner = offense.Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
  615. has_client = control_owner > 0;
  616. my_client = control_owner == CombatManager::Get_My_Id();
  617. }
  618. if ( CombatManager::I_Am_Server() ) {
  619. if ( my_client || !has_client ) {
  620. return Do_Damage( offense, scale, alternate_skin );
  621. } else {
  622. return Health; // assume we will be notified by the owner Don't apply the damage
  623. }
  624. }
  625. // I am a client. If this is my object, request damage
  626. if ( my_client ) {
  627. Request_Damage( offense, scale );
  628. }
  629. return Health; // Don't apply the damage
  630. #else
  631. // Client Authorative damage is no longer done here.
  632. if ( CombatManager::I_Am_Server() ) {
  633. return Do_Damage( offense, scale, alternate_skin );
  634. } else {
  635. return Health;
  636. }
  637. #endif
  638. }
  639. /*
  640. **
  641. */
  642. void DefenseObjectClass::Request_Damage( const OffenseObjectClass & offense, float scale )
  643. {
  644. WWASSERT(CombatManager::I_Am_Only_Client());
  645. WWASSERT(cCsDamageEvent::Get_Are_Clients_Trusted() == true);
  646. cCsDamageEvent * event = new cCsDamageEvent();
  647. if ( event ) {
  648. int damager_id = 0;
  649. if ( offense.Get_Owner() ) {
  650. damager_id = offense.Get_Owner()->Get_ID();
  651. }
  652. int damagee_id = 0;
  653. if ( Get_Owner() ) {
  654. damagee_id = Get_Owner()->Get_ID();
  655. }
  656. int warhead = offense.Get_Warhead();
  657. float damage = offense.Get_Damage() * scale;
  658. event->Init( damager_id, damagee_id, damage, warhead );
  659. // Debug_Say(( "Requesting damage of %f from %d to %d\n", damage, damager_id, damagee_id ));
  660. }
  661. }
  662. /*
  663. **
  664. */
  665. float DefenseObjectClass::Do_Damage( const OffenseObjectClass & offense, float scale, int alternate_skin )
  666. {
  667. SmartGameObj * smart = NULL;
  668. if ( offense.Get_Owner() != NULL ) {
  669. smart = offense.Get_Owner()->As_SmartGameObj();
  670. }
  671. //TSS102201
  672. //
  673. // Keep track of who is attempting to damage who. This is used as a server
  674. // heuristic used in network priority calculations. It doesn't matter whether
  675. // or not damage is actually done.
  676. //
  677. int damager_id = -1;
  678. int damagee_id = -1;
  679. if (offense.Get_Owner() != NULL) {
  680. damager_id = offense.Get_Owner()->Get_Network_ID();
  681. }
  682. if (Get_Owner() != NULL) {
  683. damagee_id = Get_Owner()->Get_Network_ID();
  684. }
  685. if (offense.Get_Owner() != NULL && damagee_id != -1) {
  686. offense.Get_Owner()->Set_Last_Object_Id_I_Damaged(damagee_id);
  687. }
  688. if (Get_Owner() != NULL && damager_id != -1) {
  689. Get_Owner()->Set_Last_Object_Id_I_Got_Damaged_By(damager_id);
  690. }
  691. float damage = offense.Get_Damage() * scale;
  692. // Scale damage by difficulty if star is applying
  693. if ( IS_MISSION && offense.Get_Owner() == COMBAT_STAR ) {
  694. switch ( CombatManager::Get_Difficulty_Level() ) {
  695. case 0: damage *= 2.0f; break;
  696. case 1: damage *= 1.333f; break;
  697. case 2: damage *= 1.0f; break;
  698. };
  699. }
  700. float shield_damage = 0;
  701. float damage_scale = 1;
  702. float shield_damage_scale = 1;
  703. if (alternate_skin != -1) {
  704. damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( alternate_skin, offense.Get_Warhead() );
  705. shield_damage_scale = 0;
  706. } else {
  707. damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
  708. shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
  709. }
  710. // check for punish = no more damage
  711. if ( smart != NULL && smart->Get_Player_Data() ) {
  712. #define PUNISH_DELAY 60
  713. if ( smart->Get_Player_Data()->Get_Punish_Timer() > PUNISH_DELAY ) {
  714. damage_scale = 0;
  715. shield_damage_scale = 0;
  716. smart->Get_Player_Data()->Set_Money( 0 );
  717. smart->Get_Player_Data()->Set_Score( 0 );
  718. }
  719. }
  720. bool is_repair = false;
  721. // check for repair on either health of shield
  722. // Note: humans don't repair health, but vehicles do.
  723. if ( (damage * damage_scale < 0) || (damage * shield_damage_scale < 0) ) { // We are repairing
  724. is_repair = true;
  725. Mark_Owner_Dirty();
  726. // Apply first to health, then to shield
  727. if ( Health < HealthMax && (damage_scale != 0) ) {
  728. damage *= damage_scale;
  729. float min_damage = (float)Health - (float)HealthMax;
  730. damage = WWMath::Clamp( damage, min_damage, 0 );
  731. //Debug_Say(( "Repairing %f health\n", damage ));
  732. Health = (float)Health - damage;
  733. } else {
  734. damage *= shield_damage_scale;
  735. shield_damage = damage;
  736. damage = 0;
  737. float min_shield_damage = (float)ShieldStrength - (float)ShieldStrengthMax;
  738. shield_damage = WWMath::Clamp( shield_damage, min_shield_damage, 0 );
  739. //Debug_Say(( "Repairing %f shield\n", shield_damage ));
  740. ShieldStrength = (float)ShieldStrength - shield_damage;
  741. }
  742. } else {
  743. if ( smart && Get_Owner() &&
  744. (smart != Get_Owner() ) &&
  745. smart->Is_Teammate( Get_Owner() ) ) {
  746. // This is friendly fire!!
  747. if ( !CombatManager::Is_Friendly_Fire_Permitted() ) {
  748. // damage = 0;
  749. return Health;
  750. }
  751. }
  752. if ( damage != 0 ) {
  753. Mark_Owner_Dirty();
  754. }
  755. // if we have a shield, redirect a fraction of our damage;
  756. // If alternate skin (MCT) ignore sheild damage;
  757. if ( (float)ShieldStrength > 0.0f && alternate_skin == -1 ) {
  758. shield_damage = damage * ArmorWarheadManager::Get_Shield_Absorbsion( ShieldType, offense.Get_Warhead() );
  759. damage -= shield_damage;
  760. shield_damage *= shield_damage_scale;
  761. float shield_damage_to_apply = shield_damage; // how much scaled damage to apply
  762. if ( shield_damage > (float)ShieldStrength ) {
  763. shield_damage = ShieldStrength;
  764. }
  765. ShieldStrength = (float)ShieldStrength - shield_damage;
  766. if ( shield_damage_scale != 0 ) {
  767. shield_damage /= shield_damage_scale; // how much un scaled damage did we apply
  768. shield_damage_to_apply /= shield_damage_scale; // how much scaled damage did we try to apply
  769. }
  770. damage += shield_damage_to_apply - shield_damage;
  771. // Clamp Shield Strength
  772. ShieldStrength = WWMath::Clamp( (float)ShieldStrength, 0, (float)ShieldStrengthMax );
  773. }
  774. // scale the (remaining) damage
  775. // (gth) added alternate_skin feature which is used by buildings when their MCT is being damaged.
  776. damage *= damage_scale;
  777. if ( (float)Health < damage ) {
  778. damage = Health;
  779. }
  780. #ifdef WWDEBUG
  781. //
  782. // TSS090601
  783. //
  784. if (OneShotKills) {
  785. damage = Health;
  786. }
  787. #endif // _WWDEBUG
  788. Health = (float)Health - damage;
  789. //
  790. // Don't allow this object to die (if necessary)
  791. //
  792. if ( CanObjectDie == false ) {
  793. Health = max ( (float)Health, 1.0F );
  794. }
  795. }
  796. if ( COMBAT_STAR != NULL && // dedicated server test
  797. Get_Owner() == COMBAT_STAR ) {
  798. Vector3 pos;
  799. Get_Owner()->Get_Position( &pos );
  800. int hitter_id = 0;
  801. const char * weapon_name = "";
  802. if ( offense.Get_Owner() ) {
  803. hitter_id = offense.Get_Owner()->Get_ID();
  804. if ( offense.Get_Owner()->Get_Weapon() ) {
  805. weapon_name = offense.Get_Owner()->Get_Weapon()->Get_Definition()->Get_Name();
  806. }
  807. }
  808. float points = damage;
  809. float armor = Get_Shield_Strength();
  810. DIAG_LOG(( "DRCV", "%s; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", weapon_name, hitter_id, points, armor, Health, pos.X, pos.Y, pos.Z ));
  811. }
  812. if (( smart == COMBAT_STAR ) && ( smart != NULL )) {
  813. Vector3 pos;
  814. smart->Get_Position( &pos );
  815. const char * weapon_name = "";
  816. int ammo = 0;
  817. if ( smart->Get_Weapon() ) {
  818. weapon_name = smart->Get_Weapon()->Get_Definition()->Get_Name();
  819. ammo = smart->Get_Weapon()->Get_Total_Rounds();
  820. }
  821. int hittee_id = 0;
  822. Vector3 victim_pos(0,0,0);
  823. const char * team_name = "";
  824. if ( Get_Owner() ) {
  825. hittee_id = Get_Owner()->Get_ID();
  826. Get_Owner()->Get_Position( &victim_pos );
  827. team_name = Player_Type_Name( Get_Owner()->Get_Player_Type() );
  828. }
  829. float points = damage;
  830. float armor = Get_Shield_Strength();
  831. DIAG_LOG(( "DEFC", "%1.2f; %1.2f; %1.2f; %s; %d; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s ", pos.X, pos.Y, pos.Z, weapon_name, ammo, hittee_id, points, armor, Health, victim_pos.X, victim_pos.Y, victim_pos.Z, team_name ));
  832. }
  833. // Clamp Health to Max
  834. Health = WWMath::Clamp( (float)Health, 0, (float)HealthMax );
  835. if ( damage > 0 && (float)Health <= 0 ) {
  836. int victim_id = 0;
  837. Vector3 victim_pos(0,0,0);
  838. if ( Get_Owner() != NULL ) {
  839. victim_id = Get_Owner()->Get_ID();
  840. Get_Owner()->Get_Position( &victim_pos );
  841. }
  842. int killer_id = 0;
  843. Vector3 killer_pos(0,0,0);
  844. const char * weapon_name = "";
  845. if ( smart != NULL ) {
  846. killer_id = smart->Get_ID();
  847. smart->Get_Position( &killer_pos );
  848. if ( smart->Get_Weapon() ) {
  849. weapon_name = smart->Get_Weapon()->Get_Definition()->Get_Name();
  850. }
  851. }
  852. DIAG_LOG(( "OBDE", "%d; %1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %s", victim_id, victim_pos.X, victim_pos.Y, victim_pos.Z, killer_id, killer_pos.X, killer_pos.Y, killer_pos.Z, weapon_name ));
  853. if ( Get_Owner() == COMBAT_STAR ) {
  854. DIAG_LOG(( "STDE", "%1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %s", victim_pos.X, victim_pos.Y, victim_pos.Z, killer_id, killer_pos.X, killer_pos.Y, killer_pos.Z, weapon_name ));
  855. }
  856. }
  857. if (damage > 0 &&
  858. (float) Health <= 0 &&
  859. smart != NULL &&
  860. smart->As_SoldierGameObj() != NULL &&
  861. Get_Owner() != NULL &&
  862. Get_Owner()->As_SoldierGameObj() != NULL) {
  863. CombatManager::On_Soldier_Kill(smart->As_SoldierGameObj(), Get_Owner()->As_SoldierGameObj());
  864. }
  865. // Apply Points for damage/death
  866. if ( smart != NULL && smart->Get_Player_Data() ) {
  867. float points_dir = 1;
  868. // If same team, make the points negative
  869. if ( Get_Owner() ) {
  870. if ( smart->Is_Teammate( Get_Owner() ) ) {
  871. points_dir = -1;
  872. } else if ( !smart->Is_Enemy( Get_Owner() ) ) {
  873. // No points for neutrals
  874. points_dir = 0;
  875. }
  876. }
  877. if (offense.Get_Owner() != Get_Owner()) {
  878. float points = (float)DamagePoints;
  879. if ( is_repair ) { // half points for repair
  880. points *= 0.5f;
  881. }
  882. smart->Get_Player_Data()->Apply_Damage_Points( points_dir * (damage + shield_damage) * points, Get_Owner() );
  883. }
  884. if ( damage > 0 && (float)Health <= 0 ) {
  885. if (offense.Get_Owner() != Get_Owner()) {
  886. smart->Get_Player_Data()->Apply_Death_Points( points_dir * (float)DeathPoints, Get_Owner() );
  887. }
  888. }
  889. }
  890. return Health;
  891. }
  892. bool DefenseObjectClass::Is_Repair( const OffenseObjectClass & offense, float scale )
  893. {
  894. float damage = offense.Get_Damage() * scale;
  895. float damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
  896. float shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
  897. // check for repair on either health of shield
  898. return ( (damage * damage_scale < 0) || (damage * shield_damage_scale < 0) );
  899. }
  900. bool DefenseObjectClass::Would_Damage( const OffenseObjectClass & offense, float scale )
  901. {
  902. float damage = offense.Get_Damage() * scale;
  903. float damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
  904. float shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
  905. SmartGameObj * smart = NULL;
  906. if ( offense.Get_Owner() != NULL ) {
  907. smart = offense.Get_Owner()->As_SmartGameObj();
  908. }
  909. if ( smart && Get_Owner() &&
  910. smart->Is_Teammate( Get_Owner() ) &&
  911. (smart != Get_Owner() ) ) {
  912. // This is friendly fire!!
  913. if ( !CombatManager::Is_Friendly_Fire_Permitted() ) {
  914. return false;
  915. }
  916. }
  917. if ( damage * damage_scale > 0 && (float)Health > 0 ) {
  918. return true;
  919. }
  920. if ( damage * shield_damage_scale > 0 && (float)ShieldStrength > 0 ) {
  921. return true;
  922. }
  923. return false;
  924. }
  925. void DefenseObjectClass::Import(BitStreamClass & packet)
  926. {
  927. bool is_health_zero = packet.Get(is_health_zero);
  928. int health = packet.Get(health, BITPACK_HEALTH);
  929. int shield_strength = packet.Get(shield_strength, BITPACK_SHIELD_STRENGTH);
  930. unsigned int shield_type;
  931. packet.Get(shield_type, BITPACK_SHIELD_TYPE);
  932. ShieldType = shield_type;
  933. Health = (float)health;
  934. ShieldStrength = (float)shield_strength;
  935. if (is_health_zero) {
  936. Health = 0;
  937. } else {
  938. Health = WWMath::Max(Health, 0.01f);
  939. }
  940. //WWASSERT(WWMath::Is_Valid_Float(ShieldStrength));
  941. //WWASSERT(packet.Is_Flushed());
  942. }
  943. void DefenseObjectClass::Export(BitStreamClass & packet)
  944. {
  945. //
  946. // N.B. The funky syntax is to prevent datasafe asserts
  947. //
  948. int health = cMathUtil::Round((double) (float)Health);
  949. int shield_strength = cMathUtil::Round((double) (float)ShieldStrength);
  950. //TSS012202
  951. //packet.Add((bool)(health == 0));
  952. packet.Add((bool)(((float)Health) == 0));
  953. packet.Add(health, BITPACK_HEALTH);
  954. packet.Add(shield_strength, BITPACK_SHIELD_STRENGTH);
  955. packet.Add((unsigned long)ShieldType, BITPACK_SHIELD_TYPE);
  956. //LastSentHealth = Health;
  957. //LastSentSkin = Skin;
  958. //LastSentShieldStrength = ShieldStrength;
  959. //LastSentShieldType = ShieldType;
  960. }
  961. /*
  962. //
  963. // This was only valid with 1 client. We would need dirty tests for every client.
  964. //
  965. bool DefenseObjectClass::Is_Defense_State_Dirty(void)
  966. {
  967. return (
  968. fabs(Health - LastSentHealth) > MISCUTIL_EPSILON ||
  969. Skin != LastSentSkin ||
  970. fabs(ShieldStrength - LastSentShieldStrength) > MISCUTIL_EPSILON ||
  971. ShieldType != LastSentShieldType);
  972. }
  973. */
  974. /*****************************************************************************************
  975. **
  976. ** DefenseObjectDefClass Implementation
  977. **
  978. *****************************************************************************************/
  979. enum
  980. {
  981. DEFENSEOBJECTDEF_CHUNK_VARIABLES = 7311607,
  982. DEFENSEOBJECTDEF_VARIABLE_HEALTH = 0x00,
  983. DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,
  984. DEFENSEOBJECTDEF_VARIABLE_SKIN,
  985. DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH,
  986. DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX,
  987. DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,
  988. DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,
  989. DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,
  990. };
  991. DefenseObjectDefClass::DefenseObjectDefClass(void) :
  992. Health( 100.0f ),
  993. HealthMax( 100.0f ),
  994. Skin( 0 ),
  995. ShieldStrength( 0 ),
  996. ShieldStrengthMax( 0 ),
  997. ShieldType( 0 ),
  998. DamagePoints( 0 ),
  999. DeathPoints( 0 )
  1000. {
  1001. }
  1002. DefenseObjectDefClass::~DefenseObjectDefClass(void)
  1003. {
  1004. }
  1005. bool DefenseObjectDefClass::Save(ChunkSaveClass &csave)
  1006. {
  1007. csave.Begin_Chunk(DEFENSEOBJECTDEF_CHUNK_VARIABLES);
  1008. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_HEALTH, Health, float);
  1009. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,HealthMax, float);
  1010. int skin_save_id = ArmorWarheadManager::Get_Armor_Save_ID( Skin );
  1011. WRITE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SKIN,skin_save_id);
  1012. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH, ShieldStrength, float);
  1013. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX, ShieldStrengthMax, float);
  1014. int shield_save_id = ArmorWarheadManager::Get_Armor_Save_ID( ShieldType );
  1015. WRITE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,shield_save_id);
  1016. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,DamagePoints, float);
  1017. WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,DeathPoints, float);
  1018. csave.End_Chunk();
  1019. return true;
  1020. }
  1021. bool DefenseObjectDefClass::Load(ChunkLoadClass &cload)
  1022. {
  1023. int skin_save_id = -2;
  1024. int shield_save_id = -2;
  1025. while (cload.Open_Chunk()) {
  1026. switch(cload.Cur_Chunk_ID())
  1027. {
  1028. case DEFENSEOBJECTDEF_CHUNK_VARIABLES:
  1029. while (cload.Open_Micro_Chunk()) {
  1030. switch(cload.Cur_Micro_Chunk_ID()) {
  1031. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_HEALTH,Health,float);
  1032. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,HealthMax,float);
  1033. READ_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SKIN,skin_save_id);
  1034. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH,ShieldStrength,float);
  1035. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX,ShieldStrengthMax,float);
  1036. READ_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,shield_save_id);
  1037. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,DamagePoints,float);
  1038. READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,DeathPoints,float);
  1039. }
  1040. cload.Close_Micro_Chunk();
  1041. }
  1042. break;
  1043. default:
  1044. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  1045. break;
  1046. };
  1047. cload.Close_Chunk();
  1048. }
  1049. Skin = ArmorWarheadManager::Find_Armor_Save_ID( skin_save_id );
  1050. ShieldType = ArmorWarheadManager::Find_Armor_Save_ID( shield_save_id );
  1051. return true;
  1052. }
  1053. void DefenseObjectClass::Set_Health(float health)
  1054. {
  1055. float old_health = Health;
  1056. Health = WWMath::Clamp(health, 0, HealthMax);
  1057. if ( old_health != (float)Health ) {
  1058. Mark_Owner_Dirty();
  1059. }
  1060. }
  1061. void DefenseObjectClass::Add_Health(float add_health)
  1062. {
  1063. Set_Health( WWMath::Clamp((float)Health + add_health, 0, HealthMax) );
  1064. }
  1065. float DefenseObjectClass::Get_Health(void) const
  1066. {
  1067. return Health;
  1068. }
  1069. void DefenseObjectClass::Set_Health_Max(float health)
  1070. {
  1071. HealthMax = WWMath::Clamp(health, 0, MAX_MAX_HEALTH);
  1072. Mark_Owner_Dirty();
  1073. }
  1074. float DefenseObjectClass::Get_Health_Max(void) const
  1075. {
  1076. return HealthMax;
  1077. }
  1078. void DefenseObjectClass::Set_Shield_Strength(float str)
  1079. {
  1080. float old = ShieldStrength;
  1081. ShieldStrength = WWMath::Clamp(str, 0, ShieldStrengthMax);
  1082. if ( old != (float)ShieldStrength ) {
  1083. Mark_Owner_Dirty();
  1084. }
  1085. }
  1086. void DefenseObjectClass::Add_Shield_Strength(float str)
  1087. {
  1088. Set_Shield_Strength( WWMath::Clamp((float)ShieldStrength + str, 0, ShieldStrengthMax) );
  1089. }
  1090. float DefenseObjectClass::Get_Shield_Strength(void) const
  1091. {
  1092. return ShieldStrength;
  1093. }
  1094. void DefenseObjectClass::Set_Shield_Strength_Max(float str)
  1095. {
  1096. ShieldStrengthMax = WWMath::Clamp(str, 0, MAX_MAX_SHIELD_STRENGTH);
  1097. Mark_Owner_Dirty();
  1098. }
  1099. float DefenseObjectClass::Get_Shield_Strength_Max(void) const
  1100. {
  1101. return ShieldStrengthMax;
  1102. }
  1103. void DefenseObjectClass::Set_Precision(void)
  1104. {
  1105. //
  1106. // This static function needs to be called after ArmorWarheadManager::Init
  1107. // has done its work.
  1108. //
  1109. cEncoderList::Set_Precision(BITPACK_HEALTH, 0, (int) MAX_MAX_HEALTH);
  1110. cEncoderList::Set_Precision(BITPACK_SHIELD_STRENGTH, 0, (int) MAX_MAX_SHIELD_STRENGTH);
  1111. cEncoderList::Set_Precision(BITPACK_SHIELD_TYPE, 0,
  1112. //ArmorWarheadManager::Get_Num_Armor_Types(), 1);
  1113. ArmorWarheadManager::Get_Num_Armor_Types());
  1114. }
  1115. /*
  1116. **
  1117. */
  1118. void DefenseObjectClass::Mark_Owner_Dirty( void )
  1119. {
  1120. if ( Get_Owner() != NULL ) {
  1121. Get_Owner()->Set_Object_Dirty_Bit( NetworkObjectClass::BIT_OCCASIONAL, true );
  1122. }
  1123. }
  1124. /*
  1125. **
  1126. */
  1127. void DefenseObjectClass::Set_Shield_Type( ArmorType type )
  1128. {
  1129. ShieldType = type;
  1130. Mark_Owner_Dirty();
  1131. }
  1132. //
  1133. // Do extra stuff when somebody kills somebody
  1134. //
  1135. /*
  1136. if ( Get_Owner() != NULL &&
  1137. Get_Owner()->As_SmartGameObj() &&
  1138. Get_Owner()->As_SmartGameObj()->Has_Player() ) {
  1139. int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
  1140. smart->Get_Player_Data()->On_Kill(victim_id);
  1141. }
  1142. */
  1143. /*
  1144. if ( Get_Owner() != NULL &&
  1145. Get_Owner()->As_DamageableGameObj() != NULL &&
  1146. Get_Owner()->As_DamageableGameObj()->As_PhysicalGameObj() != NULL &&
  1147. Get_Owner()->As_DamageableGameObj()->As_PhysicalGameObj()->As_SoldierGameObj() != NULL &&
  1148. Get_Owner()->As_SmartGameObj() != smart) {
  1149. int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
  1150. int victim_team = Get_Owner()->As_SmartGameObj()->Get_Player_Type();
  1151. smart->Get_Player_Data()->On_Kill(victim_id, victim_team);
  1152. }
  1153. */
  1154. /*
  1155. if ( Get_Owner() != NULL &&
  1156. Get_Owner()->As_SoldierGameObj != NULL &&
  1157. Get_Owner()->As_SmartGameObj() != smart) {
  1158. WWASSERT(Get_Owner()->As_SmartGameObj() != NULL);
  1159. int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
  1160. int victim_team = Get_Owner()->As_SmartGameObj()->Get_Player_Type();
  1161. smart->Get_Player_Data()->On_Kill(victim_id, victim_team);
  1162. }
  1163. */