com.l2jfree.gameserver.model.entity.events.AutomatedTvT.java Source code

Java tutorial

Introduction

Here is the source code for com.l2jfree.gameserver.model.entity.events.AutomatedTvT.java

Source

/*
 * 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 com.l2jfree.gameserver.model.entity.events;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;

import javolution.util.FastMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.l2jfree.Config;
import com.l2jfree.L2DatabaseFactory;
import com.l2jfree.gameserver.Announcements;
import com.l2jfree.gameserver.ThreadPoolManager;
import com.l2jfree.gameserver.gameobjects.L2Player;
import com.l2jfree.gameserver.gameobjects.instance.L2CubicInstance;
import com.l2jfree.gameserver.model.Location;
import com.l2jfree.gameserver.model.items.L2ItemInstance;
import com.l2jfree.gameserver.model.restriction.global.AutomatedTvTRestriction;
import com.l2jfree.gameserver.model.restriction.global.GlobalRestrictions;
import com.l2jfree.gameserver.model.skills.L2Skill;
import com.l2jfree.gameserver.network.SystemChatChannelId;
import com.l2jfree.gameserver.network.SystemMessageId;
import com.l2jfree.gameserver.network.packets.server.CreatureSay;
import com.l2jfree.gameserver.network.packets.server.SystemMessage;
import com.l2jfree.gameserver.util.Broadcast;
import com.l2jfree.tools.random.Rnd;

/**
 * @author savormix
 */
public final class AutomatedTvT {
    static {
        AutomatedTvTRestriction.getInstance().activate(); // TODO: must be checked
    }

    private static final Log _log = LogFactory.getLog(AutomatedTvT.class);

    private static final String evtName = "Team versus team";

    //when the event has ended and not yet started
    private static final int STATUS_NOT_IN_PROGRESS = 0;
    //registration in progress
    private static final int STATUS_REGISTRATION = 1;
    //registration ended, players frozen & teled to the place, waiting for them to appear
    private static final int STATUS_PREPARATION = 2;
    //players are allowed to fight
    private static final int STATUS_COMBAT = 3;
    //players are frozen, rewarded and teled back to where they were
    private static final int STATUS_REWARDS = 4;

    private static AutomatedTvT instance = null;

    public static final AutomatedTvT getInstance() {
        if (instance == null)
            instance = new AutomatedTvT();
        return instance;
    }

    /**
     * Called when configuration is reloaded and {@link Config#AUTO_TVT_ENABLED} = true<BR>
     * <CODE>instance</CODE> will only be <CODE>null</CODE> when the config is loaded during
     * server startup, and we don't want the event to start countdown THAT early.<BR>
     * <I>Normally initialization is called when loading [static] extensions.</I>
     */
    public static final void startIfNecessary() {
        if (instance != null && !instance.active) {
            instance.active = true;
            instance.buildCountArray();
            instance.tpm.scheduleGeneral(instance.task, Config.AUTO_TVT_DELAY_INITIAL_REGISTRATION);
        }
    }

    private final ThreadPoolManager tpm;

    private final AutoEventTask task;
    private final AutoReviveTask taskDuring;
    private ScheduledFuture<?> reviver;
    private ScheduledFuture<?> event;

    private final CopyOnWriteArrayList<Integer> registered;
    private final CopyOnWriteArrayList<L2Player> participants;
    private final FastMap<Integer, Participant> eventPlayers;
    private Team[] eventTeams;
    private int[] teamMembers;

    private volatile int status;
    private volatile boolean active;
    private int announced;

