btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * Author: Cord Awtry <kipsta@mediaone.net>
 *  Copyright (c) 2000-2002 Cord Awtry
 *       All rights reserved
 *
 * Based on work that was:
 *  Copyright (c) 1997 Markus Stenberg
 *  Copyright (c) 1998-2000 Thomas Wouters
 */

#include <stdio.h>
#include <stdlib.h>

#include "mech.h"
#include "btmacros.h"
#include "mech.combat.h"
#include "mech.events.h"
#include "p.pcombat.h"
#include "p.mech.combat.h"
#include "p.mech.combat.misc.h"
#include "p.mech.combat.missile.h"
#include "p.mech.damage.h"
#include "p.mech.ecm.h"
#include "p.mech.hitloc.h"
#include "p.mech.los.h"
#include "p.mech.utils.h"

int pilot_override;

void Missile_Hit(MECH * mech,
				 MECH * target,
				 int hitX,
				 int hitY,
				 int isrear,
				 int iscritical,
				 int weapindx,
				 int fireMode,
				 int ammoMode,
				 int num_missiles_hit, int damage, int salvo_size, int LOS,
				 int bth, int tIsSwarmAttack)
{
	int orig_num_missiles = num_missiles_hit;
	int this_time;
	int this_damage;
	int total_damage = 0;
	int clear_damage = 0;
	int hitloc;
	int tCheckWoodsDamageDecrement = 0;
	MAP *mech_map = getMap(mech->mapindex);
	char buf[SBUF_SIZE];

	total_damage = num_missiles_hit * damage;

	if(target && mudconf.btech_moddamagewithwoods &&
	   IsForestHex(mech_map, MechX(target), MechY(target)) &&
	   (fireMode > -1) && (ammoMode > -1) &&
	   ((MechZ(target) - 2) <= Elevation(mech_map, MechX(target),
										 MechY(target)))) {
		tCheckWoodsDamageDecrement = 1;
		clear_damage = total_damage;

		if(GetRTerrain(mech_map, MechX(target),
					   MechY(target)) == LIGHT_FOREST)
			total_damage -= 2;
		else if(GetRTerrain(mech_map, MechX(target),
							MechY(target)) == HEAVY_FOREST)
			total_damage -= 4;

		if(total_damage <= 0)
			num_missiles_hit = 0;
		else
			num_missiles_hit = total_damage / damage;

		possibly_ignite_or_clear(mech, weapindx, ammoMode, clear_damage,
								 MechX(target), MechY(target), 1);

		strcpy(buf, "");

		if(IsMissile(weapindx))
			sprintf(buf, "%s%s", "missile", orig_num_missiles > 1 ? "s" : "");
		else if(ammoMode & LBX_MODE)
			sprintf(buf, "%s%s", "pellet", orig_num_missiles > 1 ? "s" : "");
		else if((fireMode && ULTRA_MODE) || (fireMode && RFAC_MODE) ||
				(fireMode && RAC_MODES))
			sprintf(buf, "%s%s", "slug", orig_num_missiles > 1 ? "s" : "");
		else
			sprintf(buf, "%s", "damage");

		mech_printf(mech, MECHALL,
					"%s %s %s absorbed by the trees!",
					(orig_num_missiles == 1 ? "The" : num_missiles_hit ==
					 0 ? "All of the" : "Some of the"), buf,
					(orig_num_missiles == 1 ? "is" : "are"));
		mech_printf(target, MECHALL, "The trees absorb %s %s",
					((orig_num_missiles == 1) ||
					 (num_missiles_hit == 0) ? "the" : "some of the"), buf);
	}

	while (num_missiles_hit) {
		this_time = MIN(salvo_size, num_missiles_hit);
		this_damage = this_time * damage;

		if(target) {
			hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical);

			DamageMech(target, mech, LOS, GunPilot(mech), hitloc, isrear,
					   iscritical, pc_to_dam_conversion(target, weapindx,
														this_damage), 0,
					   weapindx, bth, -1, 0, tIsSwarmAttack);
		} else {
			hex_hit(mech, hitX, hitY, weapindx, ammoMode, this_damage, 1);
		}

		num_missiles_hit -= this_time;
	}
}

