COMBAT.CPP 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/COMBAT.CPP 1 3/03/97 10:24a Joe_bostic $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : COMBAT.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : September 19, 1994 *
  26. * *
  27. * Last Update : July 26, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * Combat_Anim -- Determines explosion animation to play. *
  32. * Explosion_Damage -- Inflict an explosion damage affect. *
  33. * Modify_Damage -- Adjusts damage to reflect the nature of the target. *
  34. * Wide_Area_Damage -- Apply wide area damage to the map. *
  35. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  36. #include "function.h"
  37. /***********************************************************************************************
  38. * Modify_Damage -- Adjusts damage to reflect the nature of the target. *
  39. * *
  40. * This routine is the core of combat tactics. It implements the *
  41. * affect various armor types have against various weapon types. By *
  42. * careful exploitation of this table, tactical advantage can be *
  43. * obtained. *
  44. * *
  45. * INPUT: damage -- The damage points to process. *
  46. * *
  47. * warhead -- The source of the damage points. *
  48. * *
  49. * armor -- The type of armor defending against the damage. *
  50. * *
  51. * distance -- The distance (in leptons) from the source of the damage. *
  52. * *
  53. * OUTPUT: Returns with the adjusted damage points to inflict upon the *
  54. * target. *
  55. * *
  56. * WARNINGS: none *
  57. * *
  58. * HISTORY: *
  59. * 04/16/1994 JLB : Created. *
  60. * 04/17/1994 JLB : Always does a minimum of damage. *
  61. * 01/01/1995 JLB : Takes into account distance from damage source. *
  62. * 04/11/1996 JLB : Changed damage fall-off formula for less damage fall-off. *
  63. *=============================================================================================*/
  64. int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance)
  65. {
  66. if (!damage) return(damage);
  67. /*
  68. ** If there is no raw damage value to start with, then
  69. ** there can be no modified damage either.
  70. */
  71. if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0);
  72. /*
  73. ** Negative damage (i.e., heal) is always applied full strength, but only if the heal
  74. ** effect is close enough.
  75. */
  76. if (damage < 0) {
  77. #ifdef FIXIT_CSII // checked - ajw 9/28/98
  78. if (distance < 0x008) {
  79. if(warhead != WARHEAD_MECHANICAL && armor == ARMOR_NONE) return(damage);
  80. if(warhead == WARHEAD_MECHANICAL && armor != ARMOR_NONE) return(damage);
  81. }
  82. #else
  83. if (distance < 0x008 && armor == ARMOR_NONE) return(damage);
  84. #endif
  85. return(0);
  86. }
  87. WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead);
  88. // WarheadTypeClass const * whead = &Warheads[warhead];
  89. damage = damage * whead->Modifier[armor];
  90. /*
  91. ** Reduce damage according to the distance from the impact point.
  92. */
  93. if (damage) {
  94. if (!whead->SpreadFactor) {
  95. distance /= PIXEL_LEPTON_W/4;
  96. } else {
  97. distance /= whead->SpreadFactor * (PIXEL_LEPTON_W/2);
  98. }
  99. distance = Bound(distance, 0, 16);
  100. if (distance) {
  101. damage = damage / distance;
  102. }
  103. /*
  104. ** Allow damage to drop to zero only if the distance would have
  105. ** reduced damage to less than 1/4 full damage. Otherwise, ensure
  106. ** that at least one damage point is done.
  107. */
  108. if (distance < 4) {
  109. damage = max(damage, Rule.MinDamage);
  110. }
  111. }
  112. damage = min(damage, Rule.MaxDamage);
  113. return(damage);
  114. }
  115. /***********************************************************************************************
  116. * Explosion_Damage -- Inflict an explosion damage affect. *
  117. * *
  118. * Processes the collateral damage affects typically caused by an *
  119. * explosion. *
  120. * *
  121. * INPUT: coord -- The coordinate of ground zero. *
  122. * *
  123. * strength -- Raw damage points at ground zero. *
  124. * *
  125. * source -- Source of the explosion (who is responsible). *
  126. * *
  127. * warhead -- The kind of explosion to process. *
  128. * *
  129. * OUTPUT: none *
  130. * *
  131. * WARNINGS: This routine can consume some time and will affect the AI *
  132. * of nearby enemy units (possibly). *
  133. * *
  134. * HISTORY: *
  135. * 08/16/1991 JLB : Created. *
  136. * 11/30/1991 JLB : Uses coordinate system. *
  137. * 12/27/1991 JLB : Radius of explosion damage effect. *
  138. * 04/13/1994 JLB : Streamlined. *
  139. * 04/16/1994 JLB : Warhead damage type modifier. *
  140. * 04/17/1994 JLB : Cleaned up. *
  141. * 06/20/1994 JLB : Uses object pointers to distribute damage. *
  142. * 06/20/1994 JLB : Source is a pointer. *
  143. * 06/18/1996 JLB : Strength could be negative for healing effects. *
  144. *=============================================================================================*/
  145. void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead)
  146. {
  147. CELL cell; // Cell number under explosion.
  148. ObjectClass * object; // Working object pointer.
  149. ObjectClass * objects[32]; // Maximum number of objects that can be damaged.
  150. int distance; // Distance to unit.
  151. int range; // Damage effect radius.
  152. int count; // Number of vehicle IDs in list.
  153. if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return;
  154. WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead);
  155. // WarheadTypeClass const * whead = &Warheads[warhead];
  156. // range = ICON_LEPTON_W*2;
  157. range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1);
  158. cell = Coord_Cell(coord);
  159. if ((unsigned)cell >= MAP_CELL_TOTAL) return;
  160. CellClass * cellptr = &Map[cell];
  161. ObjectClass * impacto = cellptr->Cell_Occupier();
  162. /*
  163. ** Fill the list of unit IDs that will have damage
  164. ** assessed upon them. The units can be lifted from
  165. ** the cell data directly.
  166. */
  167. count = 0;
  168. for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) {
  169. /*
  170. ** Fetch a pointer to the cell to examine. This is either
  171. ** an adjacent cell or the center cell. Damage never spills
  172. ** further than one cell away.
  173. */
  174. if (i != FACING_NONE) {
  175. cellptr = Map[cell].Adjacent_Cell(i);
  176. if (!cellptr) continue;
  177. }
  178. /*
  179. ** Add all objects in this cell to the list of objects to possibly apply
  180. ** damage to. The list stops building when the object pointer list becomes
  181. ** full. Do not include overlapping objects; selection state can affect
  182. ** the overlappers, and this causes multiplayer games to go out of sync.
  183. */
  184. object = cellptr->Cell_Occupier();
  185. while (object) {
  186. if (!object->IsToDamage && object != source) {
  187. object->IsToDamage = true;
  188. objects[count++] = object;
  189. if (count >= ARRAY_SIZE(objects)) break;
  190. }
  191. object = object->Next;
  192. }
  193. if (count >= ARRAY_SIZE(objects)) break;
  194. }
  195. /*
  196. ** Sweep through the units to be damaged and damage them. When damaging
  197. ** buildings, consider a hit on any cell the building occupies as if it
  198. ** were a direct hit on the building's center.
  199. */
  200. for (int index = 0; index < count; index++) {
  201. object = objects[index];
  202. object->IsToDamage = false;
  203. if (object->IsActive) {
  204. if (object->What_Am_I() == RTTI_BUILDING && impacto == object) {
  205. distance = 0;
  206. } else {
  207. distance = Distance(coord, object->Center_Coord());
  208. }
  209. if (object->IsDown && !object->IsInLimbo && distance < range) {
  210. int damage = strength;
  211. object->Take_Damage(damage, distance, warhead, source);
  212. }
  213. }
  214. }
  215. /*
  216. ** If there is a wall present at this location, it may be destroyed. Check to
  217. ** make sure that the warhead is of the kind that can destroy walls.
  218. */
  219. cellptr = &Map[cell];
  220. if (cellptr->Overlay != OVERLAY_NONE) {
  221. OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);
  222. if (optr->IsTiberium && whead->IsTiberiumDestroyer) {
  223. cellptr->Reduce_Tiberium(strength / 10);
  224. }
  225. if (optr->IsWall) {
  226. if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) {
  227. Map[cell].Reduce_Wall(strength);
  228. }
  229. }
  230. }
  231. /*
  232. ** If there is a bridge at this location, then it may be destroyed by the
  233. ** combat damage.
  234. */
  235. if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 ||
  236. cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H ||
  237. cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B ||
  238. cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B ||
  239. cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B ) {
  240. if (((warhead == WARHEAD_AP || warhead == WARHEAD_HE) && Random_Pick(1, Rule.BridgeStrength) < strength)) {
  241. Map.Destroy_Bridge_At(cell);
  242. }
  243. }
  244. }
  245. /***********************************************************************************************
  246. * Combat_Anim -- Determines explosion animation to play. *
  247. * *
  248. * This routine is called when a projectile impacts. This routine will determine what *
  249. * animation should be played. *
  250. * *
  251. * INPUT: damage -- The amount of damage this warhead possess (warhead size). *
  252. * *
  253. * warhead -- The type of warhead. *
  254. * *
  255. * land -- The land type that this explosion is over. Sometimes, this makes *
  256. * a difference (especially over water). *
  257. * *
  258. * OUTPUT: Returns with the animation to play. If no animation is to be played, then *
  259. * ANIM_NONE is returned. *
  260. * *
  261. * WARNINGS: none *
  262. * *
  263. * HISTORY: *
  264. * 05/19/1996 JLB : Created. *
  265. *=============================================================================================*/
  266. AnimType Combat_Anim(int damage, WarheadType warhead, LandType land)
  267. {
  268. /*
  269. ** For cases of no damage or invalid warhead, don't have any
  270. ** animation effect at all.
  271. */
  272. if (damage == 0 || warhead == WARHEAD_NONE) {
  273. return(ANIM_NONE);
  274. }
  275. static AnimType _aplist[] = {
  276. ANIM_VEH_HIT3, // Small fragment throwing explosion -- burn/exp mix.
  277. ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles.
  278. ANIM_FRAG1, // Medium fragment throwing explosion -- short decay.
  279. ANIM_FBALL1, // Large fireball explosion (bulges rightward).
  280. };
  281. static AnimType _helist[] = {
  282. ANIM_VEH_HIT1, // Small fireball explosion (bulges rightward).
  283. ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles.
  284. ANIM_ART_EXP1, // Large fragment throwing explosion -- many sparkles.
  285. ANIM_FBALL1, // Large fireball explosion (bulges rightward).
  286. };
  287. static AnimType _firelist[] = {
  288. ANIM_NAPALM1, // Small napalm burn.
  289. ANIM_NAPALM2, // Medium napalm burn.
  290. ANIM_NAPALM3, // Large napalm burn.
  291. };
  292. static AnimType _waterlist[] = {
  293. ANIM_WATER_EXP3,
  294. ANIM_WATER_EXP2,
  295. ANIM_WATER_EXP1,
  296. };
  297. WarheadTypeClass const * wptr = WarheadTypeClass::As_Pointer(warhead);
  298. // WarheadTypeClass const * wptr = &Warheads[warhead];
  299. switch (wptr->ExplosionSet) {
  300. case 6:
  301. return(ANIM_ATOM_BLAST);
  302. case 2:
  303. if (damage > 15) {
  304. return(ANIM_PIFFPIFF);
  305. }
  306. return(ANIM_PIFF);
  307. case 4:
  308. if (land == LAND_NONE) return(ANIM_FLAK);
  309. // Fixed math error
  310. if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 90), 90)]);
  311. return(_aplist[(ARRAY_SIZE(_aplist)-1) * fixed(min(damage, 90), 90)]);
  312. case 5:
  313. if (land == LAND_NONE) return(ANIM_FLAK);
  314. if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 130), 130)]);
  315. return(_helist[(ARRAY_SIZE(_helist)-1) * fixed(min(damage, 130), 130)]);
  316. case 3:
  317. if (land == LAND_NONE) return(ANIM_FLAK);
  318. if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 150), 150)]);
  319. return(_firelist[(ARRAY_SIZE(_firelist)-1) * fixed(min(damage, 150), 150)]);
  320. case 1:
  321. return(ANIM_PIFF);
  322. default:
  323. break;
  324. }
  325. return(ANIM_NONE);
  326. }
  327. /***********************************************************************************************
  328. * Wide_Area_Damage -- Apply wide area damage to the map. *
  329. * *
  330. * This routine will apply damage to a very wide area on the map. The damage will be *
  331. * spread out from the coordinate specified by the radius specified. The amount of damage *
  332. * will attenuate according to the distance from center. *
  333. * *
  334. * INPUT: coord -- The coordinate that the explosion damage will center about. *
  335. * *
  336. * radius -- The radius of the explosion. *
  337. * *
  338. * damage -- The amount of damage to apply at the center location. *
  339. * *
  340. * source -- Pointer to the purpetrator of the damage (if any). *
  341. * *
  342. * warhead -- The type of warhead that is causing the damage. *
  343. * *
  344. * OUTPUT: none *
  345. * *
  346. * WARNINGS: none *
  347. * *
  348. * HISTORY: *
  349. * 07/26/1996 JLB : Created. *
  350. *=============================================================================================*/
  351. void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int rawdamage, TechnoClass * source, WarheadType warhead)
  352. {
  353. int cell_radius = (radius + CELL_LEPTON_W-1) / CELL_LEPTON_W;
  354. CELL cell = Coord_Cell(coord);
  355. for (int x = -cell_radius; x <= cell_radius; x++) {
  356. for (int y = -cell_radius; y <= cell_radius; y++) {
  357. int xpos = Cell_X(cell) + x;
  358. int ypos = Cell_Y(cell) + y;
  359. /*
  360. ** If the potential damage cell is outside of the map bounds,
  361. ** then don't process it. This unusual check method ensures that
  362. ** damage won't wrap from one side of the map to the other.
  363. */
  364. if ((unsigned)xpos > MAP_CELL_W) {
  365. continue;
  366. }
  367. if ((unsigned)ypos > MAP_CELL_H) {
  368. continue;
  369. }
  370. CELL tcell = XY_Cell(xpos, ypos);
  371. if (!Map.In_Radar(tcell)) continue;
  372. int dist_from_center = Distance(XY_Coord(x+cell_radius, y+cell_radius), XY_Coord(cell_radius, cell_radius));
  373. int damage = rawdamage * Inverse(fixed(cell_radius, dist_from_center));
  374. Explosion_Damage(Cell_Coord(tcell), damage, source, warhead);
  375. if (warhead == WARHEAD_FIRE && damage > 100) {
  376. new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell));
  377. }
  378. }
  379. }
  380. }