    private AutomatedTvT() {
        tpm = ThreadPoolManager.getInstance();
        status = STATUS_NOT_IN_PROGRESS;
        announced = 0;
        // This has no maximum bound, thus configuration changes will not crash anything
        participants = new CopyOnWriteArrayList<L2Player>();
        registered = new CopyOnWriteArrayList<Integer>();
        eventPlayers = new FastMap<Integer, Participant>(Config.AUTO_TVT_PARTICIPANTS_MAX);
        eventTeams = null;
        task = new AutoEventTask();
        taskDuring = new AutoReviveTask();
        reviver = null;
        buildCountArray();
        active = Config.AUTO_TVT_ENABLED;
        if (active)
            tpm.scheduleGeneral(task, Config.AUTO_TVT_DELAY_INITIAL_REGISTRATION);
        _log.info("AutomatedTvT: initialized.");
    }

    private class AutoEventTask implements Runnable {
        @Override
        public void run() {
            switch (status) {
            case STATUS_NOT_IN_PROGRESS:
                if (Config.AUTO_TVT_ENABLED)
                    registrationStart();
                else
                    active = false;
                break;
            case STATUS_REGISTRATION:
                if (announced < (Config.AUTO_TVT_REGISTRATION_ANNOUNCEMENT_COUNT + 2))
                    registrationAnnounce();
                else
                    registrationEnd();
                break;
            case STATUS_PREPARATION:
                eventStart();
                break;
            case STATUS_COMBAT:
                eventEnd();
                break;
            case STATUS_REWARDS:
                status = STATUS_NOT_IN_PROGRESS;
                tpm.scheduleGeneral(task, Config.AUTO_TVT_DELAY_BETWEEN_EVENTS);
                break;
            default:
                _log.fatal("Incorrect status set in Automated " + evtName + ", terminating the event!");
            }
        }
    }

    private class AutoReviveTask implements Runnable {
        @Override
        public void run() {
            L2Player player;
            for (Participant p : eventPlayers.values()) {
                player = p.getPlayer();
                if (player != null && player.isDead())
                    revive(player, p.getTeam());
            }
        }
    }

    private final void registrationStart() {
        status = STATUS_REGISTRATION;
        Announcements.getInstance().announceToAll(SystemMessageId.REGISTRATION_PERIOD);
        SystemMessage time = new SystemMessage(SystemMessageId.REGISTRATION_TIME_S1_S2_S3);
        long timeLeft = Config.AUTO_TVT_PERIOD_LENGHT_REGISTRATION / 1000;
        time.addNumber((int) (timeLeft / 3600));
        time.addNumber((int) (timeLeft % 3600 / 60));
        time.addNumber((int) (timeLeft % 3600 % 60));
        Broadcast.toAllOnlinePlayers(time);
        Announcements.getInstance().announceToAll("To join the " + evtName + " you must type .jointvt");
        tpm.scheduleGeneral(task,
                Config.AUTO_TVT_PERIOD_LENGHT_REGISTRATION / (Config.AUTO_TVT_REGISTRATION_ANNOUNCEMENT_COUNT + 2));
    }

    private final void registrationAnnounce() {
        SystemMessage time = new SystemMessage(SystemMessageId.REGISTRATION_TIME_S1_S2_S3);
        long timeLeft = Config.AUTO_TVT_PERIOD_LENGHT_REGISTRATION;
        long elapsed = timeLeft / (Config.AUTO_TVT_REGISTRATION_ANNOUNCEMENT_COUNT + 2) * announced;
        timeLeft -= elapsed;
        timeLeft /= 1000;
        time.addNumber((int) (timeLeft / 3600));
        time.addNumber((int) (timeLeft % 3600 / 60));
        time.addNumber((int) (timeLeft % 3600 % 60));
        Broadcast.toAllOnlinePlayers(time);
        Announcements.getInstance().announceToAll("To join the " + evtName + " you must type .jointvt");
        announced++;
        tpm.scheduleGeneral(task,
                Config.AUTO_TVT_PERIOD_LENGHT_REGISTRATION / (Config.AUTO_TVT_REGISTRATION_ANNOUNCEMENT_COUNT + 2));
    }

