* @param type The type of migration occurring.
     * @param random A pseudo-random number source.
     * @param cs A <code>ChangeSet</code> to update.
     */
    public void csEmigrate(int slot, MigrationType type, Random random,
                           ChangeSet cs) {
        // Valid slots are in [1,3], recruitable indices are in [0,2].
        // An invalid slot is normal when the player has no control over
        // recruit type.
        boolean selected = 1 <= slot && slot <= Europe.RECRUIT_COUNT;
        int index = (selected) ? slot-1
            : Utils.randomInt(logger, "Choose emigrant", random,
                              Europe.RECRUIT_COUNT);

        // Create the recruit, move it to the docks.
        Europe europe = getEurope();
        UnitType recruitType = europe.getRecruitable(index);
        Game game = getGame();
        Unit unit = new ServerUnit(game, europe, this, recruitType);
        unit.setLocation(europe);

        // Handle migration type specific changes.
        switch (type) {
        case FOUNTAIN:
            setRemainingEmigrants(getRemainingEmigrants() - 1);
            break;
        case RECRUIT:
            modifyGold(-europe.getRecruitPrice());
            cs.addPartial(See.only(this), this, "gold");
            europe.increaseRecruitmentDifficulty();
            // Fall through
        case NORMAL:
            updateImmigrationRequired();
            reduceImmigration();
            cs.addPartial(See.only(this), this,
                          "immigration", "immigrationRequired");
            break;
        case SURVIVAL:
            break;
        default:
            throw new IllegalArgumentException("Bogus migration type");
        }

        // Replace the recruit we used.  Shuffle them down first
        // as AI is always recruiting slot 0.
        for (int i = index; i < Europe.RECRUIT_COUNT-1; i++) {
            europe.setRecruitable(i, europe.getRecruitable(i+1));
        }
        List<RandomChoice<UnitType>> recruits = generateRecruitablesList();
        europe.setRecruitable(Europe.RECRUIT_COUNT-1,
            RandomChoice.getWeightedRandom(logger,
                "Replace recruit", random, recruits));
        cs.add(See.only(this), europe);

        // Add an informative message if this was a survival recruitment,
        // or an ordinary migration where we did not select the unit type.
        // Other cases were selected.
        if (type == MigrationType.SURVIVAL) {
            cs.addMessage(See.only(this),
                new ModelMessage(ModelMessage.MessageType.UNIT_ADDED,
                                 "model.europe.autoRecruit",
                                 this, unit)
                .add("%europe%", europe.getNameKey())
                .addStringTemplate("%unit%", unit.getLabel()));
        } else if (!selected) {
            cs.addMessage(See.only(this),
                new ModelMessage(ModelMessage.MessageType.UNIT_ADDED,
                                 "model.europe.emigrate",
                                 this, unit)
                    .add("%europe%", europe.getNameKey())
                    .addStringTemplate("%unit%", unit.getLabel()));
        }
    }