CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

COMBAT.CPP (21703B)


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