    private final void registrationEnd() {
        announced = 0;
        status = STATUS_PREPARATION;

        registered.clear();

        // The array will never be too small
        L2Player[] reged = participants.toArray(new L2Player[participants.size()]);
        for (L2Player player : reged) {
            if (player == null)
                continue;
            if (!canJoin(player)) {
                player.sendMessage("You no longer meet the requirements to join " + evtName);
                participants.remove(player);
            }
        }

        if (participants.size() < Config.AUTO_TVT_PARTICIPANTS_MIN) {
            Announcements.getInstance().announceToAll(evtName + " will not start, not enough players!");
            participants.clear();
            status = STATUS_NOT_IN_PROGRESS;
            tpm.scheduleGeneral(task, Config.AUTO_TVT_DELAY_BETWEEN_EVENTS);
            return;
        }

        eventTeams = new Team[Config.AUTO_TVT_TEAM_LOCATIONS.length];
        for (int i = 0; i < eventTeams.length; i++) {
            if (Config.AUTO_TVT_TEAM_COLORS_RANDOM)
                eventTeams[i] = new Team(correctColor(eventTeams, Rnd.get(256), Rnd.get(256), Rnd.get(256), i));
            else
                eventTeams[i] = new Team(Config.AUTO_TVT_TEAM_COLORS[i]);
        }

        int currTeam = 0;
        SystemMessage time = new SystemMessage(SystemMessageId.BATTLE_BEGINS_S1_S2_S3);
        long timeLeft = Config.AUTO_TVT_PERIOD_LENGHT_PREPARATION / 1000;
        time.addNumber((int) (timeLeft / 3600));
        time.addNumber((int) (timeLeft % 3600 / 60));
        time.addNumber((int) (timeLeft % 3600 % 60));

        reged = participants.toArray(new L2Player[participants.size()]);
        for (L2Player player : reged) {
            if (player == null)
                continue;
            Participant p = new Participant(currTeam, player);
            eventPlayers.put(player.getObjectId(), p);
            p.setNameColor((eventTeams[currTeam].getColorRed() & 0xFF) + (eventTeams[currTeam].getColorGreen() << 8)
                    + (eventTeams[currTeam].getColorBlue() << 16));
            player.setIsPetrified(true);
            player.sendPacket(time);
            checkEquipment(player);
            if (Config.AUTO_TVT_START_CANCEL_PARTY && player.getParty() != null)
                player.getParty().removePartyMember(player);
            if (Config.AUTO_TVT_START_CANCEL_BUFFS)
                player.stopAllEffects();
            if (Config.AUTO_TVT_START_CANCEL_CUBICS && !player.getCubics().isEmpty()) {
                for (L2CubicInstance cubic : player.getCubics().values()) {
                    cubic.stopAction();
                    cubic.cancelDisappear();
                }
                player.getCubics().clear();
            }
            if (Config.AUTO_TVT_START_CANCEL_SERVITORS && player.getPet() != null)
                player.getPet().unSummon();
            if (Config.AUTO_TVT_START_CANCEL_TRANSFORMATION && player.isTransformed())
                player.untransform();
            if (player.isDead())
                player.setIsPendingRevive(true);
            player.teleToLocation(Config.AUTO_TVT_TEAM_LOCATIONS[currTeam]);
            if (Config.AUTO_TVT_START_RECOVER) {
                player.getStatus().setCurrentCp(player.getMaxCp());
                player.getStatus().setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
            }
            currTeam++;
            if (currTeam == eventTeams.length)
                currTeam = 0;
        }
        participants.clear();
        tpm.scheduleGeneral(task, Config.AUTO_TVT_PERIOD_LENGHT_PREPARATION);
    }

