doEntityDisplacement(target, dest, dest.translated(direction), new PilotingRollData(target.getId(), 0, "domino effect"));
doEntityDisplacement(violation, dest, dest.translated(direction), new PilotingRollData(violation.getId(), 0, "domino effect"));
    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 = entity.elevationOccupied(srcHex) - entity.elevationOccupied(destHex);        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");                // only given a modifier, so flesh out into a full piloting roll                PilotingRollData pilotRoll = Compute.getBasePilotingRoll(game, entity.getId());                if (roll != null) {                    pilotRoll.append(roll);                }                doEntityFall(entity, dest, fallElevation, pilotRoll);                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, "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 = target.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                // only given a modifier, so flesh out into a full piloting roll                PilotingRollData pilotRoll = Compute.getBasePilotingRoll(game, entity.getId());                pilotRoll.append(roll);                doEntityFall(entity, dest, fallElevation, 3, pilotRoll);                                // 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.IMPOSSIBLE, "pushed off a cliff"));                } else {                    // ack!  automatic death!                    phaseReport.append("*** " + entity.getDisplayName()                    + " DESTROYED due to impossible displacement! ***");                    entity.setDoomed(true);                }            }            return;        }    }