doEntityDisplacement(entity, src, targetDest, new PilotingRollData(entity.getId(), PilotingRollData.AUTOMATIC_FALL, "pushed off a cliff"));
doEntityDisplacement(entity, src, targetDest, new PilotingRollData(entity.getId(), PilotingRollData.IMPOSSIBLE, "pushed off a cliff"));
    private void doEntityDisplacement(Entity entity, Coords src, Coords dest,                                      PilotingRollData roll) {        final Hex srcHex = game.board.getHex(src);        final Hex destHex = game.board.getHex(dest);        final int direction = src.direction(dest);        int fallElevation = srcHex.getElevation() - destHex.getElevation();        Entity target = game.getEntity(dest);        // can't fall upwards        if (fallElevation < 0) {            fallElevation = 0;        }        // if destination is empty, this could be easy...        if (target == null || target.equals(entity)) {            if (fallElevation < 2) {                // no cliff: move and roll normally                phaseReport.append(entity.getDisplayName()                           + " is displaced into hex "                           + dest.getBoardNum() + ".\n");                entity.setPosition(dest);                if (roll != null) {                    pilotRolls.addElement(roll);                }                return;            } else {                // cliff: fall off it, deal damage, prone immediately                phaseReport.append(entity.getDisplayName() + " falls "                           + fallElevation + " levels into hex "                           + dest.getBoardNum() + ".\n");                doEntityFall(entity, dest, fallElevation, PilotingRollData.AUTOMATIC_FALL);                return;            }        }        // okay, destination occupied.  hmmm...        System.err.println("server.doEntityDisplacement: destination occupied");        if (fallElevation < 2) {            // domino effect: move & displace target            phaseReport.append(entity.getDisplayName()                           + " is displaced into hex "                           + dest.getBoardNum() + ", occupied by "                           + target.getDisplayName() + ".\n");            entity.setPosition(dest);            if (roll != null) {                pilotRolls.addElement(roll);            }            doEntityDisplacement(target, dest, dest.translated(direction), new PilotingRollData(target.getId(), 0, "domino effect"));            return;        } else {            // accidental fall from above: havoc!            phaseReport.append(entity.getDisplayName() + " falls "                           + fallElevation + " levels into hex "                           + dest.getBoardNum() + ", occupied by "                           + target.getDisplayName() + ".\n");            // determine to-hit number            ToHitData toHit = new ToHitData(7, "7 (base)");            toHit.append(Compute.getTargetMovementModifier(game, target.getId()));            toHit.append(Compute.getTargetTerrainModifier(game, target.getId()));            // roll dice            final int diceRoll = Compute.d6(2);            phaseReport.append("Collision occurs on a " + toHit.getValue()                           + " or greater.  Rolls " + diceRoll);            if (diceRoll >= toHit.getValue()) {                phaseReport.append(", hits!\n");                // deal damage to target                int damage = (int)Math.ceil(entity.getWeight() / 10);                phaseReport.append(target.getDisplayName() + " takes "                                   + damage + " from the collision.");                while (damage > 0) {                    int cluster = Math.min(5, damage);                    HitData hit = entity.rollHitLocation(ToHitData.HIT_PUNCH, ToHitData.SIDE_FRONT);                    phaseReport.append(damageEntity(target, hit, cluster));                    damage -= cluster;                }                phaseReport.append("\n");                // attacker falls as normal, on his back                doEntityFall(entity, dest, fallElevation, 3, PilotingRollData.AUTOMATIC_FALL);                // defender pushed away, or destroyed                Coords targetDest = Compute.getValidDisplacement(game, target.getId(), dest, direction);                if (targetDest != null) {                    doEntityDisplacement(target, dest, targetDest, new PilotingRollData(target.getId(), 2, "fallen on"));                } else {                    // ack!  automatic death!                    phaseReport.append("*** " + target.getDisplayName()                                       + " DESTROYED due to impossible displacement! ***");                    target.setDoomed(true);                }            } else {                phaseReport.append(", misses.\n");                //TODO: this is not quite how the rules go                Coords targetDest = Compute.getValidDisplacement(game, entity.getId(), dest, direction);                if (targetDest != null) {                    doEntityDisplacement(entity, src, targetDest, new PilotingRollData(entity.getId(), PilotingRollData.AUTOMATIC_FALL, "pushed off a cliff"));                } else {                    // ack!  automatic death!                    phaseReport.append("*** " + entity.getDisplayName()                                       + " DESTROYED due to impossible displacement! ***");                    entity.setDoomed(true);                }            }            return;        }    }