int MissileHitIndex(MECH * mech,
					MECH * hitMech, int weapindx, int wSection, int wCritSlot, int glance)
{
	int hit_roll;
	int r1, r2, r3, rtmp;
	int tHotloading =
		HotLoading(weapindx, GetPartFireMode(mech, wSection, wCritSlot));
	int wRollInc = 0;
	int wFinalRoll = 0;
	int tUseArtemisBonus =
		GetPartAmmoMode(mech, wSection, wCritSlot) & ARTEMIS_MODE;
	int tUseNARCBonus = 0;

	if(hitMech) {
		if(ECMProtected(hitMech) || AngelECMProtected(hitMech)) {
			tUseArtemisBonus = 0;
			tUseNARCBonus = 0;
		} else {
			tUseNARCBonus =
				(GetPartAmmoMode(mech, wSection, wCritSlot) & NARC_MODE) &&
				(checkAllSections(hitMech, NARC_ATTACHED) ||
				 checkAllSections(hitMech, INARC_HOMING_ATTACHED));
		}
	}

	if(AnyECMDisturbed(mech)) {
		tUseArtemisBonus = 0;
		tUseNARCBonus = 0;
	}

	/*
	 * Figure out the modifiers to the roll table for missiles
	 */
	if(IsMissile(weapindx) && (tUseArtemisBonus || tUseNARCBonus))
		wRollInc = 2;

	/* Roll 3 times... if we're hotloading, we'll use the 2 lowest */
	r1 = Number(1, 6);
	r2 = Number(1, 6);
	r3 = Number(1, 6);

	if(r1 > r2)
		Swap(r1, r2);
	if(r2 > r3)
		Swap(r2, r3);

	if(tHotloading)
		hit_roll = r1 + r2 - 2;
	else
		hit_roll = Roll() - 2;

	if((!hitMech || (hitMech && !AngelECMProtected(hitMech))) &&
	   !AngelECMDisturbed(mech) && (MechWeapons[weapindx].special & STREAK)) {
		return 10;
	}

	/* Glancing, per max tech, is -4 off the missile hit table */
	if(glance)
		wRollInc += -4;
	if(wRollInc)
		hit_roll = hit_roll + wRollInc;
	/* Glancing, per max tech, if its lower than 2 on the hit table, we hit with one missile. 
	 * return -1 so we can test for this elsewhere
	 */
	if(glance && (hit_roll < 0))
		return -1;
	
	wFinalRoll = MAX(MIN(hit_roll, 10), 0);

	return wFinalRoll;
}

