// iterate through steps
firstStep = true;
turnOver = false;
/* Bug 754610: Revert fix for bug 702735. */
MoveStep prevStep = null;
Vector<UnitLocation> movePath = new Vector<UnitLocation>();
for (final Enumeration<MoveStep> i = md.getSteps(); i.hasMoreElements();) {
final MoveStep step = i.nextElement();
wasProne = entity.isProne();
boolean isPavementStep = step.isPavementStep();
entity.inReverse = step.isThisStepBackwards();
boolean entityFellWhileAttemptingToStand = false;
// stop for illegal movement
if (step.getMovementType() == IEntityMovementType.MOVE_ILLEGAL) {
break;
}
// stop if the entity already killed itself
if (entity.isDestroyed() || entity.isDoomed()) {
break;
}
if (firstStep && (entity.getMovementMode() == IEntityMovementMode.WIGE) && (entity.getElevation() == 0)) {
wigeStartedLanded = true;
}
// check for MASC failure on first step
if (firstStep && (entity instanceof Mech)) {
HashMap<Integer, CriticalSlot> crits = new HashMap<Integer, CriticalSlot>();
Vector<Report> vReport = new Vector<Report>();
if (((Mech) entity).checkForMASCFailure(md, vReport, crits)) {
addReport(vReport);
for (Integer loc : crits.keySet()) {
CriticalSlot cs = crits.get(loc);
addReport(applyCriticalHit(entity, loc, cs, true));
}
// do any PSR immediately
addReport(resolvePilotingRolls(entity));
game.resetPSRs(entity);
// let the player replot their move as MP might be changed
md.clear();
fellDuringMovement = true; // so they get a new turn
} else {
addReport(vReport);
}
}
// did the entity move?
didMove = step.getDistance() > distance;
// check for aero stuff
if (entity instanceof Aero) {
Aero a = (Aero) entity;
j++;
// TODO: change the way this check is made
if (!didMove && (md.length() != j)) {
thrustUsed += step.getMp();
} else {
// if this was the last move and distance was zero, then add
// thrust
if (!didMove && (md.length() == j)) {
thrustUsed += step.getMp();
}
// then we moved to a new hex or the last step so check
// conditions
// structural damage
rollTarget = a.checkThrustSI(thrustUsed, overallMoveType);
if ((rollTarget.getValue() != TargetRoll.CHECK_FALSE) && !(entity instanceof FighterSquadron) && !game.useVectorMove()) {
if (!doSkillCheckInSpace(entity, rollTarget)) {
a.setSI(a.getSI() - 1);
// check for destruction
if (a.getSI() == 0) {
destroyEntity(entity, "Structural Integrity Collapse", false);
}
}
}
// check for pilot damage
int hits = entity.getCrew().getHits();
int health = 6 - hits;
if ((thrustUsed > (2 * health)) && !game.useVectorMove() && !(entity instanceof TeleMissile)) {
int targetroll = 2 + (thrustUsed - 2 * health) + 2 * hits;
resistGForce(entity, targetroll);
}
thrustUsed = 0;
}
rollTarget = a.checkRolls(step, overallMoveType);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
game.addControlRoll(new PilotingRollData(a.getId(), 0, "excess roll"));
}
rollTarget = a.checkManeuver(step, overallMoveType);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
if (!doSkillCheckManeuver(entity, rollTarget)) {
a.setFailedManeuver(true);
int forward = Math.max(step.getVelocityLeft() / 2, 1);
if (forward < step.getVelocityLeft()) {
fellDuringMovement = true;
}
while (forward > 0) {
curPos = curPos.translated(step.getFacing());
forward--;
distance++;
// make sure it didn't fly off the map
if (!game.getBoard().contains(curPos)) {
r = new Report(9370, Report.PUBLIC);
r.indent();
r.addDesc(entity);
addReport(r);
game.removeEntity(entity.getId(), IEntityRemovalConditions.REMOVE_PUSHED);
send(createRemoveEntityPacket(entity.getId(), IEntityRemovalConditions.REMOVE_PUSHED));
forward = 0;
fellDuringMovement = false;
return;
// make sure it didn't crash
} else if (game.getBoard().getHex(curPos).ceiling() >= step.getElevation()) {
addReport(processCrash(entity, step.getVelocity()));
forward = 0;
fellDuringMovement = false;
}
}
break;
}
}
// if out of control, check for possible collision
if (didMove && a.isOutControlTotal()) {
Enumeration<Entity> targets = game.getEntities(step.getPosition());
if (targets.hasMoreElements()) {
// Somebody here so check to see if there is a collision
int checkroll = Compute.d6(2);
// TODO: change this to 11 for Large Craft
int targetroll = 11;
if ((a instanceof Dropship) || (entity instanceof Jumpship)) {
targetroll = 10;
}
if (checkroll >= targetroll) {
// this gets complicated, I need to check for each
// unit type
// by order of movement subphase
Vector<Integer> potentialSpaceStation;
Vector<Integer> potentialWarship;
Vector<Integer> potentialJumpship;
Vector<Integer> potentialDropship;
Vector<Integer> potentialSmallCraft;
Vector<Integer> potentialASF;
potentialSpaceStation = new Vector<Integer>();
potentialWarship = new Vector<Integer>();
potentialJumpship = new Vector<Integer>();
potentialDropship = new Vector<Integer>();
potentialSmallCraft = new Vector<Integer>();
potentialASF = new Vector<Integer>();
while (targets.hasMoreElements()) {
int id = targets.nextElement().getId();
Entity ce = game.getEntity(id);
if (ce instanceof SpaceStation) {
potentialSpaceStation.addElement(id);
} else if (ce instanceof Warship) {
potentialWarship.addElement(id);
} else if (ce instanceof Jumpship) {
potentialJumpship.addElement(id);
} else if (ce instanceof Dropship) {
potentialDropship.addElement(id);
} else if (ce instanceof SmallCraft) {
potentialSmallCraft.addElement(id);
} else {
potentialASF.addElement(id);
}
}
// ok now go through and see if these have anybody
// in them
if (potentialSpaceStation.size() > 0) {
int chosen = Compute.randomInt(potentialSpaceStation.size());
Entity target = game.getEntity(potentialSpaceStation.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
} else if (potentialWarship.size() > 0) {
int chosen = Compute.randomInt(potentialWarship.size());
Entity target = game.getEntity(potentialWarship.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
} else if (potentialJumpship.size() > 0) {
int chosen = Compute.randomInt(potentialJumpship.size());
Entity target = game.getEntity(potentialJumpship.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
} else if (potentialDropship.size() > 0) {
int chosen = Compute.randomInt(potentialDropship.size());
Entity target = game.getEntity(potentialDropship.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
} else if (potentialSmallCraft.size() > 0) {
int chosen = Compute.randomInt(potentialSmallCraft.size());
Entity target = game.getEntity(potentialSmallCraft.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
} else if (potentialASF.size() > 0) {
int chosen = Compute.randomInt(potentialASF.size());
Entity target = game.getEntity(potentialASF.elementAt(chosen));
Coords dest = target.getPosition();
if (processCollision(entity, target, lastPos)) {
curPos = dest;
break;
}
}
}
}
}
// if in the atmosphere, check for a potential crash
if (game.getBoard().inAtmosphere() && (game.getBoard().getHex(step.getPosition()).ceiling() >= step.getElevation())) {
addReport(processCrash(entity, md.getFinalVelocity()));
// don't do the rest
break;
}
// handle fighter launching
if (step.getType() == MovePath.STEP_LAUNCH) {
TreeMap<Integer, Vector<Integer>> launched = step.getLaunched();
Set<Integer> bays = launched.keySet();
Iterator<Integer> bayIter = bays.iterator();
Bay currentBay;
while(bayIter.hasNext()) {
int bayId = bayIter.next();
currentBay = entity.getFighterBays().elementAt(bayId);
Vector<Integer> launches = launched.get(bayId);
int nLaunched = launches.size();
//need to make some decisions about how to handle the distribution
//of fighters to doors beyond the launch rate. The most sensible thing
//is probably to distribut them evenly.
int doors = currentBay.getDoors();
int[] distribution = new int[doors];
for(int l = 0; l < nLaunched; l++) {
distribution[l % doors] = distribution[l % doors] + 1;
}
//ok, now lets launch them
r = new Report(9380);
r.add(entity.getDisplayName());
r.subject = entity.getId();
r.newlines = 0;
r.add(nLaunched);
addReport(r);
int currentDoor = 0;
int fighterCount = 0;
boolean doorDamage = false;
for(int fighterId : launches) {
//check to see if we are in the same door
fighterCount++;
if(fighterCount > distribution[currentDoor]) {
//move to a new door
currentDoor++;
fighterCount = 0;
doorDamage = false;
}
int bonus = Math.max(0, distribution[currentDoor] - 2);
//check for door damage
if(!doorDamage && (distribution[currentDoor] > 2) && (Compute.d6(2) == 2)) {
doorDamage = true;
r = new Report(9390);
r.subject = entity.getId();
r.indent(1);
r.newlines = 0;
r.add(currentBay.getType());
addReport(r);
currentBay.destroyDoorNext();
}
Entity fighter = game.getEntity(fighterId);
if (!launchUnit(entity, fighter, curPos, curFacing, step.getVelocity(), step.getElevation(), step.getVectors(), bonus)) {
System.err.println("Error! Server was told to unload " + fighter.getDisplayName() + " from " + entity.getDisplayName() + " into " + curPos.getBoardNum());
}
}
}
// now apply any damage to bay doors
entity.resetBayDoors();
}
if (step.getType() == MovePath.STEP_OFF) {
//same as flee but different message.
//we can't use flee because if the unit is out of control, it needs to go through its
//to see if it collides with anything else
r = new Report(9370, Report.PUBLIC);
r.indent();
r.addDesc(entity);
addReport(r);
Coords pos = entity.getPosition();
int fleeDirection;
if (pos.x == 0) {
fleeDirection = IOffBoardDirections.WEST;
} else if (pos.y == 0) {
fleeDirection = IOffBoardDirections.SOUTH;
} else if (pos.x == game.getBoard().getWidth()) {
fleeDirection = IOffBoardDirections.EAST;
} else {
fleeDirection = IOffBoardDirections.NORTH;
}
// Is the unit carrying passengers?
final Vector<Entity> passengers = entity.getLoadedUnits();
if (!passengers.isEmpty()) {
for (Entity passenger : passengers) {
// Unit has fled the battlefield.
r = new Report(2010, Report.PUBLIC);
r.indent();
r.addDesc(passenger);
addReport(r);
passenger.setRetreatedDirection(fleeDirection);
game.removeEntity(passenger.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT);
send(createRemoveEntityPacket(passenger.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT));
}
}
// Handle any picked up MechWarriors
for (Integer mechWarriorId : entity.getPickedUpMechWarriors()) {
Entity mw = game.getEntity(mechWarriorId.intValue());
// Is the MechWarrior an enemy?
int condition = IEntityRemovalConditions.REMOVE_IN_RETREAT;
r = new Report(2010);
if (mw.isCaptured()) {
r = new Report(2015);
condition = IEntityRemovalConditions.REMOVE_CAPTURED;
} else {
mw.setRetreatedDirection(fleeDirection);
}
game.removeEntity(mw.getId(), condition);
send(createRemoveEntityPacket(mw.getId(), condition));
r.addDesc(mw);
r.indent();
addReport(r);
}
// Is the unit being swarmed?
final int swarmerId = entity.getSwarmAttackerId();
if (Entity.NONE != swarmerId) {
final Entity swarmer = game.getEntity(swarmerId);
// Has the swarmer taken a turn?
if (!swarmer.isDone()) {
// Dead entities don't take turns.
game.removeTurnFor(swarmer);
send(createTurnVectorPacket());
} // End swarmer-still-to-move
// Unit has fled the battlefield.
swarmer.setSwarmTargetId(Entity.NONE);
entity.setSwarmAttackerId(Entity.NONE);
r = new Report(2015, Report.PUBLIC);
r.indent();
r.addDesc(swarmer);
addReport(r);
game.removeEntity(swarmerId, IEntityRemovalConditions.REMOVE_CAPTURED);
send(createRemoveEntityPacket(swarmerId, IEntityRemovalConditions.REMOVE_CAPTURED));
}
entity.setRetreatedDirection(fleeDirection);
game.removeEntity(entity.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT);
send(createRemoveEntityPacket(entity.getId(), IEntityRemovalConditions.REMOVE_IN_RETREAT));
return;
}
}
// check piloting skill for getting up
rollTarget = entity.checkGetUp(step);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
entity.heatBuildup += 1;
entity.setProne(false);
//entity.setHullDown(false);
wasProne = false;
game.resetPSRs(entity);
entityFellWhileAttemptingToStand = !doSkillCheckInPlace(entity, rollTarget);
}
// did the entity just fall?
if (entityFellWhileAttemptingToStand) {
moveType = step.getMovementType();
curFacing = entity.getFacing();
curPos = entity.getPosition();
mpUsed = step.getMpUsed();
fellDuringMovement = true;
if ( !entity.isCarefulStand() ) {
break;
}
} else {
entity.setHullDown(false);
}
if (step.getType() == MovePath.STEP_UNJAM_RAC) {
entity.setUnjammingRAC(true);
game.addAction(new UnjamAction(entity.getId()));
// for Aeros this will end movement prematurely
// if we break
if (!(entity instanceof Aero)) {
break;
}
}
if (step.getType() == MovePath.STEP_LAY_MINE) {
layMine(entity, step.getMineToLay(), step.getPosition());
break;
}
if (step.getType() == MovePath.STEP_CLEAR_MINEFIELD) {
ClearMinefieldAction cma = new ClearMinefieldAction(entity.getId(), step.getMinefield());
entity.setClearingMinefield(true);
game.addAction(cma);
break;
}
if ((step.getType() == MovePath.STEP_SEARCHLIGHT) && entity.hasSpotlight()) {
final boolean SearchOn = !entity.isUsingSpotlight();
entity.setSpotlightState(SearchOn);
sendServerChat(entity.getDisplayName() + " switched searchlight " + (SearchOn ? "on" : "off") + '.');
}
// set most step parameters
moveType = step.getMovementType();
distance = step.getDistance();
mpUsed = step.getMpUsed();
if (cachedGravityLimit < 0) {
cachedGravityLimit = IEntityMovementType.MOVE_JUMP == moveType ? entity.getJumpMP(false) : entity.getRunMP(false, false);
}
// check for charge
if (step.getType() == MovePath.STEP_CHARGE) {
if (entity.canCharge()) {
checkExtremeGravityMovement(entity, step, curPos, cachedGravityLimit);
Targetable target = step.getTarget(game);
ChargeAttackAction caa = new ChargeAttackAction(entity.getId(), target.getTargetType(), target.getTargetId(), target.getPosition());
entity.setDisplacementAttack(caa);
game.addCharge(caa);
charge = caa;
} else {
sendServerChat("Illegal charge!! I don't think " + entity.getDisplayName() + " should be allowed to charge," + " but the client of " + entity.getOwner().getName() + " disagrees.");
sendServerChat("Please make sure " + entity.getOwner().getName() + " is running MegaMek " + MegaMek.VERSION + ", or if that is already the case, submit a bug report at http://megamek.sf.net/");
return;
}
break;
}
// check for dfa
if (step.getType() == MovePath.STEP_DFA) {
if (entity.canDFA()) {
checkExtremeGravityMovement(entity, step, curPos, cachedGravityLimit);
Targetable target = step.getTarget(game);
DfaAttackAction daa = new DfaAttackAction(entity.getId(), target.getTargetType(), target.getTargetId(), target.getPosition());
entity.setDisplacementAttack(daa);
game.addCharge(daa);
charge = daa;
} else {
sendServerChat("Illegal DFA!! I don't think " + entity.getDisplayName() + " should be allowed to DFA," + " but the client of " + entity.getOwner().getName() + " disagrees.");
sendServerChat("Please make sure " + entity.getOwner().getName() + " is running MegaMek " + MegaMek.VERSION + ", or if that is already the case, submit a bug report at http://megamek.sf.net/");
return;
}
break;
}
// check for ram
if (step.getType() == MovePath.STEP_RAM) {
if (entity.canRam()) {
Targetable target = step.getTarget(game);
RamAttackAction raa = new RamAttackAction(entity.getId(), target.getTargetType(), target.getTargetId(), target.getPosition());
entity.setRamming(true);
game.addRam(raa);
ram = raa;
} else {
sendServerChat("Illegal ram!! I don't think " + entity.getDisplayName() + " should be allowed to charge," + " but the client of " + entity.getOwner().getName() + " disagrees.");
sendServerChat("Please make sure " + entity.getOwner().getName() + " is running MegaMek " + MegaMek.VERSION + ", or if that is already the case, submit a bug report at http://megamek.sf.net/");
return;
}
break;
}
if ((step.getType() == MovePath.STEP_ACC) || (step.getType() == MovePath.STEP_ACCN)) {
if (entity instanceof Aero) {
Aero a = (Aero) entity;
if (step.getType() == MovePath.STEP_ACCN) {
a.setAccLast(true);
a.setNextVelocity(a.getNextVelocity() + 1);
} else {
a.setAccDecNow(true);
a.setCurrentVelocity(a.getCurrentVelocity() + 1);
a.setNextVelocity(a.getNextVelocity() + 1);
}
}
}
if ((step.getType() == MovePath.STEP_DEC) || (step.getType() == MovePath.STEP_DECN)) {
if (entity instanceof Aero) {
Aero a = (Aero) entity;
if (step.getType() == MovePath.STEP_DECN) {
a.setAccLast(true);
a.setNextVelocity(a.getNextVelocity() - 1);
} else {
a.setAccDecNow(true);
a.setCurrentVelocity(a.getCurrentVelocity() - 1);
a.setNextVelocity(a.getNextVelocity() - 1);
}
}
}
if (step.getType() == MovePath.STEP_EVADE) {
entity.setEvading(true);
}
if (step.getType() == MovePath.STEP_STALL) {
// TODO: check VSTOL status
r = new Report(9391);
r.subject = entity.getId();
r.addDesc(entity);
r.newlines = 0;
addReport(r);
game.addControlRoll(new PilotingRollData(entity.getId(), 0, "stalled out"));
// check for crash
if (game.getBoard().getHex(step.getPosition()).ceiling() >= step.getElevation()) {
addReport(processCrash(entity, 0));
// don't do the rest
break;
}
}
if (step.getType() == MovePath.STEP_ROLL) {
if (entity instanceof Aero) {
Aero a = (Aero) entity;
if (a.isRolled()) {
a.setRolled(false);
} else {
a.setRolled(true);
}
}
}
// check for dig in or fortify
if (entity instanceof Infantry) {
Infantry inf = (Infantry) entity;
if (step.getType() == MovePath.STEP_DIG_IN) {
inf.setDugIn(Infantry.DUG_IN_WORKING);
continue;
} else if (step.getType() == MovePath.STEP_FORTIFY) {
if (!entity.hasWorkingMisc(MiscType.F_TOOLS, MiscType.S_VIBROSHOVEL)) {
sendServerChat(entity.getDisplayName() + " failed to fortify because it is missing suitable equipment");
}
inf.setDugIn(Infantry.DUG_IN_FORTIFYING1);
continue;
} else if ((step.getType() != MovePath.STEP_TURN_LEFT) && (step.getType() != MovePath.STEP_TURN_RIGHT)) {
// other movement clears dug in status
inf.setDugIn(Infantry.DUG_IN_NONE);
}
}
// set last step parameters
curPos = step.getPosition();
if ((moveType != IEntityMovementType.MOVE_JUMP) || (entity.getJumpType() != Mech.JUMP_BOOSTER)) {
curFacing = step.getFacing();
}
// check if a building PSR will be needed later, before setting the
// new elevation
int buildingMove = entity.checkMovementInBuilding(step, prevStep, curPos, lastPos);
curVTOLElevation = step.getElevation();
curElevation = step.getElevation();
// set elevation in case of collapses
entity.setElevation(step.getElevation());
IHex curHex = game.getBoard().getHex(curPos);
// check for automatic unstick
if (entity.canUnstickByJumping() && entity.isStuck() && (moveType == IEntityMovementType.MOVE_JUMP)) {
entity.setStuck(false);
entity.setCanUnstickByJumping(false);
}
// Check for skid.
rollTarget = entity.checkSkid(moveType, prevHex, overallMoveType, prevStep, prevFacing, curFacing, lastPos, curPos, isInfantry, distance-1);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
// Have an entity-meaningful PSR message.
boolean psrFailed = true;
if (entity instanceof Mech) {
psrFailed = (0 < doSkillCheckWhileMoving(entity, lastPos, lastPos, rollTarget, true));
} else {
psrFailed = (0 < doSkillCheckWhileMoving(entity, lastPos, lastPos, rollTarget, false));
}
// Does the entity skid?
if (psrFailed) {
if (entity instanceof Tank) {
addReport(vehicleMotiveDamage((Tank) entity, 0));
}
curPos = lastPos;
int skidDistance = (int)Math.round((double)(distance-1)/2);
int skidDirection = prevFacing;
// All charge damage is based upon
// the pre-skid move distance.
entity.delta_distance = distance - 1;
// Attacks against a skidding target have additional +2.
moveType = IEntityMovementType.MOVE_SKID;
// What is the first hex in the skid?
if (step.isThisStepBackwards()) {
skidDirection = (skidDirection + 3) % 6;
}
if (processSkid(entity, curPos, prevStep.getElevation(), skidDirection, skidDistance, prevStep)) {
return;
}
// set entity parameters
curFacing = entity.getFacing();
curPos = entity.getPosition();
entity.setSecondaryFacing(curFacing);
// skid consumes all movement
if (md.hasActiveMASC()) {
mpUsed = entity.getRunMP();
} else {
mpUsed = entity.getRunMPwithoutMASC();
}
entity.moved = moveType;
fellDuringMovement = true;
turnOver = true;
distance = entity.delta_distance;
break;
} // End failed-skid-psr
} // End need-skid-psr
// check sideslip
if ((entity instanceof VTOL) || (entity.getMovementMode() == IEntityMovementMode.HOVER) || (entity.getMovementMode() == IEntityMovementMode.WIGE)) {
rollTarget = entity.checkSideSlip(moveType, prevHex, overallMoveType, prevStep, prevFacing, curFacing, lastPos, curPos, distance);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
int MoF = doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, false);
if (MoF > 0) {
// maximum distance is hexes moved / 2
int sideslipDistance = Math.min(MoF, distance - 1);
if (sideslipDistance > 0) {
int skidDirection = prevFacing;
// report sideslip
sideslipped = true;
r = new Report(2100);
r.subject = entity.getId();
r.addDesc(entity);
r.add(sideslipDistance);
addReport(r);
if (processSkid(entity, lastPos, prevStep.getElevation(), skidDirection, sideslipDistance, prevStep)) {
return;
}
if (!entity.isDestroyed() && !entity.isDoomed() && (mpUsed < entity.getRunMP())) {
fellDuringMovement = true; // No, but it should
// work...
}
if ((entity.getElevation() == 0) && ((entity.getMovementMode() == IEntityMovementMode.VTOL) || (entity.getMovementMode() == IEntityMovementMode.WIGE))) {
turnOver = true;
}
// set entity parameters
curFacing = step.getFacing();
curPos = entity.getPosition();
entity.setSecondaryFacing(curFacing);
break;
}
}
}
}
// check if we've moved into rubble
rollTarget = entity.checkRubbleMove(step, curHex, lastPos, curPos);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, true);
}
//check if we are using reckless movement
rollTarget = entity.checkRecklessMove(step, curHex, lastPos, curPos, prevHex);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
if(entity instanceof Mech) {
doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, true);
} else if (entity instanceof Tank) {
if(0 < doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, false)) {
//assume VTOLs in flight are always in clear terrain
if((0 == curHex.terrainsPresent()) || (step.getElevation() > 0)) {
r = new Report(2206);
r.addDesc(entity);
r.subject = entity.getId();
addReport(r);
mpUsed = step.getMpUsed() + 1;
fellDuringMovement = true;
break;
} else {
r = new Report(2207);
r.addDesc(entity);
r.subject = entity.getId();
addReport(r);
//until we get a rules clarification assume that the entity is both giver and taker
//for charge damage
HitData hit = entity.rollHitLocation(ToHitData.HIT_NORMAL, ToHitData.SIDE_FRONT);
addReport(damageEntity(entity, hit, ChargeAttackAction.getDamageTakenBy(entity, entity)));
turnOver = true;
break;
}
}
}
}
// check for breaking magma crust
if ((curHex.terrainLevel(Terrains.MAGMA) == 1) && (step.getElevation() == 0) && (step.getMovementType() != IEntityMovementType.MOVE_JUMP)) {
int roll = Compute.d6(1);
r = new Report(2395);
r.addDesc(entity);
r.add(roll);
r.subject = entity.getId();
addReport(r);
if (roll == 6) {
curHex.removeTerrain(Terrains.MAGMA);
curHex.addTerrain(Terrains.getTerrainFactory().createTerrain(Terrains.MAGMA, 2));
sendChangedHex(curPos);
for (Enumeration<Entity> e = game.getEntities(curPos); e.hasMoreElements();) {
Entity en = e.nextElement();
if (en != entity) {
doMagmaDamage(en, false);
}
}
}
}
// check for entering liquid magma
if ((curHex.terrainLevel(Terrains.MAGMA) == 2) && (step.getElevation() == 0) && (step.getMovementType() != IEntityMovementType.MOVE_JUMP)) {
doMagmaDamage(entity, false);
}
// check if we've moved into a swamp
rollTarget = entity.checkBogDown(step, curHex, lastPos, curPos, lastElevation, isPavementStep);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
if (0 < doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, false)) {
entity.setStuck(true);
entity.setCanUnstickByJumping(true);
r = new Report(2081);
r.add(entity.getDisplayName());
r.subject = entity.getId();
addReport(r);
//check for quicksand
addReport(checkQuickSand(curPos));
// check for accidental stacking violation
Entity violation = Compute.stackingViolation(game, entity.getId(), curPos);
if (violation != null) {
// target gets displaced, because of low elevation
int direction = lastPos.direction(curPos);
Coords targetDest = Compute.getValidDisplacement(game, entity.getId(), curPos, direction);
addReport(doEntityDisplacement(violation, curPos, targetDest, new PilotingRollData(violation.getId(), 0, "domino effect")));
// Update the violating entity's postion on the client.
entityUpdate(violation.getId());
}
break;
}
}
// check to see if we are a mech and we've moved OUT of fire
IHex lastHex = game.getBoard().getHex(lastPos);
if (entity instanceof Mech) {
if (!lastPos.equals(curPos) && (prevStep != null) && ((lastHex.containsTerrain(Terrains.FIRE) && (prevStep.getElevation() <= 1)) || (lastHex.containsTerrain(Terrains.MAGMA) && (prevStep.getElevation() == 0))) && ((step.getMovementType() != IEntityMovementType.MOVE_JUMP)
// Bug #828741 -- jumping bypasses fire, but not on the
// first step
// getMpUsed -- total MP used to this step
// getMp -- MP used in this step
// the difference will always be 0 on the "first step"
// of a jump,
// and >0 on a step in the midst of a jump
|| (0 == step.getMpUsed() - step.getMp()))) {
int heat = 0;
if (lastHex.containsTerrain(Terrains.FIRE)) {
heat += 2;
}
if (lastHex.terrainLevel(Terrains.MAGMA) == 1) {
heat += 2;
} else if (lastHex.terrainLevel(Terrains.MAGMA) == 2) {
heat += 5;
}
entity.heatFromExternal += heat;
r = new Report(2115);
r.subject = entity.getId();
r.addDesc(entity);
r.add(heat);
addReport(r);
}
}
// check to see if we are not a mech and we've moved INTO fire
if (!(entity instanceof Mech)) {
if (game.getBoard().getHex(curPos).containsTerrain(Terrains.FIRE) && !lastPos.equals(curPos) && (step.getMovementType() != IEntityMovementType.MOVE_JUMP) && (step.getElevation() <= 1)) {
doFlamingDamage(entity);
}
}
// check for extreme gravity movement
if (!i.hasMoreElements() && !firstStep) {
checkExtremeGravityMovement(entity, step, curPos, cachedGravityLimit);
}
// check for minefields. have to check both new hex and new elevation
// VTOLs may land and submarines may rise or lower into a minefield
if (!lastPos.equals(curPos) || (lastElevation != curElevation)) {
boolean boom = false;
boolean isOnGround = !i.hasMoreElements();
isOnGround |= step.getMovementType() != IEntityMovementType.MOVE_JUMP;
isOnGround &= step.getElevation() < 1;
if(isOnGround) {
boom = checkVibrabombs(entity, curPos, false, lastPos, curPos, vPhaseReport);
}
if (game.containsMinefield(curPos)) {
// set the new position temporarily, because
// infantry otherwise would get double damage
// when moving from clear into mined woods
entity.setPosition(curPos);
if(enterMinefield(entity, curPos, step.getElevation(), isOnGround, vPhaseReport)) {
//resolve any piloting rolls from damage unless unit was jumping
if(step.getMovementType() != IEntityMovementType.MOVE_JUMP) {
addReport(resolvePilotingRolls(entity));
game.resetPSRs(entity);
}
boom = true;
}
if(wasProne || !entity.isProne()) {
entity.setPosition(lastPos);
}
}
//did anything go boom?
if(boom) {
//set fell during movement so that entity will get another chance to move with any motive damage
//taken account of (functions the same as MASC failure)
//only do this if they had more steps (and they were not jumping
if(i.hasMoreElements() && (step.getMovementType() != IEntityMovementType.MOVE_JUMP)) {
md.clear();
fellDuringMovement = true;
}
//reset mines if anything detonated
resetMines();
}
}
// infantry discovers minefields if they end their move
// in a minefield.
if (!lastPos.equals(curPos) && !i.hasMoreElements() && isInfantry) {
if (game.containsMinefield(curPos)) {
Player owner = entity.getOwner();
for (Minefield mf : game.getMinefields(curPos)) {
if (!owner.containsMinefield(mf)) {
r = new Report(2120);
r.subject = entity.getId();
r.add(entity.getShortName(), true);
addReport(r);
revealMinefield(game.getTeamForPlayer(owner), mf);
}
}
}
}
// check if we've moved into water
rollTarget = entity.checkWaterMove(step, curHex, lastPos, curPos, isPavementStep);
if (rollTarget.getValue() != TargetRoll.CHECK_FALSE) {
// Swarmers need special handling.
final int swarmerId = entity.getSwarmAttackerId();
boolean swarmerDone = true;
Entity swarmer = null;
if (Entity.NONE != swarmerId) {
swarmer = game.getEntity(swarmerId);
swarmerDone = swarmer.isDone();
}
// Now do the skill check.
doSkillCheckWhileMoving(entity, lastPos, curPos, rollTarget, true);
// Swarming infantry platoons may drown.
if (curHex.terrainLevel(Terrains.WATER) > 1) {
drownSwarmer(entity, curPos);
}
// Do we need to remove a game turn for the swarmer
if (!swarmerDone && (swarmer != null) && (swarmer.isDoomed() || swarmer.isDestroyed())) {
// We have to diddle with the swarmer's
// status to get its turn removed.
swarmer.setDone(false);
swarmer.setUnloaded(false);
// Dead entities don't take turns.
game.removeTurnFor(swarmer);
send(createTurnVectorPacket());
// Return the original status.
swarmer.setDone(true);
swarmer.setUnloaded(true);
}
// check for inferno wash-off
checkForWashedInfernos(entity, curPos);
}
// In water, may or may not be a new hex, neccessary to
// check during movement, for breach damage, and always
// set dry if appropriate
// TODO: possibly make the locations local and set later
addReport(doSetLocationsExposure(entity, curHex, step.getMovementType() == IEntityMovementType.MOVE_JUMP, step.getElevation()));
// check for breaking ice by breaking through from below
if ((lastElevation < 0) && (step.getElevation() == 0)
&& lastHex.containsTerrain(Terrains.ICE) && lastHex.containsTerrain(Terrains.WATER)
&& (step.getMovementType() != IEntityMovementType.MOVE_JUMP) && !lastPos.equals(curPos)) {
//need to temporarily reset entity's position so it doesn't fall in the ice
entity.setPosition(curPos);
r = new Report(2410);
r.addDesc(entity);
addReport(r);
addReport(resolveIceBroken(lastPos));
//ok now set back
entity.setPosition(lastPos);
}
// check for breaking ice by stepping on it
if (curHex.containsTerrain(Terrains.ICE) && curHex.containsTerrain(Terrains.WATER)
&& (step.getMovementType() != IEntityMovementType.MOVE_JUMP)
&& !lastPos.equals(curPos)
&& !(entity instanceof Infantry)
&& !(step.isPavementStep() && curHex.containsTerrain(Terrains.BRIDGE))) {
if (step.getElevation() == 0) {
int roll = Compute.d6(1);
r = new Report(2118);
r.addDesc(entity);
r.add(roll);
r.subject = entity.getId();
addReport(r);
if (roll == 6) {
entity.setPosition(curPos);
addReport(resolveIceBroken(curPos));
curPos = entity.getPosition();
}
}
// or intersecting it
else if (step.getElevation() + entity.height() == 0) {
r = new Report(2410);
r.addDesc(entity);
addReport(r);
addReport(resolveIceBroken(curPos));
}
}
// Handle loading units.
if (step.getType() == MovePath.STEP_LOAD) {
// Find the unit being loaded.
Entity loaded = null;
Enumeration<Entity> entities = game.getEntities(curPos);
while (entities.hasMoreElements()) {
// Is the other unit friendly and not the current entity?
loaded = entities.nextElement();
if (!entity.isEnemyOf(loaded) && !entity.equals(loaded)) {
// The moving unit should be able to load the other
// unit and the other should be able to have a turn.
if (!entity.canLoad(loaded) || !loaded.isLoadableThisTurn()) {
// Something is fishy in Denmark.
System.err.println(entity.getShortName() + " can not load " + loaded.getShortName());
loaded = null;
} else {
// Have the deployed unit load the indicated unit.
loadUnit(entity, loaded);
// Stop looking.
break;
}
} else {
// Nope. Discard it.
loaded = null;
}
} // Handle the next entity in this hex.
// We were supposed to find someone to load.
if (loaded == null) {
System.err.println("Could not find unit for " + entity.getShortName() + " to load in " + curPos);
}
} // End STEP_LOAD
// handle fighter recovery
if (step.getType() == MovePath.STEP_RECOVER) {
loader = game.getEntity(step.getRecoveryUnit());
PilotingRollData psr = entity.getBasePilotingRoll(overallMoveType);
if (loader.mpUsed > 0) {
psr.addModifier(5, "carrier used thrust");
}
int ctrlroll = Compute.d6(2);
r = new Report(9381);
r.subject = entity.getId();
r.add(entity.getDisplayName());
r.add(loader.getDisplayName());
r.add(psr.getValue());
r.add(ctrlroll);
r.newlines = 0;
r.indent(1);
if (ctrlroll < psr.getValue()) {
r.choose(false);
addReport(r);
// damage unit
Aero a = (Aero) entity;
HitData hit = a.rollHitLocation(ToHitData.HIT_NORMAL, ToHitData.SIDE_FRONT);
addReport(damageEntity(entity, hit, 2 * (psr.getValue() - ctrlroll)));
} else {
r.choose(true);
addReport(r);
recovered = true;
}
// check for door damage
if (ctrlroll == 2) {
loader.damageDoorRecovery(entity);
r = new Report(9384);
r.subject = entity.getId();
r.indent(0);
r.add(loader.getDisplayName());
addReport(r);
}
}
//handle fighter squadron joining
if (step.getType() == MovePath.STEP_JOIN) {
loader = game.getEntity(step.getRecoveryUnit());
recovered = true;
}
// Handle unloading units.
if (step.getType() == MovePath.STEP_UNLOAD) {
Targetable unloaded = step.getTarget(game);
if (!unloadUnit(entity, unloaded, curPos, curFacing, step.getElevation())) {
System.err.println("Error! Server was told to unload " + unloaded.getDisplayName() + " from " + entity.getDisplayName() + " into " + curPos.getBoardNum());
}
}
if (((step.getType() == MovePath.STEP_BACKWARDS) || (step.getType() == MovePath.STEP_LATERAL_LEFT_BACKWARDS) || (step.getType() == MovePath.STEP_LATERAL_RIGHT_BACKWARDS)) && (game.getBoard().getHex(lastPos).getElevation() != curHex.getElevation()) && !(entity instanceof VTOL)) {
PilotingRollData psr = entity.getBasePilotingRoll(overallMoveType);
int roll = Compute.d6(2);
if (entity instanceof Tank) {
r = new Report(2435);
} else {
r = new Report(2430);
}
r.subject = entity.getId();
r.addDesc(entity);
r.add(psr.getValue());
r.add(roll);
addReport(r);
if (roll < psr.getValue()) {
if (entity instanceof Mech) {
if (curHex.getElevation() < game.getBoard().getHex(lastPos).getElevation()) {
entity.setElevation(Integer.MIN_VALUE);
addReport(doEntityFallsInto(entity, lastPos, curPos, entity.getBasePilotingRoll(overallMoveType), false));
} else {
// Tac Ops p.22
// the change in levels is not taken into
// consideration for determining the levels fallen.
entity.setElevation(Integer.MIN_VALUE);
addReport(doEntityFallsInto(entity, curPos, lastPos, entity.getBasePilotingRoll(overallMoveType), false));
}
} else if (entity instanceof Tank) {
curPos = lastPos;
}
}
}
// Handle non-infantry moving into a building.
if (buildingMove > 0) {
// Get the building being exited.
Building bldgExited = null;
if ((buildingMove & 1) == 1) {
bldgExited = game.getBoard().getBuildingAt(lastPos);
}
// Get the building being entered.
Building bldgEntered = null;
if ((buildingMove & 2) == 2) {
bldgEntered = game.getBoard().getBuildingAt(curPos);
}
// Get the building being stepped on.
Building bldgStepped = null;
if ((buildingMove & 4) == 4) {
bldgStepped = game.getBoard().getBuildingAt(curPos);
}
boolean collapsed = false;
// are we passing through a building wall?
if ((bldgEntered != null)) {
// If we're not leaving a building, just handle the
// "entered".
String reason;
if (bldgExited == null) {
reason = "entering";
}
// If we're moving within the same building, just handle
// the "within".
else if (bldgExited.equals(bldgEntered) && !(entity instanceof Protomech) && !(entity instanceof Infantry)) {
reason = "moving in";
}
// If we have different buildings, roll for each.
else {
reason = "entering";
}
collapsed = passBuildingWall(entity, bldgEntered, lastPos, curPos, distance, reason, step.isThisStepBackwards(), step.getParent().getLastStepMovementType());
addAffectedBldg(bldgEntered, collapsed);
}
// stepping on roof, no PSR just check for over weight
if (bldgStepped != null) {
collapsed = checkBuildingCollapseWhileMoving(bldgStepped, entity, curPos);
addAffectedBldg(bldgStepped, collapsed);
}
// Clean up the entity if it has been destroyed.
if (entity.isDoomed()) {
entity.setDestroyed(true);
game.moveToGraveyard(entity.getId());
send(createRemoveEntityPacket(entity.getId()));
// The entity's movement is completed.
return;
}
// TODO: what if a building collapses into rubble?
}
// did the entity just fall?
if (!wasProne && entity.isProne()) {
curFacing = entity.getFacing();
curPos = entity.getPosition();
mpUsed = step.getMpUsed();
fellDuringMovement = true;
break;
}
// dropping prone intentionally?
if (step.getType() == MovePath.STEP_GO_PRONE) {
mpUsed = step.getMpUsed();
rollTarget = entity.checkDislodgeSwarmers(step);
if (rollTarget.getValue() == TargetRoll.CHECK_FALSE) {
// Not being swarmed
entity.setProne(true);
// check to see if we washed off infernos
checkForWashedInfernos(entity, curPos);
} else {
// Being swarmed
entity.setPosition(curPos);
if (doDislodgeSwarmerSkillCheck(entity, rollTarget, curPos)) {
// Entity falls
curFacing = entity.getFacing();
curPos = entity.getPosition();
fellDuringMovement = true;
break;
}
// roll failed, go prone but don't dislodge swarmers
entity.setProne(true);
// check to see if we washed off infernos
checkForWashedInfernos(entity, curPos);
break;
}
}
// going hull down
if (step.getType() == MovePath.STEP_HULL_DOWN) {
mpUsed = step.getMpUsed();
entity.setHullDown(true);
}
// Track this step's location.
movePath.addElement(new UnitLocation(entity.getId(), curPos, curFacing, step.getElevation()));
// if the lastpos is not the same as the current position
// then add the current position to the list of places passed
// through
if (!curPos.equals(lastPos)) {