    private final void eventStart() {
        status = STATUS_COMBAT;
        SystemMessage time = new SystemMessage(SystemMessageId.BATTLE_ENDS_S1_S2_S3);
        long timeLeft = Config.AUTO_TVT_PERIOD_LENGHT_EVENT / 1000;
        time.addNumber((int) (timeLeft / 3600));
        time.addNumber((int) (timeLeft % 3600 / 60));
        time.addNumber((int) (timeLeft % 3600 % 60));
        L2Player player;
        for (Participant p : eventPlayers.values()) {
            player = p.getPlayer();
            if (player == null)
                continue;
            player.setIsPetrified(false);
            player.sendPacket(time);
            updatePlayerTitle(p);
        }
        reviver = tpm.scheduleAtFixedRate(taskDuring, Config.AUTO_TVT_REVIVE_DELAY, Config.AUTO_TVT_REVIVE_DELAY);
        event = tpm.scheduleGeneral(task, Config.AUTO_TVT_PERIOD_LENGHT_EVENT);
    }

    private final void eventEnd() {
        if (status != STATUS_COMBAT)
            return;
        status = STATUS_REWARDS;
        reviver.cancel(true);
        if (!event.cancel(false))
            return;

        int winnerTeam = getWinnerTeam();
        if (winnerTeam != -1) {
            Announcements.getInstance().announceToAll(evtName + ": Team " + (winnerTeam + 1) + " wins!");
            Announcements.getInstance()
                    .announceToAll(evtName + ": Cumulative score: " + eventTeams[winnerTeam].getPoints());
        } else
            Announcements.getInstance().announceToAll(evtName + ": There is no winner team.");

        L2Player player;
        for (Participant p : eventPlayers.values()) {
            player = p.getPlayer();
            if (player == null) {
                removeDisconnected(p.getObjectID(), p.getLoc());
                continue;
            }
            if (p.getTeam() == winnerTeam)
                reward(player);
            removeFromEvent(player, p);
        }
        eventPlayers.clear();
        tpm.scheduleGeneral(task, Config.AUTO_TVT_PERIOD_LENGHT_REWARDS);
    }

    public final void addDisconnected(L2Player participant) {
        switch (status) {
        case STATUS_REGISTRATION:
            if (Config.AUTO_TVT_REGISTER_AFTER_RELOG && registered.remove(participant.getObjectId()))
                registerPlayer(participant);
            break;
        case STATUS_COMBAT:
            Participant p = eventPlayers.get(participant.getObjectId());
            if (p == null)
                break;
            p.setPlayer(participant);
            checkEquipment(participant);
            updatePlayerTitle(p);
            int team = p.getTeam();
            p.setNameColor((eventTeams[team].getColorRed() & 0xFF) + (eventTeams[team].getColorGreen() << 8)
                    + (eventTeams[team].getColorBlue() << 16));
            participant.teleToLocation(Config.AUTO_TVT_TEAM_LOCATIONS[team]);
            break;
        }
    }

    private final void checkEquipment(L2Player player) {
        L2ItemInstance item;
        for (int i = 0; i < 25; i++) {
            synchronized (player.getInventory()) {
                item = player.getInventory().getPaperdollItem(i);
                if (item != null && !canUse(item.getItemId()))
                    player.useEquippableItem(item, true);
            }
        }
    }

    public static final boolean canUse(int itemId) {
        for (int id : Config.AUTO_TVT_DISALLOWED_ITEMS)
            if (itemId == id)
                return false;
        return true;
    }

    private final int getWinnerTeam() {
        int maxPts = 0, winTeam = -1, temp;
        for (int i = 0; i < eventTeams.length; i++) {
            temp = eventTeams[i].getPoints();
            if (temp > maxPts) {
                maxPts = temp;
                winTeam = i;
            }
        }
        return winTeam;
    }

    private final void reward(L2Player player) {
        for (int i = 0; i < Config.AUTO_TVT_REWARD_IDS.length; i++) {
            player.addItem("TvT Reward", Config.AUTO_TVT_REWARD_IDS[i], Config.AUTO_TVT_REWARD_COUNT[i], null,
                    false, true);
            player.sendPacket(new SystemMessage(SystemMessageId.CONGRATULATIONS_RECEIVED_S1)
                    .addItemName(Config.AUTO_TVT_REWARD_IDS[i]));
        }
    }

    public static final boolean isInProgress() {
        switch (getInstance().status) {
        case STATUS_PREPARATION:
        case STATUS_COMBAT:
            return true;
        default:
            return false;
        }
    }

