crashedIntoTerrain = true;
}
}
if (nextHex.containsTerrain(Terrains.BLDG_ELEV)) {
Building bldg = game.getBoard().getBuildingAt(nextPos);
if (bldg.getType() == Building.WALL) {
crashedIntoTerrain = true;
}
}
// however WIGE can gain 1 level to avoid crashing into the terrain
if (entity.getMovementMode() == IEntityMovementMode.WIGE) {
if ((nextElevation == 0) && !(nextHex.containsTerrain(Terrains.WOODS) || nextHex.containsTerrain(Terrains.JUNGLE))) {
nextElevation = 1;
crashedIntoTerrain = false;
} else if ((nextElevation == 1) && (nextHex.containsTerrain(Terrains.WOODS) || nextHex.containsTerrain(Terrains.JUNGLE))) {
nextElevation = 2;
crashedIntoTerrain = false;
}
}
if (crashedIntoTerrain) {
if (nextHex.containsTerrain(Terrains.BLDG_ELEV)) {
Building bldg = game.getBoard().getBuildingAt(nextPos);
// If you crash into a wall you want to stop in the hex
// before the wall not in the wall
// Like a building.
if (bldg.getType() == Building.WALL) {
r = new Report(2047);
} else {
r = new Report(2045);
}
} else {
r = new Report(2045);
}
r.subject = entity.getId();
r.indent();
r.add(nextPos.getBoardNum(), true);
addReport(r);
if ((entity.getMovementMode() == IEntityMovementMode.WIGE) || (entity.getMovementMode() == IEntityMovementMode.VTOL)) {
int hitSide = step.getFacing() - direction + 6;
hitSide %= 6;
int table = 0;
switch (hitSide) {// quite hackish...I think it ought to
// work, though.
case 0:// can this happen?
table = ToHitData.SIDE_FRONT;
break;
case 1:
case 2:
table = ToHitData.SIDE_LEFT;
break;
case 3:
table = ToHitData.SIDE_REAR;
break;
case 4:
case 5:
table = ToHitData.SIDE_RIGHT;
break;
}
elevation = nextElevation;
addReport(crashVTOLorWiGE((VTOL) entity, true, distance, curPos, elevation, table));
if ((nextHex.containsTerrain(Terrains.WATER) && !nextHex.containsTerrain(Terrains.ICE)) || nextHex.containsTerrain(Terrains.WOODS) || nextHex.containsTerrain(Terrains.JUNGLE)) {
addReport(destroyEntity(entity, "could not land in crash site"));
} else if (elevation < nextHex.terrainLevel(Terrains.BLDG_ELEV)) {
Building bldg = game.getBoard().getBuildingAt(nextPos);
// If you crash into a wall you want to stop in the hex
// before the wall not in the wall
// Like a building.
if (bldg.getType() == Building.WALL) {
addReport(destroyEntity(entity, "crashed into a wall"));
break;
}
addReport(destroyEntity(entity, "crashed into building"));
} else {
entity.setPosition(nextPos);
entity.setElevation(0);
addReport(doEntityDisplacementMinefieldCheck(entity, curPos, nextPos, nextElevation));
}
curPos = nextPos;
break;
}
// skidding into higher terrain does weight/20
// damage in 5pt clusters to front.
int damage = ((int) entity.getWeight() + 19) / 20;
while (damage > 0) {
addReport(damageEntity(entity, entity.rollHitLocation(ToHitData.HIT_NORMAL, ToHitData.SIDE_FRONT), Math.min(5, damage)));
damage -= 5;
}
// Stay in the current hex and stop skidding.
break;
}
// Have skidding units suffer falls (off a cliff).
else if (curAltitude > nextAltitude + entity.getMaxElevationChange()) {
// WIGE can avoid this too, if they have 2MP to spend
if ((entity.getMovementMode() == IEntityMovementMode.WIGE) && (entity.getRunMP() - 2 >= entity.mpUsed)) {
entity.mpUsed += 2;
nextAltitude = curAltitude;
} else {
addReport(doEntityFallsInto(entity, curPos, nextPos, entity.getBasePilotingRoll(step.getMovementType())));
addReport(doEntityDisplacementMinefieldCheck(entity, curPos, nextPos, nextElevation));
// Stay in the current hex and stop skidding.
break;
}
}
// Get any building in the hex.
Building bldg = null;
if (nextElevation < nextHex.terrainLevel(Terrains.BLDG_ELEV)) {
// We will only run into the building if its at a higher level,
// otherwise we skid over the roof
bldg = game.getBoard().getBuildingAt(nextPos);
}
boolean bldgSuffered = false;
boolean stopTheSkid = false;
// Does the next hex contain an entities?
// ASSUMPTION: hurt EVERYONE in the hex.
Enumeration<Entity> targets = game.getEntities(nextPos);
if (targets.hasMoreElements()) {
boolean skidChargeHit = false;
while (targets.hasMoreElements()) {
Entity target = targets.nextElement();
if ((target.getElevation() > nextElevation + entity.getHeight()) || (target.absHeight() < nextElevation)) {
// target is not in the way
continue;
}
// Can the target avoid the skid?
if (!target.isDone()) {
if (target instanceof Infantry) {
r = new Report(2420);
r.subject = target.getId();
r.addDesc(target);
addReport(r);
continue;
} else if (target instanceof Protomech) {
if (target != Compute.stackingViolation(game, entity, nextPos, null)) {
r = new Report(2420);
r.subject = target.getId();
r.addDesc(target);
addReport(r);
continue;
}
} else {
PilotingRollData psr = target.getBasePilotingRoll();
psr.addModifier(0, "avoiding collision");
int roll = Compute.d6(2);
r = new Report(2425);
r.subject = target.getId();
r.addDesc(target);
r.add(psr.getValue());
r.add(psr.getDesc());
r.add(roll);
addReport(r);
if (roll >= psr.getValue()) {
game.removeTurnFor(target);
avoidedChargeUnits.add(target);
continue;
// TODO: the charge should really be suspended
// and resumed after the target moved.
}
}
}
// Mechs and vehicles get charged,
// but need to make a to-hit roll
if ((target instanceof Mech) || (target instanceof Tank)) {
ChargeAttackAction caa = new ChargeAttackAction(entity.getId(), target.getTargetType(), target.getTargetId(), target.getPosition());
ToHitData toHit = caa.toHit(game, true);
// roll
int roll = Compute.d6(2);
// Update report.
r = new Report(2050);
r.subject = entity.getId();
r.indent();
r.add(target.getShortName(), true);
r.add(nextPos.getBoardNum(), true);
r.newlines = 0;
addReport(r);
if (toHit.getValue() == TargetRoll.IMPOSSIBLE) {
roll = -12;
r = new Report(2055);
r.subject = entity.getId();
r.add(toHit.getDesc());
r.newlines = 0;
addReport(r);
} else if (toHit.getValue() == TargetRoll.AUTOMATIC_SUCCESS) {
r = new Report(2060);
r.subject = entity.getId();
r.add(toHit.getDesc());
r.newlines = 0;
addReport(r);
roll = Integer.MAX_VALUE;
} else {
// report the roll
r = new Report(2065);
r.subject = entity.getId();
r.add(toHit.getValue());
r.add(roll);
r.newlines = 0;
addReport(r);
}
// Resolve a charge against the target.
// ASSUMPTION: buildings block damage for
// *EACH* entity charged.
if (roll < toHit.getValue()) {
r = new Report(2070);
r.subject = entity.getId();
addReport(r);
} else {
// Resolve the charge.
resolveChargeDamage(entity, target, toHit, direction);
// HACK: set the entity's location
// to the original hex again, for the other targets
if (targets.hasMoreElements()) {
entity.setPosition(curPos);
}
bldgSuffered = true;
skidChargeHit = true;
// The skid ends here if the target lives.
if (!target.isDoomed() && !target.isDestroyed() && !game.isOutOfGame(target)) {
stopTheSkid = true;
}
}
// if we don't do this here,
// we can have a mech without a leg
// standing on the field and moving
// as if it still had his leg after
// getting skid-charged.
if (!target.isDone()) {
addReport(resolvePilotingRolls(target));
game.resetPSRs(target);
target.applyDamage();
addNewLines();
}
}
// Resolve "move-through" damage on infantry.
// Infantry inside of a building don't get a
// move-through, but suffer "bleed through"
// from the building.
else if ((target instanceof Infantry) && (bldg != null)) {
// Update report.
r = new Report(2075);
r.subject = entity.getId();
r.indent();
r.add(target.getShortName(), true);
r.add(nextPos.getBoardNum(), true);
r.newlines = 0;
addReport(r);
// Infantry don't have different
// tables for punches and kicks
HitData hit = target.rollHitLocation(ToHitData.HIT_NORMAL, Compute.targetSideTable(entity, target));
hit.setGeneralDamageType(HitData.DAMAGE_PHYSICAL);
// Damage equals tonnage, divided by 5.
// ASSUMPTION: damage is applied in one hit.
addReport(damageEntity(target, hit, Math.round(entity.getWeight() / 5)));
addNewLines();
}
// Has the target been destroyed?
if (target.isDoomed()) {
// Has the target taken a turn?
if (!target.isDone()) {
// Dead entities don't take turns.
game.removeTurnFor(target);
send(createTurnVectorPacket());
} // End target-still-to-move
// Clean out the entity.
target.setDestroyed(true);
game.moveToGraveyard(target.getId());
send(createRemoveEntityPacket(target.getId()));
}
// Update the target's position,
// unless it is off the game map.
if (!game.isOutOfGame(target)) {
entityUpdate(target.getId());
}
} // Check the next entity in the hex.
if (skidChargeHit) {
// HACK: set the entities position to that
// hex's coords, because we had to move the entity
// back earlier for the other targets
entity.setPosition(nextPos);
}
for (Entity e : avoidedChargeUnits) {
GameTurn newTurn = new GameTurn.SpecificEntityTurn(e.getOwner().getId(), e.getId());
game.insertNextTurn(newTurn);
send(createTurnVectorPacket());
}
}
// Handle the building in the hex.
if (bldg != null) {
// Report that the entity has entered the bldg.
r = new Report(2080);
r.subject = entity.getId();
r.indent();
r.add(bldg.getName());
r.add(nextPos.getBoardNum(), true);
addReport(r);
// If the building hasn't already suffered
// damage, then apply charge damage to the
// building and displace the entity inside.
// ASSUMPTION: you don't charge the building
// if Tanks or Mechs were charged.
int chargeDamage = ChargeAttackAction.getDamageFor(entity, game.getOptions().booleanOption("tacops_charge_damage"), entity.delta_distance);
if (!bldgSuffered) {
Vector<Report> reports = damageBuilding(bldg, chargeDamage, nextPos);
for (Report report : reports) {
report.subject = entity.getId();
}
addReport(reports);
// Apply damage to the attacker.
int toAttacker = ChargeAttackAction.getDamageTakenBy(entity, bldg, nextPos);
HitData hit = entity.rollHitLocation(ToHitData.HIT_NORMAL, entity.sideTable(nextPos));
hit.setGeneralDamageType(HitData.DAMAGE_PHYSICAL);
addReport(damageEntity(entity, hit, toAttacker));
addNewLines();
entity.setPosition(nextPos);
entity.setElevation(nextElevation);
addReport(doEntityDisplacementMinefieldCheck(entity, curPos, nextPos, nextElevation));
curPos = nextPos;
} // End buildings-suffer-too
// Any infantry in the building take damage
// equal to the building being charged.
// ASSUMPTION: infantry take no damage from the
// building absorbing damage from
// Tanks and Mechs being charged.
damageInfantryIn(bldg, chargeDamage, nextPos);
// If a building still stands, then end the skid,
// and add it to the list of affected buildings.
if (bldg.getCurrentCF(nextPos) > 0) {
stopTheSkid = true;
addAffectedBldg(bldg, false);
} else {
// otherwise it collapses immediately on our head
checkForCollapse(bldg, game.getPositionMap(), nextPos, true);
}
} // End handle-building.
// Do we stay in the current hex and stop skidding?
if (stopTheSkid) {
break;
}
// Update entity position and elevation
entity.setPosition(nextPos);
entity.setElevation(nextElevation);
addReport(doEntityDisplacementMinefieldCheck(entity, curPos, nextPos, nextElevation));
skidDistance++;
// Check for collapse of any building the entity might be on
Building roof = game.getBoard().getBuildingAt(nextPos);
if (roof != null) {
if (checkForCollapse(roof, game.getPositionMap(), nextPos, true)) {
break; // stop skidding if the building collapsed
}
}