int MissileHitTarget(MECH * mech,
					 int weapindx,
					 int wSection,
					 int wCritSlot,
					 MECH * hitMech, int hitX, int hitY, int LOS,
					 int baseToHit, int roll, int incoming,
					 int tIsSwarmAttack, int player_roll)
{
	int isrear = 0, iscritical = 0;
	int AMStype, ammoLoc, ammoCrit;
	int AMSShotdown = 0;
	int hit;
	int i, j = -1, k, l = 0;
	int wNARCType = 0;
	int ammoMode = GetPartAmmoMode(mech, wSection, wCritSlot);
	int tIsInferno = (ammoMode & INFERNO_MODE);
	int wNARCHitLoc = 0;
	int tIsRear = 0;
	char strLocName[30];
	int missileindex = 0;

	/* Check to see if we're a NARC or iNARC launcher firing homing missiles */
	if(IsMissile(weapindx)) {
		if((MechWeapons[weapindx].special & NARC) &&
		   !(GetPartAmmoMode(mech, wSection, wCritSlot) & NARC_MODE))
			wNARCType = 1;
		else if((MechWeapons[weapindx].special & INARC) &&
				!(GetPartAmmoMode(mech, wSection,
								  wCritSlot) & INARC_EXPLO_MODE)) {

			if(GetPartAmmoMode(mech, wSection,
							   wCritSlot) & INARC_HAYWIRE_MODE)
				wNARCType = 3;
			else if(GetPartAmmoMode(mech, wSection,
									wCritSlot) & INARC_ECM_MODE)
				wNARCType = 4;
			else
				wNARCType = 2;
		}

		/* Prefill our AMS data */
		if(hitMech && (!((ammoMode & SWARM_MODE) ||
						 (ammoMode & SWARM1_MODE)
						 || (ammoMode & MINE_MODE)))) {
			if(LocateAMSDefenses(hitMech, &AMStype, &ammoLoc, &ammoCrit))
				AMSShotdown =
					AMSMissiles(mech, hitMech, wNARCType ? 1 : incoming,
								AMStype, ammoLoc, ammoCrit, LOS,
								roll >= baseToHit);
		}

		if(wNARCType) {
			if(roll >= baseToHit) {
				if(hitMech) {
					if(AMSShotdown > 0) {
						if(LOS)
							mech_notify(mech, MECHALL,
										"The pod is shot down by the target!");

						mech_notify(hitMech, MECHALL,
									"Your Anti-Missile System activates and shoots down the incoming pod!");

						return 0;
					}

					wNARCHitLoc = findNARCHitLoc(mech, hitMech, &tIsRear);

					/* sanity check */
					if(wNARCHitLoc < 0) {
						mech_notify(mech, MECHALL,
									"Your NARC Beacon attaches to the target!");

						return 0;
					}

					ArmorStringFromIndex(wNARCHitLoc, strLocName,
										 MechType(hitMech),
										 MechMove(hitMech));

					if(wNARCType == 1)
						MechSections(hitMech)[wNARCHitLoc].specials |=
							NARC_ATTACHED;
					else if(wNARCType == 2)
						MechSections(hitMech)[wNARCHitLoc].specials |=
							INARC_HOMING_ATTACHED;
					else if(wNARCType == 3) {
						MechSections(hitMech)[wNARCHitLoc].specials |=
							INARC_HAYWIRE_ATTACHED;

						mech_notify(hitMech, MECHALL,
									"Your targetting systems goes a bit haywire!");
					} else if(wNARCType == 4) {
						MechSections(hitMech)[wNARCHitLoc].specials |=
							INARC_ECM_ATTACHED;

						checkECM(hitMech);
					}

					mech_printf(hitMech, MECHALL,
								"A NARC Beacon has been attached to your %s%s!",
								strLocName, tIsRear == 1 ? " (Rear)" : "");
					mech_printf(mech, MECHALL,
								"Your NARC Beacon attaches to the target's %s%s!",
								strLocName, tIsRear == 1 ? " (Rear)" : "");
				}
			} else
				mech_notify(mech, MECHALL,
							"Your NARC Beacon flies off into the distance.");

			return 0;
		}
	}

	if(roll < baseToHit)
		return incoming;

	for(i = 0; MissileHitTable[i].key >= 0; i++)
		if((k = MissileHitTable[i].num_missiles[10]) <= incoming &&
		   ((MechWeapons[MissileHitTable[i].key].special & STREAK) ==
			(MechWeapons[weapindx].special & STREAK)))
			if(k >= l && (j < 0 || MissileHitTable[i].key != weapindx ||
						  k > l)) {
				j = i;
				l = k;
			}

	if(j < 0)
		return 0;

	missileindex = MissileHitIndex(mech,hitMech,weapindx,wSection,wCritSlot,(mudconf.btech_glancing_blows) && (player_roll == baseToHit) ? 1 : 0);
	if (missileindex < 0 )
		hit = MIN(incoming, 1);
	else
		hit = MIN(incoming, MissileHitTable[j].num_missiles[missileindex]);


	if(LOS) {
		mech_printf(mech, MECHALL, "%%cg%s with %d missile%s!%%c",
					LOS == 1 ? "You hit" : "The swarm hits", hit,
					hit > 1 ? "s" : "");
	}

	if(AMSShotdown > 0) {
		if(AMSShotdown >= hit) {
			if(LOS)
				mech_notify(mech, MECHALL,
							"All of your missiles are shot down by the target!");

			mech_notify(hitMech, MECHALL,
						"Your Anti-Missile System activates and shoots all the incoming missiles!");
		} else {
			mech_printf(mech, MECHALL,
						"The target shoots down %d of your missiles!",
						AMSShotdown);

			mech_printf(hitMech, MECHALL,
						"Your Anti-Missile System activates and shoots down %d incoming missiles!",
						AMSShotdown);
		}
	}

	hit = MAX(0, hit - AMSShotdown);

	if(hit <= 0)
		return 0;

	if(tIsInferno) {
		if(hitMech)
			Inferno_Hit(mech, hitMech, hit, LOS);
		else
			hex_hit(mech, hitX, hitY, weapindx, GetPartAmmoMode(mech,
																wSection,
																wCritSlot), 0,
					0);
	} else {
		if(mudconf.btech_glancing_blows &&
		   (player_roll == baseToHit) && hitMech ) {
			if(!(MechWeapons[weapindx].special & STREAK)) {
				MechLOSBroadcast(hitMech, "is nicked by a glancing blow!");
				mech_notify(hitMech, MECHALL,
						"You are nicked by a glancing blow!");
			}
		}
			Missile_Hit(mech, hitMech, hitX, hitY, isrear, iscritical,
						weapindx, GetPartFireMode(mech, wSection, wCritSlot),
						GetPartAmmoMode(mech, wSection, wCritSlot), hit,
						MechWeapons[weapindx].damage, Clustersize(weapindx),
						LOS, baseToHit, tIsSwarmAttack);
	}

	return incoming - hit;
}