    public static final boolean isReged(L2Player player) {
        return getInstance().isMember(player);
    }

    public static final boolean isPlaying(L2Player player) {
        return isInProgress() && isReged(player);
    }

    public final boolean isMember(L2Player player) {
        if (player == null)
            return false;

        switch (status) {
        case STATUS_NOT_IN_PROGRESS:
            return false;
        case STATUS_REGISTRATION:
            return participants.contains(player);
        case STATUS_PREPARATION:
            return participants.contains(player) || isMember(player.getObjectId());
        case STATUS_COMBAT:
        case STATUS_REWARDS:
            return isMember(player.getObjectId());
        default:
            return false;
        }
    }

    private final boolean isMember(int oID) {
        return eventPlayers.get(oID) != null;
    }

    public static int getTeam(L2Player player) {
        Participant p = getInstance().eventPlayers.get(player.getObjectId());
        if (p != null)
            return p.getTeam();
        else
            // re-check if it doesn't create problems
            return -1;
    }

    public static int getNameColor(L2Player player) {
        Participant p = getInstance().eventPlayers.get(player.getObjectId());
        if (p != null)
            return p.getNameColor();
        else
            return -1;
    }

    private final boolean canJoin(L2Player player) {
        // Cannot mess with observation, Olympiad, raids, sieges or other events
        if (GlobalRestrictions.isRestricted(player, AutomatedTvTRestriction.class))
            return false;

        // Level restrictions
        boolean can = player.getLevel() <= Config.AUTO_TVT_LEVEL_MAX;
        can &= player.getLevel() >= Config.AUTO_TVT_LEVEL_MIN;
        // Hero restriction
        if (!Config.AUTO_TVT_REGISTER_HERO)
            can &= !player.isHero();
        // Cursed weapon owner restriction
        if (!Config.AUTO_TVT_REGISTER_CURSED)
            can &= !player.isCursedWeaponEquipped();
        return can;
    }

    public final void registerPlayer(L2Player player) {
        if (!active)
            return;

        if (status != STATUS_REGISTRATION || participants.size() >= Config.AUTO_TVT_PARTICIPANTS_MAX)
            player.sendPacket(SystemMessageId.REGISTRATION_PERIOD_OVER);
        else if (!participants.contains(player)) {
            if (!canJoin(player)) {
                player.sendMessage("You do not meet the requirements to join " + evtName);
                return;
            }
            participants.add(player);
            registered.add(player.getObjectId());
            player.sendMessage("You have been registered to " + evtName);
            if (Config.AUTO_TVT_REGISTER_CANCEL)
                player.sendMessage("If you decide to cancel your registration, type .leavetvt");
        } else
            player.sendMessage("Already registered!");
    }

    public final void cancelRegistration(L2Player player) {
        if (!active)
            return;

        if (status != STATUS_REGISTRATION)
            player.sendPacket(SystemMessageId.REGISTRATION_PERIOD_OVER);
        else if (participants.contains(player)) {
            participants.remove(player);
            registered.remove(player.getObjectId());
            player.sendMessage("You have cancelled your registration in " + evtName);
        } else
            player.sendMessage("You have not registered in " + evtName);
    }

