de.xaniox.heavyspleef.flag.defaults.FlagTeam.java Source code

Java tutorial

Introduction

Here is the source code for de.xaniox.heavyspleef.flag.defaults.FlagTeam.java

Source

/*
 * This file is part of HeavySpleef.
 * Copyright (c) 2014-2016 Matthias Werning
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package de.xaniox.heavyspleef.flag.defaults;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import de.matzefratze123.inventoryguilib.GuiInventory;
import de.matzefratze123.inventoryguilib.GuiInventorySlot;
import de.xaniox.heavyspleef.core.MetadatableItemStack;
import de.xaniox.heavyspleef.core.event.*;
import de.xaniox.heavyspleef.core.flag.*;
import de.xaniox.heavyspleef.core.game.*;
import de.xaniox.heavyspleef.core.game.Game.JoinResult;
import de.xaniox.heavyspleef.core.i18n.Messages;
import de.xaniox.heavyspleef.core.player.SpleefPlayer;
import de.xaniox.heavyspleef.core.stats.Statistic;
import de.xaniox.heavyspleef.core.stats.StatisticRecorder;
import de.xaniox.heavyspleef.flag.presets.EnumListFlag;
import de.xaniox.heavyspleef.flag.presets.LocationFlag;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.MaterialData;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import org.dom4j.Element;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

@Flag(name = "team", conflictsWith = { FlagScoreboard.class, FlagMinPlayers.class, FlagMaxPlayers.class })
@BukkitListener
public class FlagTeam extends EnumListFlag<FlagTeam.TeamColor> {

    private static final int INVENTORY_SIZE = 9;
    private static final char INFINITY_CHAR = '\u221E';
    private static final int[][] INVENTORY_LAYOUTS = { { 2 }, { 1 }, { 1, 3 }, { 0, 2 }, { 0, 2, 3 }, { 0, 1, 3 },
            { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, };
    private static final int OFFSET = 2;

    private static final MaterialData TEAM_SELECT_ITEMDATA = new MaterialData(Material.CLAY_BRICK);
    private static final String TEAM_SELECT_ITEM_KEY = "team_select";
    public static final String OBJECTIVE_NAME = "spleef_teams";

    private Map<SpleefPlayer, TeamColor> players;
    private Map<SpleefPlayer, TeamColor> deadPlayers;
    private List<TeamColor> deadTeams;
    private Map<TeamColor, Location> spawnpoints;
    private boolean updateInventory;
    private GuiInventory teamChooser;
    private Scoreboard scoreboard;
    private @Inject Game game;

    public FlagTeam() {
        players = Maps.newHashMap();
        deadPlayers = Maps.newLinkedHashMap();
        deadTeams = Lists.newArrayList();
        spawnpoints = Maps.newHashMap();
        updateInventory = true;
    }

    public Map<SpleefPlayer, TeamColor> getPlayers() {
        return players;
    }

    public Scoreboard getScoreboard() {
        return scoreboard;
    }

    @Override
    public void setValue(List<TeamColor> value) {
        super.setValue(value);

        updateInventory = true;
    }

    @Override
    public Class<TeamColor> getEnumType() {
        return TeamColor.class;
    }

    @Override
    public void validateInput(List<TeamColor> input) throws ValidationException {
        if (input.size() <= 1) {
            throw new ValidationException(getI18N().getString(Messages.Command.AT_LEAST_TWO_TEAMS));
        }

        List<TeamColor> existing = Lists.newArrayList();
        for (TeamColor color : input) {
            if (existing.contains(color)) {
                throw new ValidationException(getI18N().getString(Messages.Command.NO_DUPLICATE_TEAMS));
            }
        }
    }

    @Override
    public void defineGameProperties(Map<GameProperty, Object> properties) {
    }

    @Override
    public void getDescription(List<String> description) {
        description.add("Enables team games in spleef.");
    }

    @Override
    public void onFlagAdd(Game game) {
        StatisticRecorder recorder = game.getStatisticRecorder();

        if (recorder != null) {
            RatingCompute compute = new TeamRatingCompute();
            recorder.setRatingCompute(compute);
        }
    }

    @Override
    public void onFlagRemove(Game game) {
        StatisticRecorder recorder = game.getStatisticRecorder();
        recorder.setRatingCompute(null);
    }

    @Override
    public void marshal(Element element) {
        super.marshal(element);

        for (Entry<TeamColor, Location> entry : spawnpoints.entrySet()) {
            Element spawnpointElement = element.addElement("spawnpoint");
            TeamColor color = entry.getKey();
            Location location = entry.getValue();

            spawnpointElement.addElement("color").addText(color.name());
            Element locationElement = spawnpointElement.addElement("location");

            locationElement.addElement("world").addText(String.valueOf(location.getWorld().getName()));
            locationElement.addElement("x").addText(String.valueOf(location.getX()));
            locationElement.addElement("y").addText(String.valueOf(location.getY()));
            locationElement.addElement("z").addText(String.valueOf(location.getZ()));
            locationElement.addElement("yaw").addText(String.valueOf(location.getYaw()));
            locationElement.addElement("pitch").addText(String.valueOf(location.getPitch()));
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void unmarshal(Element element) {
        super.unmarshal(element);

        List<Element> spawnpointElements = element.elements("spawnpoint");
        for (Element spawnpointElement : spawnpointElements) {
            TeamColor color = TeamColor.valueOf(spawnpointElement.elementText("color"));

            Element locationElement = spawnpointElement.element("location");

            World world = Bukkit.getWorld(locationElement.elementText("world"));
            double x = Double.parseDouble(locationElement.elementText("x"));
            double y = Double.parseDouble(locationElement.elementText("y"));
            double z = Double.parseDouble(locationElement.elementText("z"));
            float yaw = Float.parseFloat(locationElement.elementText("yaw"));
            float pitch = Float.parseFloat(locationElement.elementText("pitch"));

            Location location = new Location(world, x, y, z, yaw, pitch);
            spawnpoints.put(color, location);
        }
    }

    private void updateInventory(final Game game) {
        List<Integer> slots = getInventoryLayout();
        GuiInventory inventory = new GuiInventory(getHeavySpleef().getPlugin(), 1,
                getI18N().getString(Messages.Player.TEAM_SELECTOR_TITLE)) {

            @Override
            public void onClick(GuiClickEvent event) {
                Player player = event.getPlayer();
                SpleefPlayer spleefPlayer = getHeavySpleef().getSpleefPlayer(player);
                GuiInventorySlot slot = event.getSlot();

                event.setCancelled(true);

                TeamColor color = (TeamColor) slot.getValue();
                if (color == null) {
                    return;
                }

                PlayerSelectTeamEvent selectEvent = new PlayerSelectTeamEvent(game, spleefPlayer, color);
                game.getEventBus().callEvent(selectEvent);

                if (selectEvent.isCancelled()) {
                    spleefPlayer.sendMessage(selectEvent.getFailMessage());
                    return;
                }

                players.put(spleefPlayer, color);
                spleefPlayer.sendMessage(getI18N().getVarString(Messages.Player.TEAM_CHOOSEN)
                        .setVariable("team", color.getChatColor() + getLocalizedColorName(color)).toString());
                updateScoreboard();
                updateInventory(game);
                updateViews();

                PlayerSelectedTeamEvent selectedEvent = new PlayerSelectedTeamEvent(game, spleefPlayer, color);
                game.getEventBus().callEvent(selectedEvent);
            }
        };

        GetMaxPlayersEvent event = new GetMaxPlayersEvent();
        game.getEventBus().callEvent(event);

        for (int i = 0; i < size(); i++) {
            int slot = slots.get(i);
            TeamColor color = get(i);

            GuiInventorySlot invSlot = inventory.getSlot(slot, 0);

            ItemStack teamStack = color.getMaterialWoolData().toItemStack(1);
            ItemMeta meta = teamStack.getItemMeta();

            meta.setDisplayName(color.getChatColor() + getLocalizedColorName(color));
            meta.setLore(Lists.newArrayList(ChatColor.YELLOW + "" + sizeOf(color) + ChatColor.AQUA + "/"
                    + (event.getMaxPlayers() != 0 ? String.valueOf(event.getMaxPlayers()) : INFINITY_CHAR)));

            teamStack.setItemMeta(meta);
            invSlot.setItem(teamStack);
            invSlot.setValue(color);
        }

        teamChooser = inventory;
    }

    private List<Integer> getInventoryLayout() {
        int size = size();
        List<Integer> slots = Lists.newArrayList();

        if (size % 2 != 0) {
            slots.add(4);
        }

        for (int slot : INVENTORY_LAYOUTS[size - OFFSET]) {
            int reflectedSlot = INVENTORY_SIZE - 1 - slot;

            slots.add(slot);
            slots.add(reflectedSlot);
        }

        return slots;
    }

    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        Player player = event.getPlayer();
        SpleefPlayer spleefPlayer = getHeavySpleef().getSpleefPlayer(player);

        Action action = event.getAction();
        if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) {
            return;
        }

        if (!players.containsKey(spleefPlayer)) {
            return;
        }

        ItemStack clicked = event.getItem();
        if (clicked == null || !clicked.hasItemMeta() || !clicked.getItemMeta().hasLore()) {
            return;
        }

        MetadatableItemStack metadatable = new MetadatableItemStack(clicked);

        if (!metadatable.hasMetadata(TEAM_SELECT_ITEM_KEY)) {
            return;
        }

        teamChooser.open(player);
    }

    @Subscribe
    public void onPlayerPreJoinGame(PlayerPreJoinGameEvent event) {
        GameState state = event.getGame().getGameState();
        if (state == GameState.STARTING || state == GameState.WARMUP) {
            event.setJoinResult(JoinResult.TEMPORARY_DENY);
            event.setMessage(getI18N().getVarString(Messages.Command.GAME_IS_INGAME)
                    .setVariable("game", game.getName()).toString());
        }
    }

    @Subscribe
    public void onGameJoin(PlayerJoinGameEvent event) {
        Game game = event.getGame();
        SpleefPlayer player = event.getPlayer();

        if (updateInventory) {
            updateInventory(game);
        }

        MetadatableItemStack teamSelectorOpener = new MetadatableItemStack(TEAM_SELECT_ITEMDATA.toItemStack(1));
        ItemMeta meta = teamSelectorOpener.getItemMeta();
        meta.setDisplayName(getI18N().getString(Messages.Player.TEAM_SELECTOR_TITLE));
        meta.setLore(Lists.newArrayList(getI18N().getString(Messages.Player.CLICK_TO_JOIN_TEAM)));
        teamSelectorOpener.setItemMeta(meta);
        teamSelectorOpener.setMetadata(TEAM_SELECT_ITEM_KEY, null);

        player.getBukkitPlayer().getInventory().addItem(teamSelectorOpener);
        player.getBukkitPlayer().updateInventory();

        players.put(player, null);
        updateScoreboard();
    }

    @Subscribe
    public void onGameCountdown(GameCountdownEvent event) {
        List<TeamSizeHolder> teamSizes = Lists.newArrayList();
        for (TeamColor color : getValue()) {
            TeamSizeHolder holder = new TeamSizeHolder();
            holder.color = color;
            holder.size = sizeOf(color);
            teamSizes.add(holder);
        }

        Collections.sort(teamSizes);

        Map<SpleefPlayer, TeamColor> tempTeamAssigns = Maps.newHashMap();
        for (Entry<SpleefPlayer, TeamColor> entry : players.entrySet()) {
            TeamColor assignedColor = entry.getValue();
            SpleefPlayer player = entry.getKey();

            if (assignedColor == null) {
                TeamSizeHolder lowestHolder = teamSizes.get(0);
                assignedColor = lowestHolder.color;

                lowestHolder.size++;
                Collections.sort(teamSizes);
            }

            tempTeamAssigns.put(player, assignedColor);
        }

        //Finally, check if there are enough players to start the game
        ValidateTeamsEvent validateEvent = new ValidateTeamsEvent(teamSizes);
        game.getEventBus().callEvent(validateEvent);

        if (validateEvent.isCancelled()) {
            event.setCancelled(true);
            event.setErrorBroadcast(validateEvent.getErrorMessage());
            return;
        }

        //Enough players to start the game, set the temporary assigns to real ones
        players = tempTeamAssigns;

        //Remove team selection items
        for (SpleefPlayer player : players.keySet()) {
            Inventory inventory = player.getBukkitPlayer().getInventory();
            for (ItemStack stack : inventory.getContents()) {
                if (stack == null) {
                    continue;
                }

                MetadatableItemStack metaStack = new MetadatableItemStack(stack);
                if (!metaStack.hasItemMeta() || !metaStack.getItemMeta().hasLore()
                        || !metaStack.hasMetadata(TEAM_SELECT_ITEM_KEY)) {
                    continue;
                }

                inventory.remove(stack);
            }

            TeamColor color = players.get(player);

            player.getBukkitPlayer().updateInventory();
            player.sendMessage(getI18N().getVarString(Messages.Player.TEAM_CHOOSEN)
                    .setVariable("team", color.getChatColor() + getLocalizedColorName(color)).toString());
        }

        //Determine spawnpoints for teams
        Map<SpleefPlayer, Location> spawnLocationMap = Maps.newHashMap();
        for (Entry<SpleefPlayer, TeamColor> entry : players.entrySet()) {
            SpleefPlayer player = entry.getKey();
            TeamColor color = entry.getValue();

            Location spawnpoint = spawnpoints.get(color);
            if (spawnpoint != null) {
                spawnLocationMap.put(player, spawnpoint);
            }
        }

        if (!spawnLocationMap.isEmpty()) {
            event.setSpawnLocationMap(spawnLocationMap);
        }

        updateScoreboard();
    }

    @Subscribe
    public void onPlayerLeave(PlayerLeftGameEvent event) {
        SpleefPlayer player = event.getPlayer();
        TeamColor color = players.get(player);

        if (color == null) {
            return;
        }

        players.remove(player);

        if (event.getGame().getGameState().isGameActive()) {
            deadPlayers.put(player, color);

            if (event.getCause() != QuitCause.WIN) {
                int size = sizeOf(color);

                if (size <= 0) {
                    deadTeams.add(color);
                    event.getGame().broadcast(getI18N().getVarString(Messages.Broadcast.TEAM_IS_OUT)
                            .setVariable("color", color.getChatColor() + getLocalizedColorName(color)).toString());
                } else {
                    event.getGame()
                            .broadcast(getI18N().getVarString(Messages.Broadcast.TEAMS_LEFT)
                                    .setVariable("team", color.getChatColor() + getLocalizedColorName(color))
                                    .setVariable("amount", String.valueOf(size)).toString());
                }

                TeamColor lastColor = null;
                boolean oneTeamLeft = true;
                for (TeamColor teamColor : players.values()) {
                    if (lastColor == null) {
                        lastColor = teamColor;
                        continue;
                    }

                    if (lastColor != teamColor) {
                        oneTeamLeft = false;
                    }
                }

                if (oneTeamLeft) {
                    Set<SpleefPlayer> leftSet = players.keySet();
                    SpleefPlayer[] left = leftSet.toArray(new SpleefPlayer[leftSet.size()]);

                    deadTeams.add(lastColor);
                    event.getGame().requestWin(left, false);
                    game.broadcast(getI18N().getVarString(Messages.Broadcast.TEAM_WON)
                            .setVariable("team", lastColor.getChatColor() + getLocalizedColorName(lastColor))
                            .setVariable("game", game.getName()).toString());
                }
            }
        }

        updateScoreboard(player);
    }

    @Subscribe(priority = Subscribe.Priority.HIGH)
    public void onGameEnd(GameEndEvent event) {
        deadPlayers.clear();
        deadTeams.clear();
    }

    @Subscribe
    public void onGetScoreboardDisplayName(FlagScoreboard.GetScoreboardDisplayNameEvent event) {
        event.setDisplayName(ChatColor.GOLD + "" + ChatColor.BOLD + "Team Spleef");
    }

    private void updateScoreboard() {
        updateScoreboard(null);
    }

    @SuppressWarnings("deprecation")
    private void updateScoreboard(SpleefPlayer left) {
        if (scoreboard == null) {
            scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
            scoreboard.registerNewObjective(OBJECTIVE_NAME, "dummy");

            for (TeamColor color : getValue()) {
                Team team = scoreboard.registerNewTeam(color.name());
                team.setCanSeeFriendlyInvisibles(true);
                team.setAllowFriendlyFire(false);
                team.setPrefix(color.getChatColor().toString());
            }

            TeamScoreboardInitializeEvent event = new TeamScoreboardInitializeEvent();
            game.getEventBus().callEvent(event);
        }

        if (left != null) {
            Scoreboard mainBoard = Bukkit.getScoreboardManager().getMainScoreboard();
            left.getBukkitPlayer().setScoreboard(mainBoard);
        }

        for (Entry<SpleefPlayer, TeamColor> entry : players.entrySet()) {
            SpleefPlayer player = entry.getKey();
            Player bukkitPlayer = player.getBukkitPlayer();

            if (bukkitPlayer.getScoreboard() != scoreboard) {
                bukkitPlayer.setScoreboard(scoreboard);
            }

            TeamColor color = entry.getValue();
            if (color == null) {
                continue;
            }

            Team team = scoreboard.getTeam(color.name());
            if (!team.hasPlayer(bukkitPlayer)) {
                team.addPlayer(bukkitPlayer);
            }
        }
    }

    public String getLocalizedColorName(TeamColor color) {
        String[] localizedArray = getI18N().getStringArray(Messages.Arrays.TEAM_COLOR_ARRAY);
        return color.getLocalizedName(localizedArray);
    }

    public int sizeOf(TeamColor color) {
        int count = 0;
        for (Entry<SpleefPlayer, TeamColor> entry : players.entrySet()) {
            if (entry.getValue() == color) {
                ++count;
            }
        }

        return count;
    }

    @Flag(name = "spawnpoint", parent = FlagTeam.class)
    public static class FlagTeamSpawnpoint extends LocationFlag {

        private TeamColor color;

        @Override
        public Location parseInput(SpleefPlayer player, String input) throws InputParseException {
            //(Ab)using a child flag to set spawnpoints in the parent flag
            TeamColor color;

            try {
                color = TeamColor.valueOf(input.toUpperCase());
            } catch (Exception e) {
                color = TeamColor.byColorName(getI18N().getStringArray(Messages.Arrays.TEAM_COLOR_ARRAY), input);
            }

            if (color == null) {
                throw new InputParseException(getI18N().getVarString(Messages.Command.TEAM_COLOR_NOT_FOUND)
                        .setVariable("color", input).toString());
            }

            this.color = color;
            return super.parseInput(player, input);
        }

        @Override
        public void onFlagAdd(Game game) {
            FlagTeam team = (FlagTeam) getParent();
            team.spawnpoints.put(color, getValue());

            game.removeFlag(getClass());
        }

        @Override
        public void getDescription(List<String> description) {
            description.add("Sets the spawnpoint for a team");
        }

    }

    static class GetMaxPlayersEvent extends Event {

        private int maxPlayers;

        public int getMaxPlayers() {
            return maxPlayers;
        }

        public void setMaxPlayers(int maxPlayers) {
            this.maxPlayers = maxPlayers;
        }

    }

    static class ValidateTeamsEvent extends Event implements Cancellable {

        private boolean cancelled;
        private String errorMessage;
        private List<TeamSizeHolder> teams;

        public ValidateTeamsEvent(List<TeamSizeHolder> teams) {
            this.teams = teams;
        }

        public boolean isCancelled() {
            return cancelled;
        }

        public void setCancelled(boolean cancelled) {
            this.cancelled = cancelled;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public void setErrorMessage(String errorMessage) {
            this.errorMessage = errorMessage;
        }

        public List<TeamSizeHolder> getTeams() {
            return teams;
        }

    }

    static class TeamSizeHolder implements Comparable<TeamSizeHolder> {

        private TeamColor color;
        private int size;

        public TeamColor getColor() {
            return color;
        }

        public int getSize() {
            return size;
        }

        @Override
        public int compareTo(TeamSizeHolder o) {
            return Integer.valueOf(size).compareTo(o.size);
        }

    }

    public static class PlayerSelectedTeamEvent extends PlayerGameEvent {

        private TeamColor colorSelected;

        public PlayerSelectedTeamEvent(Game game, SpleefPlayer who, TeamColor color) {
            super(game, who);

            this.colorSelected = color;
        }

        public TeamColor getColorSelected() {
            return colorSelected;
        }

    }

    public static class PlayerSelectTeamEvent extends PlayerGameEvent implements Cancellable {

        private boolean cancelled;
        private String failMessage;
        private TeamColor colorSelected;

        public PlayerSelectTeamEvent(Game game, SpleefPlayer who, TeamColor color) {
            super(game, who);

            this.colorSelected = color;
        }

        @Override
        public boolean isCancelled() {
            return cancelled;
        }

        @Override
        public void setCancelled(boolean cancel) {
            this.cancelled = cancel;
        }

        public String getFailMessage() {
            return failMessage;
        }

        public void setFailMessage(String failMessage) {
            this.failMessage = failMessage;
        }

        public TeamColor getColorSelected() {
            return colorSelected;
        }

    }

    public static class TeamScoreboardInitializeEvent extends Event {

    }

    public enum TeamColor {

        WHITE(ChatColor.WHITE, 0x0, Color.WHITE), GOLD(ChatColor.GOLD, 0x1, Color.fromRGB(0xFFD700)), YELLOW(
                ChatColor.YELLOW, 0x4,
                Color.YELLOW), GREEN(ChatColor.GREEN, 0x5, Color.LIME), PINK(ChatColor.LIGHT_PURPLE, 0x6,
                        Color.fromRGB(0xFF66CC)), GRAY(ChatColor.GRAY, 0x7, Color.GRAY), BLUE(ChatColor.BLUE, 0xB,
                                Color.BLUE), RED(ChatColor.RED, 0xE, Color.RED);

        private ChatColor chatColor;
        private byte legacyWoolData;
        private Color rgbColor;
        private MaterialData woolData;

        @SuppressWarnings("deprecation")
        private TeamColor(ChatColor chatColor, int legacyWoolData, Color rgbColor) {
            this.chatColor = chatColor;
            this.legacyWoolData = (byte) legacyWoolData;
            this.rgbColor = rgbColor;
            this.woolData = new MaterialData(Material.WOOL, (byte) legacyWoolData);
        }

        public ChatColor getChatColor() {
            return chatColor;
        }

        public byte getLegacyWoolData() {
            return legacyWoolData;
        }

        public Color getRGB() {
            return rgbColor;
        }

        public String getLocalizedName(String[] nameArray) {
            //Important: Keep the order of the array like the enum order
            return nameArray[ordinal()];
        }

        public MaterialData getMaterialWoolData() {
            return woolData;
        }

        public static TeamColor byColorName(String[] nameArray, String name) {
            final int illegalIndex = -1;

            int index = illegalIndex;
            for (int i = 0; i < nameArray.length; i++) {
                if (!nameArray[i].equalsIgnoreCase(name)) {
                    continue;
                }

                index = i;
                break;
            }

            if (index == illegalIndex) {
                return null;
            }

            return values()[index];
        }

    }

    private class TeamRatingCompute implements RatingCompute {

        @Override
        public RatingResult compute(Map<String, Statistic> statistics, Game game, SpleefPlayer[] winnersArray) {
            Map<TeamColor, Double> ratings = Maps.newHashMap();
            Map<String, Double> results = Maps.newHashMap();

            for (TeamColor color : getValue()) {
                Set<SpleefPlayer> subSet = getDeadPlayersByTeam(color);
                if (subSet.isEmpty()) {
                    continue;
                }

                double averageRating = 0;
                int counter = 0;

                for (SpleefPlayer player : subSet) {
                    Statistic statistic = statistics.get(player.getName());
                    if (statistic == null) {
                        continue;
                    }

                    averageRating += statistic.getRating();
                    counter++;
                }

                averageRating /= counter;
                ratings.put(color, averageRating);
            }

            for (Entry<TeamColor, Double> entry : ratings.entrySet()) {
                TeamColor teamColor = entry.getKey();

                double expectation = e(ratings, teamColor);
                double score = s(getPlace(teamColor), ratings.size());

                double ratingDif = K * (score - expectation);

                Set<SpleefPlayer> players = getDeadPlayersByTeam(teamColor);
                for (SpleefPlayer player : players) {
                    Statistic statistic = statistics.get(player.getName());

                    results.put(player.getName(), statistic.getRating() + (ratingDif / players.size()));
                }
            }

            return new RatingResult(results);
        }

        private Set<SpleefPlayer> getDeadPlayersByTeam(TeamColor color) {
            Set<SpleefPlayer> subSet = Sets.newHashSet();
            for (Entry<SpleefPlayer, TeamColor> entry : deadPlayers.entrySet()) {
                if (entry.getValue() != color) {
                    continue;
                }

                subSet.add(entry.getKey());
            }

            return subSet;
        }

        private int getPlace(TeamColor color) {
            for (int i = 0; i < deadTeams.size(); i++) {
                TeamColor deadTeam = deadTeams.get(i);

                if (deadTeam == color) {
                    return deadTeams.size() - i;
                }
            }

            throw new IllegalArgumentException(
                    "Dead team list has does not contain a team with the color " + color.name());
        }

        private double e(Map<TeamColor, Double> ratings, TeamColor color) {
            double sumE = 0;
            for (Entry<TeamColor, Double> entry : ratings.entrySet()) {
                if (entry.getKey() == color) {
                    continue;
                }

                double dif = ratings.get(entry.getKey()) - ratings.get(color);
                if (dif > D) {
                    dif = D;
                } else if (dif < -D) {
                    dif = -D;
                }

                sumE += 1D / (1 + Math.pow(10, dif / D));
            }

            final int size = ratings.size();
            return sumE / ((size * (size - 1)) / 2);
        }

        private double s(int place, int numTeams) {
            return (double) (numTeams - place) / ((numTeams * (numTeams - 1)) / 2);
        }

    }

}