void SwarmHitTarget(MECH * mech,
					int weapindx,
					int wSection,
					int wCritSlot,
					MECH * hitMech,
					int LOS, int baseToHit, int roll, int incoming, int fof,
					int tIsSwarmAttack, int player_roll)
{
#define MAX_STAR 10
	/* Max # of targets we'll try to hit: 10 */
	MECH *star[MAX_STAR];
	int present_target = 0;
	int missiles;
	int loop;
	MAP *map = FindObjectsData(mech->mapindex);
	float r = 0.0, ran = 0, flrange = 0.0;
	MECH *source = mech, *tempMech;
	int i, j;

	for(loop = 0; MissileHitTable[loop].key != -1; loop++)
		if(MissileHitTable[loop].key == weapindx)
			break;

	if(!(MissileHitTable[loop].key == weapindx))
		return;

	missiles = MissileHitTable[loop].num_missiles[10];
	while (missiles > 0) {
		flrange = flrange + FaMechRange(source, hitMech);
		ran = FaMechRange(mech, hitMech);
		if(flrange > EGunRange(weapindx)) {
			mech_notify(hitMech, MECHALL,
						"Luckily, the missiles fall short of you!");
			return;
		}
		if(!(missiles =
			 MissileHitTarget(mech, weapindx, wSection, wCritSlot,
							  hitMech, -1, -1, InLineOfSight_NB(mech, hitMech,
																MechX(mech),
																MechY(mech),
																ran) ?
							  present_target == 0 ? 1 : 2 : 0, baseToHit,
							  present_target == 0 ? roll : Roll(), missiles,
							  tIsSwarmAttack, player_roll)))
			return;
		/* Try to acquire a new target NOT in the star */
		if(present_target == MAX_STAR)
			return;
		star[present_target++] = hitMech;
		source = hitMech;
		hitMech = NULL;
		for(i = 0; i < map->first_free; i++)
			if((tempMech = FindObjectsData(map->mechsOnMap[i])))
				if(!fof || (MechTeam(tempMech) != MechTeam(mech))) {
					for(j = 0; j < present_target; j++)
						if(tempMech == star[j])
							break;
					if(MechStatus(tempMech) & COMBAT_SAFE)
						continue;
					if(j != present_target)
						continue;
					if(!hitMech && (r = FaMechRange(source, tempMech)) < 1.9)
						if(InLineOfSight_NB(source, tempMech,
											MechX(source), MechY(source),
											r)) {
							hitMech = tempMech;
							ran = r;
						}
				}
		if(!hitMech)
			return;
		if(mech != hitMech)
			mech_notify(hitMech, MECHALL,
						"The missile-swarm turns towards you!");
		if(InLineOfSight_NB(mech, source, MechX(mech), MechY(mech),
							FaMechRange(mech, source)))
			mech_printf(mech, MECHALL,
						"Your missile-swarm of %d missile%s targets %s!",
						missiles, missiles > 1 ? "s" : "",
						mech == hitMech ? "YOU!!" : GetMechToMechID(mech,
																	hitMech));
		MechLOSBroadcasti(mech, hitMech, "'s missile-swarm targets %s!");
	}
}