    public final void onKill(L2Player killer, L2Player victim) {
        if (status != STATUS_COMBAT || !isMember(killer) || !isMember(victim))
            return;
        Participant kp = eventPlayers.get(killer.getObjectId());
        Participant vp = eventPlayers.get(victim.getObjectId());
        if (kp.getTeam() != vp.getTeam()) {
            kp.increaseScore();
            eventTeams[kp.getTeam()].addPoint();
            if (kp.isGodlike() && Config.AUTO_TVT_GODLIKE_ANNOUNCE) {
                CreatureSay cs = new CreatureSay(0, SystemChatChannelId.Chat_Shout, evtName,
                        killer.getName() + ": God-like!");
                for (Participant p : eventPlayers.values())
                    if (p.getPlayer() != null)
                        p.getPlayer().sendPacket(cs);
            }
        } else if (Config.AUTO_TVT_TK_PUNISH) {
            kp.decreaseScore(true);
            if (Config.AUTO_TVT_TK_PUNISH_CANCEL)
                killer.stopAllEffects();
            if (Config.AUTO_TVT_TK_PUNISH_EFFECTS != null) {
                for (L2Skill s : Config.AUTO_TVT_TK_PUNISH_EFFECTS) {
                    if (s == null)
                        continue;
                    if (killer.getFirstEffect(s) != null)
                        killer.getFirstEffect(s).exit();
                    s.getEffects(killer, killer);
                }
            }
        }
        vp.decreaseScore(false);
        updatePlayerTitle(kp);
        updatePlayerTitle(vp);
    }

    public final void revive(L2Player participant, int team) {
        participant.setIsPendingRevive(true);
        participant.teleToLocation(Config.AUTO_TVT_TEAM_LOCATIONS[team]);
    }

    public final void recover(L2Player revived) {
        if (Config.AUTO_TVT_REVIVE_RECOVER && isPlaying(revived)) {
            revived.getStatus().setCurrentCp(revived.getMaxCp());
            revived.getStatus().setCurrentHpMp(revived.getMaxHp(), revived.getMaxMp());
        }
    }

    private final void updatePlayerTitle(Participant p) {
        L2Player player = p.getPlayer();
        if (player == null)
            return;
        if (p.isGodlike())
            player.getAppearance().setVisibleTitle(Config.AUTO_TVT_GODLIKE_TITLE);
        else
            player.getAppearance().setVisibleTitle("Score: " + p.getScore());
        player.broadcastTitleInfo();
    }

    public final void onDisconnection(L2Player player) {
        if (!isReged(player))
            return;
        switch (status) {
        case STATUS_REGISTRATION:
            participants.remove(player);
            break;
        case STATUS_PREPARATION:
            participants.remove(player);
            Participant part = eventPlayers.remove(player.getObjectId());
            if (part != null)
                removeFromEvent(player, part);
            break;
        case STATUS_COMBAT:
        case STATUS_REWARDS:
            Participant p = eventPlayers.get(player.getObjectId());
            p.setPlayer(null);
            if (!checkTeamStatus())
                eventEnd();
            break;
        }
    }

    private final int[] countTeamMembers() {
        int[] count = new int[eventTeams.length];
        System.arraycopy(teamMembers, 0, count, 0, count.length);
        for (Participant p : eventPlayers.values())
            if (p.getPlayer() != null)
                count[p.getTeam()]++;
        return count;
    }

    private final boolean checkTeamStatus() {
        int[] members = countTeamMembers();
        boolean flag = false;
        for (int i : members) {
            if (i > 0) {
                if (flag)
                    return true;
                else
                    flag = true;
            }
        }
        return false;
    }

