* @param isCapital
* whether it was capital scale damage that caused critical
*/
public Vector<Report> applyCriticalHit(Entity en, int loc, CriticalSlot cs, boolean secondaryEffects, int damageCaused, boolean isCapital) {
Vector<Report> vDesc = new Vector<Report>();
Report r;
// Handle hits on "critical slots" of tanks.
if (en instanceof Tank) {
Tank t = (Tank) en;
HitData hit;
switch (cs.getIndex()) {
case Tank.CRIT_NONE:
// no effect
r = new Report(6005);
r.subject = t.getId();
vDesc.add(r);
break;
case Tank.CRIT_AMMO:
// ammo explosion
r = new Report(6610);
r.subject = t.getId();
vDesc.add(r);
int damage = 0;
for (Mounted m : t.getAmmo()) {
m.setHit(true);
int tmp = m.getShotsLeft() * ((AmmoType) m.getType()).getDamagePerShot() * ((AmmoType) m.getType()).getRackSize();
m.setShotsLeft(0);
// non-explosive ammo can't explode
if (!m.getType().isExplosive()) {
continue;
}
damage += tmp;
r = new Report(6390);
r.subject = t.getId();
r.add(m.getName());
r.add(tmp);
r.newlines = 0;
vDesc.add(r);
}
hit = new HitData(loc);
vDesc.addAll(damageEntity(t, hit, damage, true));
break;
case Tank.CRIT_CARGO:
// Cargo/infantry damage
r = new Report(6615);
r.subject = t.getId();
vDesc.add(r);
Vector<Entity> passengers = t.getLoadedUnits();
Entity target = passengers.get(Compute.randomInt(passengers.size()));
hit = target.rollHitLocation(ToHitData.HIT_NORMAL, ToHitData.SIDE_FRONT);
// FIXME should be original weapon damage
vDesc.addAll(damageEntity(target, hit, 5));
break;
case Tank.CRIT_COMMANDER:
if (en.crew.getOptions().booleanOption("vdni") || en.crew.getOptions().booleanOption("bvdni")) {
r = new Report(6191);
r.subject = t.getId();
vDesc.add(r);
vDesc.addAll(damageCrew(en, 1));
} else {
if (en.crew.getOptions().booleanOption("pain_shunt") && !t.isCommanderHitPS()) {
r = new Report(6606);
r.subject = t.getId();
vDesc.add(r);
t.setCommanderHitPS(true);
} else {
r = new Report(6605);
r.subject = t.getId();
vDesc.add(r);
t.setCommanderHit(true);
}
}
// fall through here, because effects of crew stunned also apply
case Tank.CRIT_CREW_STUNNED:
if (en.crew.getOptions().booleanOption("vdni") || en.crew.getOptions().booleanOption("bvdni")) {
r = new Report(6191);
r.subject = t.getId();
vDesc.add(r);
vDesc.addAll(damageCrew(en, 1));
} else {
if (en.crew.getOptions().booleanOption("pain_shunt")
|| en.crew.getOptions().booleanOption("dermal_armor")) {
r = new Report(6186);
r.subject = t.getId();
vDesc.add(r);
} else {
t.stunCrew();
r = new Report(6185);
r.add(t.getStunnedTurns() - 1);
r.subject = t.getId();
vDesc.add(r);
}
}
break;
case Tank.CRIT_DRIVER:
if (en.crew.getOptions().booleanOption("vdni") || en.crew.getOptions().booleanOption("bvdni")) {
r = new Report(6191);
r.subject = t.getId();
vDesc.add(r);
vDesc.addAll(damageCrew(en, 1));
} else {
if (en.crew.getOptions().booleanOption("pain_shunt") && !t.isDriverHitPS()) {
r = new Report(6601);
r.subject = t.getId();
vDesc.add(r);
t.setDriverHitPS(true);
} else {
r = new Report(6600);
r.subject = t.getId();
vDesc.add(r);
t.setDriverHit(true);
}
}
break;
case Tank.CRIT_CREW_KILLED:
if (en.crew.getOptions().booleanOption("vdni") || en.crew.getOptions().booleanOption("bvdni")) {
r = new Report(6191);
r.subject = t.getId();
vDesc.add(r);
vDesc.addAll(damageCrew(en, 1));
} else {
if (en.crew.getOptions().booleanOption("pain_shunt") && !t.isCrewHitPS()) {
r = new Report(6191);
r.subject = t.getId();
vDesc.add(r);
t.setCrewHitPS(true);
} else {
r = new Report(6190);
r.subject = t.getId();
vDesc.add(r);
t.getCrew().setDoomed(true);
}
}
break;
case Tank.CRIT_ENGINE:
r = new Report(6210);
r.subject = t.getId();
vDesc.add(r);
t.engineHit();
t.engineHitsThisRound++;
boolean engineExploded = checkEngineExplosion(t, vDesc, 1);
if (engineExploded) {
vDesc.addAll(destroyEntity(en, "engine destruction", true, true));
}
if (t instanceof VTOL) {
PilotingRollData psr = t.getBasePilotingRoll();
IHex hex = game.getBoard().getHex(t.getPosition());
psr.addModifier(4, "forced landing");
int elevation = Math.max(hex.terrainLevel(Terrains.BLDG_ELEV), hex.terrainLevel(Terrains.BRIDGE_ELEV));
elevation = Math.max(elevation, 0);
elevation = Math.min(elevation, t.getElevation());
if (t.getElevation() > elevation) {
if (!hex.containsTerrain(Terrains.FUEL_TANK) && !hex.containsTerrain(Terrains.JUNGLE) && !hex.containsTerrain(Terrains.MAGMA) && !hex.containsTerrain(Terrains.MUD) && !hex.containsTerrain(Terrains.RUBBLE) && !hex.containsTerrain(Terrains.WATER) && !hex.containsTerrain(Terrains.WOODS)) {
r = new Report(2180);
r.subject = t.getId();
r.addDesc(t);
r.add(psr.getLastPlainDesc(), true);
vDesc.add(r);
// roll
final int diceRoll = Compute.d6(2);
r = new Report(2185);
r.subject = t.getId();
r.add(psr.getValueAsString());
r.add(psr.getDesc());
r.add(diceRoll);
if (diceRoll < psr.getValue()) {
r.choose(false);
vDesc.add(r);
vDesc.addAll(crashVTOLorWiGE(t));
} else {
r.choose(true);
vDesc.add(r);
t.setElevation(elevation);
}
} else {
vDesc.addAll(crashVTOLorWiGE(t));
}
}
}
break;
case Tank.CRIT_FUEL_TANK:
r = new Report(6215);
r.subject = t.getId();
vDesc.add(r);
vDesc.addAll(destroyEntity(t, "fuel explosion", false, false));
break;
case Tank.CRIT_SENSOR:
r = new Report(6620);
r.subject = t.getId();
vDesc.add(r);
t.setSensorHits(t.getSensorHits() + 1);
break;
case Tank.CRIT_STABILIZER:
r = new Report(6625);
r.subject = t.getId();
vDesc.add(r);
t.setStabiliserHit(loc);
break;
case Tank.CRIT_TURRET_DESTROYED:
r = new Report(6630);
r.subject = t.getId();
vDesc.add(r);
t.destroyLocation(Tank.LOC_TURRET);
vDesc.addAll(destroyEntity(t, "turret blown off", true, true));
break;
case Tank.CRIT_TURRET_JAM:
if (t.isTurretEverJammed()) {
r = new Report(6640);
r.subject = t.getId();
vDesc.add(r);
t.lockTurret();
break;
}
r = new Report(6635);
r.subject = t.getId();
vDesc.add(r);
t.jamTurret();
break;
case Tank.CRIT_TURRET_LOCK:
r = new Report(6640);
r.subject = t.getId();
vDesc.add(r);
t.lockTurret();
break;
case Tank.CRIT_WEAPON_DESTROYED: {
r = new Report(6305);
r.subject = t.getId();
ArrayList<Mounted> weapons = new ArrayList<Mounted>();
for (Mounted weap : t.getWeaponList()) {
if ((weap.getLocation() == loc) && !weap.isHit() && !weap.isDestroyed()) {
weapons.add(weap);
}
}
// sort weapons by BV
Collections.sort(weapons, new WeaponComparator());
int roll = Compute.d6();
Mounted weapon;
if (roll < 4) {
// defender should choose, we'll just use the lowest BV
// weapon
weapon = weapons.get(weapons.size() - 1);
} else {
// attacker chooses, we'll use the highest BV weapon
weapon = weapons.get(0);
}
weapon.setHit(true);
r.add(weapon.getName());
vDesc.add(r);
// explosive weapons e.g. gauss now explode
vDesc.addAll(explodeEquipment(t, loc, weapon));
weapon.setDestroyed(true);
break;
}
case Tank.CRIT_WEAPON_JAM: {
r = new Report(6645);
r.subject = t.getId();
ArrayList<Mounted> weapons = new ArrayList<Mounted>();
for (Mounted weap : t.getWeaponList()) {
if ((weap.getLocation() == loc) && !weap.isJammed() && !weap.isHit() && !weap.isDestroyed()) {
weapons.add(weap);
}
}
Mounted weapon = weapons.get(Compute.randomInt(weapons.size()));
weapon.setJammed(true);
t.addJammedWeapon(weapon);
r.add(weapon.getName());
vDesc.add(r);
break;
}
case VTOL.CRIT_PILOT:
r = new Report(6650);
r.subject = t.getId();
vDesc.add(r);
t.setDriverHit(true);
PilotingRollData psr = t.getBasePilotingRoll();
psr.addModifier(0, "pilot injury");
if (!doSkillCheckInPlace(t, psr)) {
r = new Report(6675);
r.subject = t.getId();
r.addDesc(t);
vDesc.add(r);
boolean crash = true;
if (t.canGoDown()) {
t.setElevation(t.getElevation() - 1);
crash = !t.canGoDown();
}
if (crash) {
vDesc.addAll(crashVTOLorWiGE(t));
}
}
break;
case VTOL.CRIT_COPILOT:
r = new Report(6655);
r.subject = t.getId();
vDesc.add(r);
t.setCommanderHit(true);
break;
case VTOL.CRIT_ROTOR_DAMAGE: {
r = new Report(6660);
r.subject = t.getId();
vDesc.add(r);
int mp = t.getOriginalWalkMP();
if (mp > 1) {
t.setOriginalWalkMP(mp - 1);
} else if (mp == 1) {
t.setOriginalWalkMP(0);
vDesc.addAll(crashVTOLorWiGE(t));
}
break;
}
case VTOL.CRIT_ROTOR_DESTROYED:
r = new Report(6670);
r.subject = t.getId();
vDesc.add(r);
t.immobilize();
t.destroyLocation(VTOL.LOC_ROTOR);
vDesc.addAll(crashVTOLorWiGE(t));
break;
case VTOL.CRIT_FLIGHT_STABILIZER:
r = new Report(6665);
r.subject = t.getId();
vDesc.add(r);
t.setStabiliserHit(VTOL.LOC_ROTOR);
break;
}
} else if (en instanceof Aero) {
Aero a = (Aero) en;
Jumpship js = new Jumpship();
if (en instanceof Jumpship) {
js = (Jumpship) en;
} else {
js = null;
}
switch (cs.getIndex()) {
case Aero.CRIT_NONE:
// no effect
r = new Report(6005);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
break;
case Aero.CRIT_FCS:
// Fire control system
r = new Report(9105);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setFCSHits(a.getFCSHits() + 1);
break;
case Aero.CRIT_SENSOR:
// sensors
r = new Report(6620);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setSensorHits(a.getSensorHits() + 1);
break;
case Aero.CRIT_AVIONICS:
// avionics
r = new Report(9110);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setAvionicsHits(a.getAvionicsHits() + 1);
if(a.isPartOfFighterSquadron()) {
game.addControlRoll(new PilotingRollData(a.getTransportId(), 1, "avionics hit"));
} else if(a.isCapitalFighter()) {
game.addControlRoll(new PilotingRollData(a.getId(), 1, "avionics hit"));
} else {
game.addControlRoll(new PilotingRollData(a.getId(), 0, "avionics hit"));
}
break;
case Aero.CRIT_CONTROL:
// force control roll
r = new Report(9115);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
if(a.isPartOfFighterSquadron()) {
game.addControlRoll(new PilotingRollData(a.getTransportId(), 1, "critical hit"));
} else if(a.isCapitalFighter()) {
game.addControlRoll(new PilotingRollData(a.getId(), 1, "critical hit"));
} else {
game.addControlRoll(new PilotingRollData(a.getId(), 0, "critical hit"));
}
break;
case Aero.CRIT_FUEL_TANK:
// fuel tank
r = new Report(9120);
r.subject = a.getId();
r.newlines = 0;
int boomTarget = 9;
if(a.isLargeCraft() && a.isClan() && game.getOptions().booleanOption("stratops_harjel")) {
boomTarget = 11;
}
// check for possible explosion
int fuelroll = Compute.d6(2);
if (fuelroll > boomTarget) {
r.choose(true);
vDesc.add(r);
vDesc.addAll(destroyEntity(a, "fuel explosion", false, false));
} else {
r.choose(false);
vDesc.add(r);
}
break;
case Aero.CRIT_CREW:
// pilot hit
r = new Report(6650);
if (en.crew.getOptions().booleanOption("dermal_armor")) {
r = new Report(6651);
r.subject = a.getId();
vDesc.add(r);
break;
}
if ((a instanceof SmallCraft) || (a instanceof Jumpship)) {
r = new Report(9197);
}
if(a.isLargeCraft() && a.isClan()
&& game.getOptions().booleanOption("stratops_harjel") && (a.getIgnoredCrewHits() < 2)) {
a.setIgnoredCrewHits(a.getIgnoredCrewHits() + 1);
r = new Report(9198);
r.subject = a.getId();
r.newlines = 1;
vDesc.add(r);
break;
}
r.subject = a.getId();
r.newlines = 1;
vDesc.add(r);
vDesc.addAll(damageCrew(a, 1));
// The pilot may have just expired.
if ((a.crew.isDead() || a.crew.isDoomed()) && !a.crew.isEjected()) {
vDesc.addAll(destroyEntity(a, "pilot death", true, true));
}
break;
case Aero.CRIT_GEAR:
// landing gear
r = new Report(9125);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setGearHit(true);
break;
case Aero.CRIT_BOMB:
// bomb destroyed
// go through bomb list and choose one
ArrayList<Mounted> bombs = new ArrayList<Mounted>();
for (Mounted bomb : a.getBombs()) {
if (!bomb.isDestroyed() && bomb.getType().isHittable() && (bomb.getShotsLeft() > 0)) {
bombs.add(bomb);
}
}
if (bombs.size() > 0) {
Mounted hitbomb = bombs.get(Compute.randomInt(bombs.size()));
hitbomb.setShotsLeft(0);
hitbomb.setDestroyed(true);
r = new Report(9130);
r.subject = a.getId();
r.newlines = 0;
r.add(hitbomb.getDesc());
vDesc.add(r);
} else {
r = new Report(9131);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
}
break;
case Aero.CRIT_HEATSINK:
// heat sink hit
int sinksLost = 1;
if (isCapital) {
sinksLost = 10;
}
r = new Report(9135);
r.subject = a.getId();
r.newlines = 0;
r.add(sinksLost);
vDesc.add(r);
a.setHeatSinks(Math.max(0, a.getHeatSinks() - sinksLost));
break;
case Aero.CRIT_WEAPON_BROAD:
if(a instanceof Warship) {
if((loc == Warship.LOC_ALS) || (loc == Warship.LOC_FLS)) {
loc = Warship.LOC_LBS;
} else if((loc == Warship.LOC_ARS) || (loc == Warship.LOC_FRS)) {
loc = Warship.LOC_RBS;
}
}
case Aero.CRIT_WEAPON:
if(a.isCapitalFighter()) {
boolean destroyAll = false;
if((loc == Aero.LOC_NOSE) || (loc == Aero.LOC_AFT)) {
destroyAll = true;
}
if(loc == Aero.LOC_WINGS) {
if(a.areWingsHit()) {
destroyAll = true;
} else {
a.setWingsHit(true);
}
}
for (Mounted weap : a.getWeaponList()) {
if (weap.getLocation() == loc) {
if(destroyAll) {
weap.setHit(true);
weap.setDestroyed(true);
} else {
weap.setNWeapons(weap.getNWeapons() / 2);
}
}
}
//also destroy any ECM or BAP in this location
for(Mounted misc : a.getMisc()) {
if(misc.getType().hasFlag(MiscType.F_ECM) || misc.getType().hasFlag(MiscType.F_ANGEL_ECM)
|| misc.getType().hasFlag(MiscType.F_BAP)) {
misc.setHit(true);
misc.setDestroyed(true);
}
}
r = new Report(9152);
r.subject = a.getId();
r.newlines = 0;
r.add(a.getLocationName(loc));
vDesc.add(r);
break;
}
r = new Report(9150);
r.subject = a.getId();
r.newlines = 0;
ArrayList<Mounted> weapons = new ArrayList<Mounted>();
for (Mounted weap : a.getWeaponList()) {
if ((weap.getLocation() == loc) && !weap.isDestroyed() && weap.getType().isHittable()) {
weapons.add(weap);
}
}
//add in in hittable misc equipment
for(Mounted misc : a.getMisc()) {
if (misc.getType().isHittable() && (misc.getLocation() == loc) && !misc.isDestroyed()) {
weapons.add(misc);
}
}
if (weapons.size() > 0) {
Mounted weapon = weapons.get(Compute.randomInt(weapons.size()));
// possibly check for an ammo explosion
// don't allow ammo explosions on fighter squadrons
if (game.getOptions().booleanOption("ammo_explosions") && !(a instanceof FighterSquadron) && (weapon.getType() instanceof WeaponType)) {
// does it use Ammo?
WeaponType wtype = (WeaponType) weapon.getType();
if (wtype.getAmmoType() != AmmoType.T_NA) {
Mounted m = weapon.getLinked();
int ammoroll = Compute.d6(2);
if (ammoroll >= 10) {
r = new Report(9151);
r.subject = a.getId();
r.add(m.getName());
r.newlines = 0;
vDesc.add(r);
vDesc.addAll(explodeEquipment(a, loc, m));
break;
}
}
}
weapon.setHit(true);
r.add(weapon.getName());
vDesc.add(r);
// explosive weapons e.g. gauss now explode
vDesc.addAll(explodeEquipment(a, loc, weapon));
weapon.setDestroyed(true);
} else {
r = new Report(9155);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
}
break;
case Aero.CRIT_ENGINE:
// engine hit
r = new Report(9140);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.engineHitsThisRound++;
boolean engineExploded = checkEngineExplosion(a, vDesc, 1);
if (((a.getEngineHits() + 1) < a.getMaxEngineHits()) && !engineExploded) {
a.setEngineHits(a.getEngineHits() + 1);
if ((a instanceof SmallCraft) || (a instanceof Jumpship)) {
a.setOriginalWalkMP(Math.max(0, a.getOriginalWalkMP() - 1));
} else {
a.setOriginalWalkMP(Math.max(0, a.getOriginalWalkMP() - 2));
}
} else {
// this engine hit puts the ASF out of commission
vDesc.addAll(destroyEntity(a, "engine destruction", true, true));
}
break;
case Aero.CRIT_LEFT_THRUSTER:
// thruster hit
r = new Report(9160);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setLeftThrustHits(a.getLeftThrustHits() + 1);
break;
case Aero.CRIT_RIGHT_THRUSTER:
// thruster hit
r = new Report(9160);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
a.setRightThrustHits(a.getRightThrustHits() + 1);
break;
case Aero.CRIT_CARGO:
// cargo hit
// First what percentage of the cargo did the hit destroy?
double percentDestroyed = 0.0;
double mult = 2.0;
if(a.isLargeCraft() && a.isClan() && game.getOptions().booleanOption("stratops_harjel")) {
mult = 4.0;
}
if (damageCaused > 0) {
percentDestroyed = Math.min(damageCaused / (mult * a.getSI()), 1.0);
}
// did it hit cargo or units
int roll = Compute.d6(1);
if (roll < 4) {
// cargo was hit
// just report; no game effect
r = new Report(9165);
r.subject = a.getId();
r.newlines = 0;
r.add((int) (percentDestroyed * 100));
vDesc.add(r);
} else {
// units were hit
// get a list of units
Vector<Entity> passengers = en.getBayLoadedUnits();
int unitsDestroyed = (int) Math.ceil(percentDestroyed * passengers.size());
r = new Report(9166);
r.subject = a.getId();
r.newlines = 0;
r.add(unitsDestroyed);
vDesc.add(r);
while (unitsDestroyed > 0) {
// redraw loaded units to make sure I don't get ones
// already destroyed
Vector<Entity> units = en.getLoadedUnits();
if (units.size() > 0) {
Entity target = units.get(Compute.randomInt(units.size()));
vDesc.addAll(destroyEntity(target, "cargo damage", false, false));
}
unitsDestroyed--;
}
}
break;
case Aero.CRIT_DOOR:
// door hit
// choose a random bay
String bayType = en.damageBayDoor();
if (!bayType.equals("none")) {
r = new Report(9170);
r.subject = a.getId();
r.add(bayType);
r.newlines = 0;
vDesc.add(r);
} else {
r = new Report(9171);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
}
break;
case Aero.CRIT_DOCK_COLLAR:
// docking collar hit
// different effect for dropships and jumpships
if (en instanceof Dropship) {
Dropship ds = (Dropship) en;
ds.setDamageDockCollar(true);
r = new Report(9175);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
}
if (en instanceof Jumpship) {
// damage the docking collar
if (en.damageDockCollar()) {
r = new Report(9176);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
} else {
r = new Report(9177);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
}
}
break;
case Aero.CRIT_KF_BOOM:
// KF boom hit
// no real effect yet
r = new Report(9180);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
break;
case Aero.CRIT_CIC:
if (js == null) {
break;
}
// CIC hit
r = new Report(9185);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
js.setCICHits(js.getCICHits() + 1);
break;
case Aero.CRIT_KF_DRIVE:
if (js == null) {
break;
}
// KF Drive hit
r = new Report(9190);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
js.setKFIntegrity(js.getKFIntegrity() - 1);
break;
case Aero.CRIT_GRAV_DECK:
if (js == null) {
break;
}
// Grave Deck hit
r = new Report(9195);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
break;
case Aero.CRIT_LIFE_SUPPORT:
// Life Support hit
a.setLifeSupport(false);
r = new Report(9196);
r.subject = a.getId();
r.newlines = 0;
vDesc.add(r);
break;
}
} else if (en instanceof BattleArmor) {
// We might as well handle this here.
// However, we're considering a crit against BA as a "crew kill".
BattleArmor ba = (BattleArmor) en;
r = new Report(6111);
int randomTrooper = ba.getRandomTrooper();
ba.destroyLocation(randomTrooper);
r.add(randomTrooper);
r.newlines = 1;
vDesc.add(r);
} else if (CriticalSlot.TYPE_SYSTEM == cs.getType()) {
// Handle critical hits on system slots.
cs.setHit(true);
if (en instanceof Protomech) {
int numHit = ((Protomech) en).getCritsHit(loc);
if ((cs.getIndex() != Protomech.SYSTEM_TORSO_WEAPON_A) && (cs.getIndex() != Protomech.SYSTEM_TORSO_WEAPON_B)) {
r = new Report(6225);
r.subject = en.getId();
r.indent(3);
r.newlines = 0;
r.add(Protomech.systemNames[cs.getIndex()]);
vDesc.addElement(r);
}
switch (cs.getIndex()) {
case Protomech.SYSTEM_HEADCRIT:
if (2 == numHit) {
r = new Report(6230);
r.subject = en.getId();
r.newlines = 0;
vDesc.addElement(r);
en.destroyLocation(loc);
}
break;
case Protomech.SYSTEM_ARMCRIT:
if (2 == numHit) {
r = new Report(6235);
r.subject = en.getId();
r.newlines = 0;
vDesc.addElement(r);
en.destroyLocation(loc);
}
break;
case Protomech.SYSTEM_LEGCRIT:
if (3 == numHit) {
r = new Report(6240);
r.subject = en.getId();
r.newlines = 0;
vDesc.addElement(r);
en.destroyLocation(loc);
}
break;
case Protomech.SYSTEM_TORSOCRIT:
if (3 == numHit) {
vDesc.addAll(destroyEntity(en, "torso destruction"));
}
// Torso weapon hits are secondary effects and
// do not occur when loading from a scenario.
else if (secondaryEffects) {
int tweapRoll = Compute.d6(1);
CriticalSlot newSlot = null;
switch (tweapRoll) {
case 1:
case 2:
newSlot = new CriticalSlot(CriticalSlot.TYPE_SYSTEM, Protomech.SYSTEM_TORSO_WEAPON_A);
vDesc.addAll(applyCriticalHit(en, Entity.NONE, newSlot, secondaryEffects));
break;
case 3:
case 4:
newSlot = new CriticalSlot(CriticalSlot.TYPE_SYSTEM, Protomech.SYSTEM_TORSO_WEAPON_B);
vDesc.addAll(applyCriticalHit(en, Entity.NONE, newSlot, secondaryEffects));
break;
case 5:
case 6:
// no effect
}
}
break;
case Protomech.SYSTEM_TORSO_WEAPON_A:
Mounted weaponA = ((Protomech) en).getTorsoWeapon(true);
if (null != weaponA) {
weaponA.setHit(true);
r = new Report(6245);
r.subject = en.getId();
r.newlines = 0;
vDesc.addElement(r);
}
break;
case Protomech.SYSTEM_TORSO_WEAPON_B:
Mounted weaponB = ((Protomech) en).getTorsoWeapon(false);
if (null != weaponB) {
weaponB.setHit(true);
r = new Report(6250);
r.subject = en.getId();
r.newlines = 0;
vDesc.addElement(r);
}
break;
} // End switch( cs.getType() )
// Shaded hits cause pilot damage.
if (((Protomech) en).shaded(loc, numHit)) {
// Destroyed Protomech sections have
// already damaged the pilot.
int pHits = Protomech.POSSIBLE_PILOT_DAMAGE[loc] - ((Protomech) en).getPilotDamageTaken(loc);
if (Math.min(1, pHits) > 0) {
Report.addNewline(vDesc);
vDesc.addAll(damageCrew(en, 1));
pHits = 1 + ((Protomech) en).getPilotDamageTaken(loc);
((Protomech) en).setPilotDamageTaken(loc, pHits);
}
} // End have-shaded-hit
} // End entity-is-protomech
else {
r = new Report(6225);
r.subject = en.getId();
r.indent(3);
r.add(((Mech) en).getSystemName(cs.getIndex()));
r.newlines = 0;
vDesc.addElement(r);
switch (cs.getIndex()) {
case Mech.SYSTEM_COCKPIT:
// Don't kill a pilot multiple times.
if (Pilot.DEATH > en.getCrew().getHits()) {
// boink!
en.getCrew().setDoomed(true);
Report.addNewline(vDesc);
vDesc.addAll(destroyEntity(en, "pilot death", true));
}
break;
case Mech.SYSTEM_ENGINE:
// if the slot is missing, the location was previously
// destroyedd and the enginehit was then counted already
if (!cs.isMissing()) {
en.engineHitsThisRound++;
}
boolean engineExploded = false;
int numEngineHits = 0;
numEngineHits += en.getHitCriticals(CriticalSlot.TYPE_SYSTEM, Mech.SYSTEM_ENGINE, Mech.LOC_CT);
numEngineHits += en.getHitCriticals(CriticalSlot.TYPE_SYSTEM, Mech.SYSTEM_ENGINE, Mech.LOC_RT);
numEngineHits += en.getHitCriticals(CriticalSlot.TYPE_SYSTEM, Mech.SYSTEM_ENGINE, Mech.LOC_LT);
engineExploded = checkEngineExplosion(en, vDesc, numEngineHits);
if (!engineExploded && (numEngineHits > 2)) {
// third engine hit
vDesc.addAll(destroyEntity(en, "engine destruction"));
if (game.getOptions().booleanOption("auto_abandon_unit")) {
vDesc.addAll(abandonEntity(en));
}
}
break;
case Mech.SYSTEM_GYRO:
if (en.getGyroType() != Mech.GYRO_HEAVY_DUTY) {
int gyroHits = en.getHitCriticals(CriticalSlot.TYPE_SYSTEM, Mech.SYSTEM_GYRO, loc);
// check for < 3 to make sure only one PSR gets
// scheduled,
// otherwise the avoid pilot damage to-hit is too high
// pilot value at 3 so we end up at a total of +6
if ((gyroHits > 1) && (gyroHits < 3)) {
// gyro destroyed
game.addPSR(new PilotingRollData(en.getId(), TargetRoll.AUTOMATIC_FAIL, 3, "gyro destroyed"));
} else {
// first gyro hit
game.addPSR(new PilotingRollData(en.getId(), 3, "gyro hit"));
}// No check against HD Gyros.
} else {
int gyroHits = en.getHitCriticals(CriticalSlot.TYPE_SYSTEM, Mech.SYSTEM_GYRO, loc);
// check for < 4 to make sure only one PSR gets
// scheduled,
// otherwise the avoid pilot damage to-hit is too high
// pilot value at 3 so we end up at a total of +6
if ((gyroHits > 2) && (gyroHits < 4)) {
// gyro destroyed
game.addPSR(new PilotingRollData(en.getId(), TargetRoll.AUTOMATIC_FAIL, 1, "gyro destroyed"));
} else if (gyroHits == 1) {
// first gyro hit
game.addPSR(new PilotingRollData(en.getId(), 2, "gyro hit"));
} else {
// second gyro hit
game.addPSR(new PilotingRollData(en.getId(), 3, "gyro hit"));
}
}
break;
case Mech.ACTUATOR_UPPER_LEG:
case Mech.ACTUATOR_LOWER_LEG:
case Mech.ACTUATOR_FOOT:
// leg/foot actuator piloting roll
game.addPSR(new PilotingRollData(en.getId(), 1, "leg/foot actuator hit"));
break;
case Mech.ACTUATOR_HIP:
// hip piloting roll
game.addPSR(new PilotingRollData(en.getId(), 2, "hip actuator hit"));
break;
}
} // End entity-is-mek
} // End crit-on-system-slot
// Handle critical hits on equipment slots.
else if (CriticalSlot.TYPE_EQUIPMENT == cs.getType()) {
cs.setHit(true);
Mounted mounted = en.getEquipment(cs.getIndex());
EquipmentType eqType = mounted.getType();
boolean hitBefore = mounted.isHit();
r = new Report(6225);
r.subject = en.getId();
r.indent(3);
r.add(mounted.getDesc());
r.newlines = 0;
vDesc.addElement(r);
// Shield objects are not useless when they take one crit.
// Shields can be critted and still be usable.
if ((eqType instanceof MiscType) && ((MiscType) eqType).isShield()) {
mounted.setHit(false);
} else {
mounted.setHit(true);
}
if ((eqType instanceof MiscType) && eqType.hasFlag(MiscType.F_HARJEL)) {
r = new Report(6254);
r.subject = en.getId();
r.indent(2);
vDesc.add(r);
vDesc.addAll(breachLocation(en, loc, null, true));
}
// If the item is the ECM suite of a Mek Stealth system
// then it's destruction turns off the stealth.
if (!hitBefore && (eqType instanceof MiscType) && eqType.hasFlag(MiscType.F_ECM) && (mounted.getLinkedBy() != null)) {
Mounted stealth = mounted.getLinkedBy();
r = new Report(6255);
r.subject = en.getId();
r.indent(2);
r.add(stealth.getType().getName());
r.newlines = 0;
vDesc.addElement(r);
stealth.setMode("Off");
}
// Handle equipment explosions.
// Equipment explosions are secondary effects and
// do not occur when loading from a scenario.
if (((secondaryEffects && eqType.isExplosive()) || mounted.isHotLoaded() || mounted.hasChargedCapacitor()) && !hitBefore) {
vDesc.addAll(explodeEquipment(en, loc, mounted));
}
// Make sure that ammo in this slot is exhaused.
if (mounted.getShotsLeft() > 0) {
mounted.setShotsLeft(0);
}
} // End crit-on-equipment-slot
// mechs with TSM hit by anti-tsm missiles this round get another crit
if ((en instanceof Mech) && en.hitThisRoundByAntiTSM) {
Mech mech = (Mech) en;
if (mech.hasTSM()) {
r = new Report(6430);
r.subject = en.getId();
r.indent(2);
r.addDesc(en);
r.newlines = 0;
vDesc.addElement(r);
vDesc.addAll(oneCriticalEntity(en, Compute.d6(2)));
}
en.hitThisRoundByAntiTSM = false;
}
// if using buffered VDNI then a possible pilot hit
if (en.crew.getOptions().booleanOption("bvdni") && !en.crew.getOptions().booleanOption("pain_shunt")) {
Report.addNewline(vDesc);
int roll = Compute.d6(2);
r = new Report(3580);
r.subject = en.getId();
r.addDesc(en);
r.add(7);
r.add(roll);
r.choose(roll >= 8);
r.indent(2);
vDesc.add(r);
if (roll >= 7) {
vDesc.addAll(damageCrew(en, 1));
}
}