/**
* Handle a club attack
*/
private void resolveClubAttack(PhysicalResult pr, int lastEntityId) {
final ClubAttackAction caa = (ClubAttackAction) pr.aaa;
final Entity ae = game.getEntity(caa.getEntityId());
// get damage, ToHitData and roll from the PhysicalResult
int damage = pr.damage;
final ToHitData toHit = pr.toHit;
int roll = pr.roll;
final Targetable target = game.getTarget(caa.getTargetType(), caa.getTargetId());
Entity te = null;
if (target.getTargetType() == Targetable.TYPE_ENTITY) {
te = (Entity) target;
}
boolean throughFront = true;
if (te != null) {
throughFront = Compute.isThroughFrontHex(game, ae.getPosition(), te);
}
final boolean targetInBuilding = Compute.isInBuilding(game, te);
final boolean glancing = game.getOptions().booleanOption("tacops_glancing_blows") && (roll == toHit.getValue());
// Set Margin of Success/Failure.
toHit.setMoS(roll - Math.max(2, toHit.getValue()));
final boolean directBlow = game.getOptions().booleanOption("tacops_direct_blow") && ((toHit.getMoS() / 3) >= 1);
Report r;
// Which building takes the damage?
Building bldg = game.getBoard().getBuildingAt(target.getPosition());
// restore club attack
caa.getClub().restore();
// Shield bash causes 1 point of damage to the shield
if (((MiscType) caa.getClub().getType()).isShield()) {
((Mech) ae).shieldAbsorptionDamage(1, caa.getClub().getLocation(), false);
}
if (lastEntityId != caa.getEntityId()) {
// who is making the attacks
r = new Report(4005);
r.subject = ae.getId();
r.addDesc(ae);
addReport(r);
}
r = new Report(4145);
r.subject = ae.getId();
r.indent();
r.add(caa.getClub().getName());
r.add(target.getDisplayName());
r.newlines = 0;
addReport(r);
// Flail/Wrecking Ball auto misses on a 2 and hits themself.
if ((((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_FLAIL)
|| ((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_WRECKING_BALL))
&& (roll == 2)) {
// miss
r = new Report(4035);
r.subject = ae.getId();
addReport(r);
ToHitData newToHit = new ToHitData(TargetRoll.AUTOMATIC_SUCCESS, "hit with own flail/wrecking ball");
pr.damage /= 2;
newToHit.setHitTable(ToHitData.HIT_NORMAL);
newToHit.setSideTable(ToHitData.SIDE_FRONT);
pr.toHit = newToHit;
pr.aaa.setTargetId(ae.getId());
pr.aaa.setTargetType(Targetable.TYPE_ENTITY);
pr.roll = Integer.MAX_VALUE;
resolveClubAttack(pr, ae.getId());
game.addPSR(new PilotingRollData(ae.getId(), 0, "missed a flail/wrecking ball attack"));
return;
}
// Need to compute 2d6 damage. and add +3 heat build up.
if (((MiscType) (caa.getClub().getType())).hasSubType(MiscType.S_BUZZSAW)) {
damage = Compute.d6(2);
ae.heatBuildup += 3;
// Buzzsaw's blade will shatter on a roll of 2.
if (roll == 2) {
Mounted club = caa.getClub();
for (Mounted eq : ae.getWeaponList()) {
if ((eq.getLocation() == club.getLocation()) && (eq.getType() instanceof MiscType) && ((MiscType) eq.getType()).hasFlag(MiscType.F_CLUB) && ((MiscType) eq.getType()).hasSubType(MiscType.S_BUZZSAW)) {
eq.setDestroyed(true);
break;
}
}
r = new Report(4037);
r.subject = ae.getId();
addReport(r);
damage = 0;
return;
}
}
if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
r = new Report(4075);
r.subject = ae.getId();
r.add(toHit.getDesc());
addReport(r);
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_MACE_THB)) {
game.addPSR(new PilotingRollData(ae.getId(), 0, "missed a mace attack"));
}
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_MACE)) {
game.addPSR(new PilotingRollData(ae.getId(), 2, "missed a mace attack"));
}
return;
} else if (toHit.getValue() == TargetRoll.AUTOMATIC_SUCCESS) {
r = new Report(4080);
r.subject = ae.getId();
r.add(toHit.getDesc());
r.newlines = 0;
addReport(r);
roll = Integer.MAX_VALUE;
} else {
// report the roll
r = new Report(4025);
r.subject = ae.getId();
r.add(toHit.getValue());
r.add(roll);
r.newlines = 0;
addReport(r);
if (glancing) {
r = new Report(4030);
r.subject = ae.getId();
r.newlines = 0;
addReport(r);
}
if (directBlow) {
r = new Report(4032);
r.subject = ae.getId();
r.newlines = 0;
addReport(r);
}
}
// do we hit?
if (roll < toHit.getValue()) {
// miss
r = new Report(4035);
r.subject = ae.getId();
addReport(r);
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_MACE_THB)) {
game.addPSR(new PilotingRollData(ae.getId(), 0, "missed a mace attack"));
}
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_MACE)) {
game.addPSR(new PilotingRollData(ae.getId(), 2, "missed a mace attack"));
}
// If the target is in a building, the building absorbs the damage.
if (targetInBuilding && (bldg != null)) {
// Only report if damage was done to the building.
if (damage > 0) {
Vector<Report> buildingReport = damageBuilding(bldg, damage, target.getPosition());
for (Report report : buildingReport) {
report.subject = ae.getId();
}
addReport(buildingReport);
}
}
return;
}
// Targeting a building.
if ((target.getTargetType() == Targetable.TYPE_BUILDING)
|| (target.getTargetType() == Targetable.TYPE_FUEL_TANK)) {
// The building takes the full brunt of the attack.
r = new Report(4040);
r.subject = ae.getId();
addReport(r);
Vector<Report> buildingReport = damageBuilding(bldg, damage, target.getPosition());
for (Report report : buildingReport) {
report.subject = ae.getId();
}
addReport(buildingReport);
// Damage any infantry in the hex.
damageInfantryIn(bldg, damage, target.getPosition());
// And we're done!
return;
}
HitData hit = te.rollHitLocation(toHit.getHitTable(), toHit.getSideTable());
hit.setGeneralDamageType(HitData.DAMAGE_PHYSICAL);
r = new Report(4045);
r.subject = ae.getId();
r.add(toHit.getTableDesc());
r.add(te.getLocationAbbr(hit));
r.newlines = 0;
addReport(r);
// The building shields all units from a certain amount of damage.
// The amount is based upon the building's CF at the phase's start.
if (targetInBuilding && (bldg != null)) {
int bldgAbsorbs = (int) Math.ceil(bldg.getPhaseCF(target.getPosition()) / 10.0);
int toBldg = Math.min(bldgAbsorbs, damage);
damage -= toBldg;
addNewLines();
Vector<Report> buildingReport = damageBuilding(bldg, damage, target.getPosition());
for (Report report : buildingReport) {
report.subject = ae.getId();
}
addReport(buildingReport);
}
// A building may absorb the entire shot.
if (damage == 0) {
r = new Report(4050);
r.subject = ae.getId();
r.add(te.getShortName());
r.add(te.getOwner().getName());
r.newlines = 0;
addReport(r);
} else {
if (glancing) {
damage = (int) Math.floor(damage / 2.0);
}
if (directBlow) {
damage += toHit.getMoS() / 3;
hit.makeDirectBlow(toHit.getMoS() / 3);
}
/*if (damage >= 1 && te.hasWorkingMisc(MiscType.F_SPIKES, -1, hit.getLocation())) {
r = new Report(4330);
r.indent(2);
r.newlines = 0;
r.subject = ae.getId();
addReport(r);
checkBreakSpikes(te, hit.getLocation());
damage = Math.max(1, damage - 4);
int loc = caa.getClub().getLocation();
if (loc == Entity.LOC_NONE) {
addReport(damageEntity(ae, new HitData(Mech.LOC_LARM), 1, false, DamageType.NONE, false, false, false));
addReport(damageEntity(ae, new HitData(Mech.LOC_RARM), 1, false, DamageType.NONE, false, false, false));
} else {
addReport(damageEntity(ae, new HitData(loc), 2, false, DamageType.NONE, false, false, false));
}
}*/
DamageType damageType = DamageType.NONE;
addReport(damageEntity(te, hit, damage, false, damageType, false, false, throughFront));
if (target instanceof VTOL) {
// destroy rotor
addReport(applyCriticalHit(te, VTOL.LOC_ROTOR, new CriticalSlot(CriticalSlot.TYPE_SYSTEM, VTOL.CRIT_ROTOR_DESTROYED), false));
}
}
// On a roll of 10+ a lance hitting a mech/Vehicle can cause 1 point of
// internal damage
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_LANCE) && (te.getArmor(hit) > 0) && (te.getArmorType() != EquipmentType.T_ARMOR_HARDENED)) {
roll = Compute.d6(2);
// Pierce checking report
r = new Report(4021);
r.indent(2);
r.subject = ae.getId();
r.add(te.getLocationAbbr(hit));
r.add(roll);
r.newlines = 1;
addReport(r);
if (roll >= 10) {
hit.makeGlancingBlow();
addReport(damageEntity(te, hit, 1, false, DamageType.NONE, true, false, throughFront));
}
}
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_CHAIN_WHIP)
&& (te instanceof Mech)) {
addNewLines();
int loc = hit.getLocation();
int toHitNumber = toHit.getValue();
if ( ((loc == Mech.LOC_LLEG) || (loc == Mech.LOC_RLEG)) && (!te.hasActiveShield(loc) && !te.hasPassiveShield(loc))) {
roll = Compute.d6(2);
if ( (((Mech)ae).hasTSM() && (ae.heat >= 9))
&& (!((Mech)te).hasTSM() || ((((Mech)te).hasTSM()) && (te.heat < 9))) ) {
toHitNumber -= 2;
}
r = new Report(4450);
r.subject = ae.getId();
r.add(ae.getShortName());
r.add(te.getShortName());
r.add(toHitNumber);
r.add(roll);
r.indent(2);
r.newlines = 0;
addReport(r);
if ( roll >= toHit.getValue() ) {
r = new Report(2270);
r.subject = ae.getId();
r.newlines = 0;
addReport(r);
game.addPSR(new PilotingRollData(te.getId(),3,"Snared by chain whip"));
}else {
r = new Report(2357);
r.subject = ae.getId();
r.newlines = 0;
addReport(r);
}
} else if ( ((loc == Mech.LOC_RARM) || (loc == Mech.LOC_LARM))
&& (!te.hasActiveShield(loc) && !te.hasPassiveShield(loc) && !te.hasNoDefenseShield(loc)) ) {
GrappleAttackAction gaa = new GrappleAttackAction(ae.getId(),te.getId());
ToHitData grappleHit = GrappleAttackAction.toHit(game, ae.getId(), target);
PhysicalResult grappleResult = new PhysicalResult();
grappleResult.aaa = gaa;
grappleResult.toHit = grappleHit;
grappleResult.roll = Compute.d6(2);
resolveGrappleAttack(grappleResult, lastEntityId, hit.getLocation() == Mech.LOC_RARM ? Entity.GRAPPLE_RIGHT : Entity.GRAPPLE_LEFT );
}
}
addNewLines();
if (((MiscType) caa.getClub().getType()).hasSubType(MiscType.S_TREE_CLUB)) {
// the club breaks
r = new Report(4150);
r.subject = ae.getId();
r.add(caa.getClub().getName());
addReport(r);
ae.removeMisc(caa.getClub().getName());
}
//if the target is an industrial mech, it needs to check for crits
//at the end of turn
if ((target instanceof Mech) && ((Mech)target).isIndustrial()) {