    private final void removeDisconnected(int objID, Location loc) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement ps = con
                    .prepareStatement("UPDATE characters SET heading=?,x=?,y=?,z=? WHERE charId=?");
            ps.setInt(1, loc.getHeading());
            if (Config.AUTO_TVT_OVERRIDE_TELE_BACK) {
                ps.setInt(2, Config.AUTO_TVT_DEFAULT_TELE_BACK.getX());
                ps.setInt(3, Config.AUTO_TVT_DEFAULT_TELE_BACK.getY());
                ps.setInt(4, Config.AUTO_TVT_DEFAULT_TELE_BACK.getZ());
            } else {
                ps.setInt(2, loc.getX());
                ps.setInt(3, loc.getY());
                ps.setInt(4, loc.getZ());
            }
            ps.setInt(5, objID);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException e) {
            _log.error("Could not remove a disconnected TvT player!", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private final int[] correctColor(Team[] teams, int rn, int gn, int bn, int current) {
        int[] result = { rn, gn, bn };
        // Possible to do fast enough even if there are 32+ teams,
        // But I don't think this "blind shot" idea is suited for that
        if (Config.AUTO_TVT_TEAM_LOCATIONS.length > 32 || current == 0)
            return result;

        int totalDiff, noticeable = (256 * 2) / Config.AUTO_TVT_TEAM_LOCATIONS.length;
        while (true) {
            for (int i = 0; i < current; i++) {
                totalDiff = (Math.abs(result[0] - teams[i].getColorRed())
                        + Math.abs(result[1] - teams[i].getColorGreen())
                        + Math.abs(result[2] - teams[i].getColorBlue()));
                if (totalDiff < noticeable) {
                    result[0] = Rnd.get(256);
                    result[1] = Rnd.get(256);
                    result[2] = Rnd.get(256);
                } else
                    return result;
            }
        }
    }

    private final void buildCountArray() {
        teamMembers = new int[Config.AUTO_TVT_TEAM_LOCATIONS.length];
        for (int i = 0; i < teamMembers.length; i++)
            teamMembers[i] = 0;
    }

    private final void removeFromEvent(L2Player player, Participant p) {
        player.getAppearance().setVisibleTitle(null);
        p.setNameColor(-1);
        if (!player.isDead()) {
            player.getStatus().setCurrentCp(player.getMaxCp());
            player.getStatus().setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
        } else
            player.setIsPendingRevive(true);
        if (Config.AUTO_TVT_OVERRIDE_TELE_BACK)
            player.teleToLocation(Config.AUTO_TVT_DEFAULT_TELE_BACK);
        else
            player.teleToLocation(p.getLoc(), true);
    }

    private class Participant {
        private final int team;
        private final int objectID;
        private final Location loc;
        private int nameColor = -1;
        private volatile L2Player player;
        private int points;
        private int killsNoDeath;

        private Participant(int team, L2Player player) {
            this.team = team;
            objectID = player.getObjectId();
            loc = player.getLoc();
            this.player = player;
            points = 0;
            killsNoDeath = 0;
        }

        public final L2Player getPlayer() {
            return player;
        }

        public final void setPlayer(L2Player player) {
            this.player = player;
        }

        public final int getObjectID() {
            return objectID;
        }

        public final int getTeam() {
            return team;
        }

        public final Location getLoc() {
            return loc;
        }

        public final int getNameColor() {
            return nameColor;
        }

        public final void setNameColor(int nameColor) {
            this.nameColor = nameColor;
        }

        public final int getScore() {
            return points;
        }

        public final void increaseScore() {
            if (isGodlike())
                points += Config.AUTO_TVT_GODLIKE_POINT_MULTIPLIER;
            else
                points++;
            killsNoDeath++;
        }

        public final void decreaseScore(boolean tk) {
            if (tk)
                points -= Config.AUTO_TVT_TK_PUNISH_POINTS_LOST;
            else
                points--;
            if (!tk)
                killsNoDeath = 0;
            else if (Config.AUTO_TVT_TK_RESET_GODLIKE)
                killsNoDeath = 0;
        }

        public final boolean isGodlike() {
            return (Config.AUTO_TVT_GODLIKE_SYSTEM && killsNoDeath >= Config.AUTO_TVT_GODLIKE_MIN_KILLS);
        }
    }

    private class Team {
        // Array index serves better than ID
        private final int r;
        private final int g;
        private final int b;
        private int points;

        private Team(int[] rgb) {
            r = rgb[0];
            g = rgb[1];
            b = rgb[2];
            points = 0;
        }

        private Team(int color) {
            this(new int[] { (color >> 16) & 0xFF, (color >> 8) & 0xFF, (color >> 0) & 0xFF });
        }

        public final int getPoints() {
            return points;
        }

        public final void addPoint() {
            points++;
        }

        public final int getColorRed() {
            return r;
        }

        public final int getColorGreen() {
            return g;
        }

        public final int getColorBlue() {
            return b;
        }
    }
}