final DfaAttackAction daa = (DfaAttackAction) pr.aaa;
final Entity ae = game.getEntity(daa.getEntityId());
final Targetable target = game.getTarget(daa.getTargetType(), daa.getTargetId());
// get damage, ToHitData and roll from the PhysicalResult
int damage = pr.damage;
final ToHitData toHit = pr.toHit;
int roll = pr.roll;
Entity te = null;
if ((target != null) && (target.getTargetType() == Targetable.TYPE_ENTITY)) {
// Lets re-write around that horrible hack that was here before.
// So instead of asking if a specific location is wet and praying
// that it won't cause an NPE...
// We'll check 1) if the hex has water, and 2) if it's deep enough
// to cover the unit in question at its current elevation.
// It's especially important to make sure it's done this way,
// because some units (Sylph, submarines) can be at ANY elevation
// underwater, and VTOLs can be well above the surface.
te = (Entity) target;
IHex hex = game.getBoard().getHex(te.getPosition());
if (hex.containsTerrain(Terrains.WATER)) {
if (te.absHeight() < hex.getElevation()) {
damage = (int) Math.ceil(damage * 0.5f);
}
}
}
boolean throughFront = true;
if (te != null) {
throughFront = Compute.isThroughFrontHex(game, ae.getPosition(), 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(daa.getTargetPos());
// is the attacker dead? because that sure messes up the calculations
if (ae == null) {
return;
}
final int direction = ae.getFacing();
if (lastEntityId != daa.getEntityId()) {
// who is making the attack
r = new Report(4005);
r.subject = ae.getId();
r.addDesc(ae);
addReport(r);
}
// entity isn't DFAing any more
ae.setDisplacementAttack(null);
// should we even bother?
if ((target == null) || ((target.getTargetType() == Targetable.TYPE_ENTITY) && (te.isDestroyed() || te.isDoomed() || te.crew.isDead()))) {
r = new Report(4245);
r.subject = ae.getId();
r.indent();
addReport(r);
if (ae.isProne()) {
// attacker prone during weapons phase
addReport(doEntityFall(ae, daa.getTargetPos(), 2, 3, ae.getBasePilotingRoll()));
} else {
// same effect as successful DFA
addReport(doEntityDisplacement(ae, ae.getPosition(), daa.getTargetPos(), new PilotingRollData(ae.getId(), 4, "executed death from above")));
}
return;
}
r = new Report(4246);
r.subject = ae.getId();
r.indent();
r.add(target.getDisplayName());
r.newlines = 0;
addReport(r);
// target still in the same position?
if (!target.getPosition().equals(daa.getTargetPos())) {
r = new Report(4215);
r.subject = ae.getId();
addReport(r);
addReport(doEntityFallsInto(ae, ae.getPosition(), daa.getTargetPos(), ae.getBasePilotingRoll()));
return;
}
// hack: if the attacker's prone, or incapacitated, fudge the roll
if (ae.isProne() || !ae.isActive()) {
roll = -12;
r = new Report(4250);
r.subject = ae.getId();
r.add(toHit.getDesc());
addReport(r);
} else if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
roll = -12;
r = new Report(4255);
r.subject = ae.getId();
r.add(toHit.getDesc());
addReport(r);
} else if (toHit.getValue() == TargetRoll.AUTOMATIC_SUCCESS) {
r = new Report(4260);
r.subject = ae.getId();
r.add(toHit.getDesc());
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()) {
Coords dest = te.getPosition();
Coords targetDest = Compute.getPreferredDisplacement(game, te.getId(), dest, direction);
// miss
r = new Report(4035);
r.subject = ae.getId();
addReport(r);
if (targetDest != null) {
// attacker falls into destination hex
r = new Report(4265);
r.subject = ae.getId();
r.addDesc(ae);
r.add(dest.getBoardNum(), true);
addReport(r);
// move target to preferred hex
addReport(doEntityDisplacement(te, dest, targetDest, null));
int height = 2 + (game.getBoard().getHex(dest).containsTerrain(Terrains.BLDG_ELEV) ? game.getBoard().getHex(dest).terrainLevel(Terrains.BLDG_ELEV) : 0);
addReport(doEntityFall(ae, dest, height, 3, ae.getBasePilotingRoll()));
} else {
// attacker destroyed
// Tanks suffer an ammo/power plant hit.
// TODO : a Mech suffers a Head Blown Off crit.
addReport(destroyEntity(ae, "impossible displacement", ae instanceof Mech, ae instanceof Mech));
}
return;
}
// we hit...
// Target entities are pushed away or destroyed.
Coords dest = te.getPosition();
Coords targetDest = Compute.getValidDisplacement(game, te.getId(), dest, direction);
if (targetDest != null) {
addReport(doEntityDisplacement(te, dest, targetDest, new PilotingRollData(te.getId(), 2, "hit by death from above")));
} else {
// ack! automatic death! Tanks
// suffer an ammo/power plant hit.
// TODO : a Mech suffers a Head Blown Off crit.
addReport(destroyEntity(te, "impossible displacement", te instanceof Mech, te instanceof Mech));
}
// Can't DFA a target inside of a building.
int damageTaken = DfaAttackAction.getDamageTakenBy(ae);
r = new Report(4040);
r.subject = ae.getId();
addReport(r);
// Targeting a building.
if ((target.getTargetType() == Targetable.TYPE_BUILDING) || (target.getTargetType() == Targetable.TYPE_FUEL_TANK)) {
// The building takes the full brunt of the attack.
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());
} else { // Target isn't building.
if (glancing) {
damage = (int) Math.floor(damage / 2.0);
}
if (directBlow) {
damage += toHit.getMoS() / 3;
}
// damage target
r = new Report(4230);
r.subject = ae.getId();
r.add(damage);
r.add(toHit.getTableDesc());
r.newlines = 0;
addReport(r);
// work out which locations have spikes
int[] spikes = new int[te.locations()];
for (int i = 0; i < te.locations(); i++) {
spikes[i] = 0;
}
for (Mounted m : te.getMisc()) {
if ((m.getLocation() != Entity.LOC_NONE) && m.getType().hasFlag(MiscType.F_SPIKES)) {
spikes[m.getLocation()] = 1;
}
}
int spikeDamage = 0;
while (damage > 0) {
int cluster = Math.min(5, damage);
HitData hit = te.rollHitLocation(toHit.getHitTable(), toHit.getSideTable());
hit.setGeneralDamageType(HitData.DAMAGE_PHYSICAL);
if (directBlow) {
hit.makeDirectBlow(toHit.getMoS() / 3);
}
if (spikes[hit.getLocation()] == 1) {
r = new Report(4330);
r.indent(2);