boolean wigeStartedLanded = false;
int prevFacing = curFacing;
IHex prevHex = null;
final boolean isInfantry = entity instanceof Infantry;
AttackAction charge = null;
RamAttackAction ram = null;
PilotingRollData rollTarget;
// cache this here, otherwise changing MP in the turn causes
// errorneous gravity PSRs
int cachedGravityLimit = -1;
int thrustUsed = 0;
int j = 0;
boolean didMove = false;
boolean recovered = false;
Entity loader = null;
// get a list of coordinates that the unit passed through this turn
// so that I can later recover potential bombing targets
// it may already have some values
Vector<Coords> passedThrough = entity.getPassedThrough();
passedThrough.add(curPos);
// Compile the move
md.compile(game, entity);
// if advanced movement is being used then set the new vectors based on
// movepath
entity.setVectors(md.getFinalVectors());
overallMoveType = md.getLastStepMovementType();
// check for starting in liquid magma
if ((game.getBoard().getHex(entity.getPosition()).terrainLevel(Terrains.MAGMA) == 2) && (entity.getElevation() == 0)) {
doMagmaDamage(entity, false);
}
// set acceleration used to default
if (entity instanceof Aero) {
Aero a = (Aero) entity;
a.setAccLast(false);
}
// 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.");