/*
 * Fix AMS:
 *
 * - Applied after number missiles is determined
 * - Ammo used == missiles shot down
 * - d6 for IS, 2d6 for clan
 * - Not used against Arrow IV, Thunder, Flare, Swarm or Swarm-1
 */

/****************************************
 * START: AMS related functions 
 ****************************************/
int AMSMissiles(MECH * mech,
				MECH * hitMech,
				int incoming, int type,
				int ammoLoc, int ammoCrit, int LOS, int missilesDidHit)
{
	int num_missiles_shotdown;

	if(MechWeapons[type].special & CLAT)
		num_missiles_shotdown = Roll();
	else
		num_missiles_shotdown = Number(1, 6);

	if(num_missiles_shotdown > incoming)
		num_missiles_shotdown = incoming;

	if(num_missiles_shotdown >= GetPartData(hitMech, ammoLoc, ammoCrit))
		GetPartData(hitMech, ammoLoc, ammoCrit) = 0;
	else
		GetPartData(hitMech, ammoLoc, ammoCrit) -= num_missiles_shotdown;

	if(!missilesDidHit) {
		mech_notify(hitMech, MECHALL,
					"Your Anti-Missile System activates and shoots at the incoming missiles!");
		return 0;
	}

	return num_missiles_shotdown;
}

int LocateAMSDefenses(MECH * target,
					  int *AMStype, int *ammoLoc, int *ammoCrit)
{
	int AMSsect, AMScrit;
	int i, j = 0, w, t = 0;

	if(!(MechSpecials(target) & (IS_ANTI_MISSILE_TECH |
								 CL_ANTI_MISSILE_TECH)) ||
	   !Started(target) || !(MechStatus(target) & AMS_ENABLED))
		return 0;

	for(i = 0; i < NUM_SECTIONS; i++) {
		for(j = 0; j < NUM_CRITICALS; j++)
			if(IsWeapon((t = GetPartType(target, i, j))))
				if(IsAMS(Weapon2I(t)))
					if(!(PartIsNonfunctional(target, i, j) ||
						 WpnIsRecycling(target, i, j)))
						break;
		if(j < NUM_CRITICALS)
			break;
	}

	if(i == NUM_SECTIONS)
		return 0;

	w = Weapon2I(t);
	AMSsect = i;
	AMScrit = j;
	*AMStype = w;

	if(!(FindAmmoForWeapon(target, w, AMSsect, ammoLoc, ammoCrit)))
		return 0;

	if(!(GetPartData(target, *ammoLoc, *ammoCrit)))
		return 0;

	SetRecyclePart(target, AMSsect, AMScrit, MechWeapons[w].vrt);
	MechWeapHeat(target) += (float) MechWeapons[w].heat;
	return 1;
}

/****************************************
 * END: AMS related functions 
 ****************************************/