[СТАТЬЯ] Как сделать мини-игру?

HunterGaming

Активный участник
Пользователь
Баллы
96
Имя в Minecraft
sqdFendy
МИНИ-ИГРА

Привет форумчане, думаю все хотят научится писать мини-игры(по крайней мере я хотел, и научился уже на 75%), и писал недавно о том, кто бы создал тему по созданию мини-игры, вот сам я и создаю).

Что же нужно для создания мини-игры? Как минимум знание основ Java, а также Bukkit API.

В этой теме я расскажу и покажу вам основной код мини-игры, логику и эвенты дописать думаю сами сумеете. У нас будет 5 классов:
- Arena(в этом классе мы будем описывать свойства и методы),
- ArenaManager(Менеджер всех арен),
- ArenaState(Статус арены: LOBBY, GAME),
- Utils(Класс с утилитой),
- Main(Главный класс).

Начнем мы с класса Arena:

Java:
package me.hunterplay.spleef.arena;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.*********ventory.ItemStack;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;

import me.hunterplay.spleef.Main;
import me.hunterplay.spleef.config.MessageConfig;
import me.hunterplay.spleef.utils.Utils;

public class Arena extends ArenaManager {

    private Arena arena;

    private String arenaName; //Имя арены
    private List<String> arenaPlayers; //Лист игроков
    private int arenaTimeToStart; //Время до старта игры
    private int arenaTime; //Время игры
    private int arenaMinPlayers; //Минимальное кол-во игроков
    private int arenaMaxPlayers; //Максимальное кол-во игроков
    private ArenaState arenaState; //Статус арены
    private Location arenaSpawnPoint; //Локация, куда будет телепортироваться игрок на игру
    private Location arenaLobby; //Лобби арены
    private Location arenaPos1; //1 позиция арены
    private Location arenaPos2; //2 позиция арены
    private boolean lobbyCountdown; //Булевское значение - таймер лобби
    private boolean arenaCountdown; //Булевское значение - таймер арены
    private int lobbyScheduler; //Шедулер лобби
    private int arenaScheduler; //Шедулер арены

    public Arena(String arenaName) { //Конструктор для создания арены, для того что бы создать арену нужно написать Arena arena = new Arena("Arena1"), но так мы писать не будем, для этого есть отдельный класс ArenaManager, в нем будет метод
        this.arenaName = arenaName;
        arenaPlayers = new ArrayList<String>();
        arenaState = ArenaState.LOBBY;
        arena = this;
    }

    public void addPlayer(String pName) { //Добавление игрока на арену
        Player p = Bukkit.getPlayer(pName); //Создаем объект игрока с именем pName

        if (arenaLobby == null || arenaPos1 == null || arenaPos2 == null) { //Если локации равняются null, то пишем сообщение о том что арена не настроена и выходим из метода
            p.sendMessage("Арена не настроена!");
            return;
        }

        if (arenaIsFull()) { //Если арена заполнена, то пишем сообщение о том что она заполнена и выходим из метода
            p.sendMessage("Арена заполнена!");
            return;
        }

        if (arenaState != ArenaState.LOBBY) { //Если статус арены не лобби, то пишем сообщение о том что арена находится в игре и выходим из метода
            p.sendMessage("Арена находится в игре!");
            return;
        }

        p.getInventory().clear(); //Очищаем весь инвентарь игрока
        p.setHealth(p.getMaxHealth()); //Лечим игрока
        p.setFoodLevel(20); //Кормим игрока
        p.setFireTicks(0); //Тушим игрока от огня
        p.setGameMode(GameMode.SURVIVAL); //Ставим режим выживания
        p.teleport(arenaLobby); //Телепортируем в лобби арены
        arenaPlayers.add(pName); //Добавляем игрока в лист игроков
        sendMessageToArena(p.getName() + " присоединился к игре. Игроков " + arenaPlayers.size() + "/" + arenaMaxPlayers + "."); //Отправляем сообщение всем учащимся арены, о том что присоединился новый игрок

        Scoreboard scoreBoard = Bukkit.getScoreboardManager().getNewScoreboard(); //Создаем скорборд
        Objective objective = scoreBoard.registerNewObjective(arenaName, "dummy"); //Создаем обжектив с именем арены
        objective.setDisplayName("Lobby"); //Указываем тайтл нашего скорборда
        objective.setDisplaySlot(DisplaySlot.SIDEBAR); //Задаем displayslot, чтоб скорборд находился справа экрана
        for (int i = 0; i < 3; i++) { //Цикл для создания пустых ячеек в скорборде, сделано для красоты
            String[] str = { "", " ", "  " };
            Score score = objective.getScore(str[I]);
            score.setScore(i + 1);
        }
        Score messagePlayers = objective.getScore("Игроков " + arenaPlayers.size() + "/" + arenaMaxPlayers); //Показываем кол-во игроков и максимальное кол-во арены в скорборде
        messagePlayers.setScore(0);
        p.setScoreboard(scoreBoard); //Присваиваем скорборд игроку

        if (arenaPlayers.size() >= arenaMinPlayers) { //Если кол-во игроков в листе больше минимального значения, то запускаем таймер
            startCountdown(); //Запуск таймера
        }
    }

    public void startArena() { //Старт арены
        if (arenaCountdown) //Если arenaCountdown = true, то выходим из метода
            return;

        arenaCountdown = true; //Ставим значение true для arenaCountdown

        for (String pName : arenaPlayers) { //Перебираем всех игроков арены
            if (Bukkit.getPlayer(pName) == null) { //Если игрок вышел, то убираем его из листа
                arenaPlayers.remove(pName);
            }

            if (arenaPlayers.size() < arenaMinPlayers) { //Если кол-во игроков меньше чем минимальное, выполняем следующие действия и выходим из метода
                sendMessageToArena("Недостаточно игроков для старта игры!"); //Уведомляем игроков, о том что кол-во игроков меньше чем минимальное кол-во игроков
                Scoreboard scoreBoard = Bukkit.getScoreboardManager().getNewScoreboard(); //Создаем новый скорборд
                Objective objective = scoreBoard.registerNewObjective(arenaName, "dummy"); //Создаем новый обжектив с именем арены
                objective.setDisplayName("Lobby"); //Указываем тайтл нашего скорборда
                objective.setDisplaySlot(DisplaySlot.SIDEBAR); //Задаем displayslot, чтоб скорборд находился справа экрана
                for (int i = 0; i < 3; i++) { //Цикл для создания пустых ячеек в скорборде, сделано для красоты
                    String[] str = { "", " ", "  " };
                    Score score = objective.getScore(str[I]);
                    score.setScore(i + 1);
                }
                Score messagePlayers = objective.getScore("Игроков " + arenaPlayers.size() + "/" + arenaMaxPlayers); //Показываем кол-во игроков и максимальное кол-во арены в скорборде
                messagePlayers.setScore(0);
                Bukkit.getPlayer(pName).setScoreboard(scoreBoard); //Присваиваем скорборд игроку
                return;
            }
        }

        arenaState = ArenaState.GAME; //Задаем статус арены
        arenaCountdown = true; //Задаем arenaCountdown
        sendMessageToArena("Игра началась..."); //Уведомляем всех игроков арены, о том что игра началась

        for (String pName : arenaPlayers) {
            Player p = Bukkit.getPlayer(pName);

            //TODO: если надо выдаем предметы, думаю сделаете сами

            p.teleport(arenaSpawnPoint); //Телепортируем всех игроков арены на локацию арены
        }

        arenaScheduler = Bukkit.getScheduler().scheduleSyncRepeatingTask(Main.getInstance(), new Runnable() { //Создаем шедулер и присваиваем его значение нашей переменной arenaScheduler
            int arenaT = arenaTime; //Время игры

            [USER=79777]@Override[/USER]
            public void run() {
                Scoreboard scoreBoard = Bukkit.getScoreboardManager().getNewScoreboard();
                Objective objective = scoreBoard.registerNewObjective(arenaName, "dummy");
                objective.setDisplayName("Game");
                objective.setDisplaySlot(DisplaySlot.SIDEBAR);
                Score messageToStart = objective.getScore("До конца осталось:");
                messageToStart.setScore(1);
                Score timeToStart = objective.getScore(Utils.getUtils().formatTime(arenaT));
                timeToStart.setScore(0);

                Iterator<String> players = arenaPlayers.iterator(); //Итератор - почти тоже самое, что у оператор for, но только если здесь вы используете for, и начнете чистить список играющих, то у вас вылетит ConcurrentModificationException, поэтому здесь используем Iterator

                while(players.hasNext()) { // Пока у итератора не кончились ячейки
                    Player p = Bukkit.getPlayer(players.next());

                    p.setScoreboard(scoreBoard); //Присваиваем игрокам скорборд
                }

                if (arenaT <= 0) { //Если время меньше или равно 0, то заканчиваем игру
                    endArena(); //Конец игры
                }
                arenaT--;
            }
        }, 20L, 20L);
    }

    public void endArena() { //Конец

        //TODO: если надо, то ресетим площадку арены, либо загружаем схематик
     
        if (arenaPlayers != null) { //Если лист не пустой
            broadcastMessage(arenaPlayers.get(0) + " победил на арене " + arena.getArenaName());
            arenaState = ArenaState.LOBBY;

            Iterator<String> endPlayers = arenaPlayers.iterator();
            while(endPlayers.hasNext()) {
                Player p = Bukkit.getPlayer(endPlayers.next());
                endPlayers.remove();

                if (p != null) {
                    p.teleport(ArenaManager.getArenaManager().getMainLobby()); // Телепортируем игрока главное лобби
                    p.setHealth(p.getMaxHealth());
                    p.setFireTicks(0);
                    p.getInventory().clear();
                }
                stopArenaCountdown(p); //Останавливаем шедулер арены, и ставим стандартный скорборд игроку
            }
        }
    }

    public void startCountdown() { //Старт таймера в лобби
        if (lobbyCountdown) //Если lobbyCountdown = true, выходим из метода
            return;

        lobbyCountdown = true; //Ставим значение true

        lobbyScheduler = Bukkit.getScheduler().scheduleSyncRepeatingTask(Main.getInstance(), new Runnable() { //Создаем шедулер и присваиваем его нашей переменной lobbyScheduler
            int arenaTTS = arenaTimeToStart; //Время до старта игры

            [USER=79777]@Override[/USER]
            public void run() {
                Scoreboard scoreBoard = Bukkit.getScoreboardManager().getNewScoreboard();
                Objective objective = scoreBoard.registerNewObjective(arenaName, "dummy");
                objective.setDisplayName("Lobby");
                objective.setDisplaySlot(DisplaySlot.SIDEBAR);
                Score messageToStart = objective.getScore("До начала осталось:");
                messageToStart.setScore(3);
                Score timeToStart = objective.getScore(Utils.getUtils().formatTime(arenaTTS));
                timeToStart.setScore(2);
                Score none = objective.getScore("");
                none.setScore(1);
                Score players = objective.getScore("Игроков " + arenaPlayers.size() + "/" + arenaMaxPlayers);
                players.setScore(0);

                if (arenaPlayers.size() == 0) { //Если кол-во игроков в листе равно 0, то
                    Bukkit.getScheduler().cancelTask(lobbyScheduler); //Останавливаем шедулер лобби
                    lobbyCountdown = false; //Ставим значение false
                }

                for (String pName : arenaPlayers) {
                    Player p = Bukkit.getPlayer(pName);

                    p.setScoreboard(scoreBoard);

                    if (arenaTTS <= 0) { //Если arenaTTS меньше или равно 0, то
                        stopLobbyCountdown(p); //Останавливаем шедулер лобби и задаем стандартный скорборд
                        startArena(); //Начинаем игру
                    }
                }
                arenaTTS--;
            }
        }, 20L, 20L);
    }

    public void stopLobbyCountdown(Player p) { //Метод для удаления шедура лобби и установки стандартного скорборда
        Bukkit.getScheduler().cancelTask(lobbyScheduler);
        lobbyCountdown = false;
        p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
    }

    public void stopArenaCountdown(Player p) { //Метод для удалиния шедулера арены и установки стандартного скорборда
        Bukkit.getScheduler().cancelTask(arenaScheduler);
        arenaCountdown = false;
        p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
    }

    public void sendMessageToArena(String msg) { //Отправка сообщения всем игрокам арены
        if (arenaPlayers.size() == 0) {
            return;
        }

        for (String pName : arenaPlayers)
            if (Bukkit.getPlayer(pName) != null)
                Bukkit.getPlayer(pName).sendMessage(msg);
    }
}
 
Последнее редактирование:
Продолжение первой статья, геттеры и сеттеры, тоже вставьте в этот класс

Java:
    public boolean arenaIsFull() { //Проверка, заполнена ли арена
        return arenaPlayers.size() >= arenaMaxPlayers;
    }

    public Arena getArena() { //Геттер arena
        return arena;
    }

    public String getArenaName() { //Получение объекта арены
        return arenaName;
    }

    public void setArenaName(String arenaName) { //Установка имени арены
        this.arenaName = arenaName;
    }

    public List<String> getArenaPlayers() { //Получение листа игроков
        return arenaPlayers;
    }

    public int getArenaTimeToStart() { //Получение времени до начала игры
        return arenaTimeToStart;
    }

    public void setArenaTimeToStart(int arenaTimeToStart) { //Установка времени для начала игры
        this.arenaTimeToStart = arenaTimeToStart;
    }

    public int getArenaTime() { //Получение времени игры
        return arenaTime;
    }

    public void setArenaTime(int arenaTime) { //Установка времени игры
        this.arenaTime = arenaTime;
    }

    public int getArenaMinPlayers() { //Получение минимального кол-ва игроков
        return arenaMinPlayers;
    }

    public void setArenaMinPlayers(int arenaMinPlayers) { //Установка минимального кол-ва игроков
        this.arenaMinPlayers = arenaMinPlayers;
    }

    public int getArenaMaxPlayers() { //Получение максимального кол-ва игроков
        return arenaMaxPlayers;
    }

    public void setArenaMaxPlayers(int arenaMaxPlayers) { //Установка максимального кол-ва игроков
        this.arenaMaxPlayers = arenaMaxPlayers;
    }

    public ArenaState getArenaState() { //Получение статуса арены
        return arenaState;
    }

    public void setArenaState(ArenaState arenaState) { //Установка статуса арены
        this.arenaState = arenaState;
    }

    public Location getArenaSpawnPoint() { //Получение локации арены
        return arenaSpawnPoint;
    }

    public void setArenaSpawnPoint(Location arenaSpawnPoint) { //Установка локации арены
        this.arenaSpawnPoint = arenaSpawnPoint;
    }

    public Location getArenaLobby() { //Получение лобби арены
        return arenaLobby;
    }

    public void setArenaLobby(Location arenaLobby) { //Установка лобби арены
        this.arenaLobby = arenaLobby;
    }

    public Location getArenaPos1() { //Получение 1 позиции арены
        return arenaPos1;
    }

    public void setArenaPos1(Location arenaPos1) { //Установка 1 позиции арены
        this.arenaPos1 = arenaPos1;
    }

    public Location getArenaPos2() { //Получение 2 позиции арены
        return arenaPos2;
    }

    public void setArenaPos2(Location arenaPos2) { //Установка 2 позиции арены 
        this.arenaPos2 = arenaPos2;
    }

    public boolean isLobbyCountdown() { //Проверка, включен ли таймер в лобби
        return arenaCountdown;
    }

    public void setLobbyCountdown(boolean lobbyCountdown) { //Включение/отключение таймера в лобби
        this.lobbyCountdown = lobbyCountdown;
    }

    public boolean isArenaCountdown() { //Проверка, включен ли таймер на арене
        return arenaCountdown;
    }

    public void setArenaCountdown(boolean arenaCountdown) { //Включение/отключение таймера на арене
        this.arenaCountdown = arenaCountdown;
    }

    public int getLobbyScheduler() { //Получение значения lobbyScheduler
        return lobbyScheduler;
    }

    public void setLobbyScheduler(int lobbyScheduler) { //Установка значения lobbyScheduler
        this.lobbyScheduler = lobbyScheduler;
    }

    public int getArenaScheduler() { //Получение значения arenaScheduler
        return arenaScheduler;
    }

    public void setArenaScheduler(int arenaScheduler) { //Установка значения arenaScheduler
        this.arenaScheduler = arenaScheduler;
    }

На сегодня все, если вам понравилось начало, отпишите в комментариях и я продолжу статью.
 
Код:
public Arena(String arenaName) { //Конструктор для создания арены, для того что бы создать арену нужно написать Arena arena = new Arena("Arena1"), но так мы писать не будем, для этого есть отдельный класс ArenaManager, в нем будет метод
        this.arenaName = arenaName;
        arenaPlayers = new ArrayList<String>();
        arenaTimeToStart = 0;
        arenaTime = 0;
        arenaMinPlayers = 0;
        arenaMaxPlayers = 0;
        arenaState = ArenaState.LOBBY;
        arenaSpawnPoint = null;
        arenaLobby = null;
        arenaPos1 = null;
        arenaPos2 = null;
        lobbyCountdown = false;
        arenaCountdown = false;
        lobbyScheduler = 0;
        arenaScheduler = 0;
        arena = this;
    }
Зачем так делать? Эти обьекты и так будут null, а примитивные переменные и так будут равны 0. Ну а boolean переменные и так будут false.
 
Код:
public Arena(String arenaName) { //Конструктор для создания арены, для того что бы создать арену нужно написать Arena arena = new Arena("Arena1"), но так мы писать не будем, для этого есть отдельный класс ArenaManager, в нем будет метод
        this.arenaName = arenaName;
        arenaPlayers = new ArrayList<String>();
        arenaTimeToStart = 0;
        arenaTime = 0;
        arenaMinPlayers = 0;
        arenaMaxPlayers = 0;
        arenaState = ArenaState.LOBBY;
        arenaSpawnPoint = null;
        arenaLobby = null;
        arenaPos1 = null;
        arenaPos2 = null;
        lobbyCountdown = false;
        arenaCountdown = false;
        lobbyScheduler = 0;
        arenaScheduler = 0;
        arena = this;
    }
Зачем так делать? Эти обьекты и так будут null, а примитивные переменные и так будут равны 0. Ну а boolean переменные и так будут false.
Спасибо, буду знать и исправлю.
 
private int lobbyScheduler; //Шедулер лобби private int arenaScheduler; //Шедулер арены
runTaskTimer = BukkitTask - BukkitTask[]
private Location arenaPos1; //1 позиция арены private Location arenaPos2; //2 позиция арены
Location[]
private List<String> arenaPlayers; //Лист игроков
Зачем хранить игроков в виде их имен? Если игрок выйдет, он уже проигрывает/выйдет из очереди (waiting), как я понимаю, смысл тогда хранить их имена?
for (int i = 0; i < 3; i++) { //Цикл для создания пустых ячеек в скорборде, сделано для красоты String[] str = { "", " ", " " }; Score score = objective.getScore(str); score.setScore(i + 1); }
Ужас, лучше сделать отдельный класс для работы с бордом, который сам будет искать повторяющиеся линии, записывая при вписывании линии их в лист
Iterator<String> players = arenaPlayers.iterator(); //Итератор - почти тоже самое, что у оператор for, но только если здесь вы используете for, и начнете чистить список играющих, то у вас вылетит ConcurrentModificationException, поэтому здесь используем Iterator while(players.hasNext()) { // Пока у итератора не кончились ячейки Player p = Bukkit.getPlayer(players.next()); p.setScoreboard(scoreBoard); //Присваиваем игрокам скорборд }

public void stopLobbyCountdown(Player p) { //Метод для удаления шедура лобби и установки стандартного скорборда Bukkit.getScheduler().cancelTask(lobbyScheduler); lobbyCountdown = false; p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard()); }
АААААААААААААААААААА, стопаем один шедуллер столько раз, сколько игроков на арене 😗
Продолжение первой статья, геттеры и сеттеры, тоже вставьте в этот класс

Java:
    public boolean arenaIsFull() { //Проверка, заполнена ли арена
        return arenaPlayers.size() >= arenaMaxPlayers;
    }

    public Arena getArena() { //Геттер arena
        return arena;
    }

    public String getArenaName() { //Получение объекта арены
        return arenaName;
    }

    public void setArenaName(String arenaName) { //Установка имени арены
        this.arenaName = arenaName;
    }

    public List<String> getArenaPlayers() { //Получение листа игроков
        return arenaPlayers;
    }

    public int getArenaTimeToStart() { //Получение времени до начала игры
        return arenaTimeToStart;
    }

    public void setArenaTimeToStart(int arenaTimeToStart) { //Установка времени для начала игры
        this.arenaTimeToStart = arenaTimeToStart;
    }

    public int getArenaTime() { //Получение времени игры
        return arenaTime;
    }

    public void setArenaTime(int arenaTime) { //Установка времени игры
        this.arenaTime = arenaTime;
    }

    public int getArenaMinPlayers() { //Получение минимального кол-ва игроков
        return arenaMinPlayers;
    }

    public void setArenaMinPlayers(int arenaMinPlayers) { //Установка минимального кол-ва игроков
        this.arenaMinPlayers = arenaMinPlayers;
    }

    public int getArenaMaxPlayers() { //Получение максимального кол-ва игроков
        return arenaMaxPlayers;
    }

    public void setArenaMaxPlayers(int arenaMaxPlayers) { //Установка максимального кол-ва игроков
        this.arenaMaxPlayers = arenaMaxPlayers;
    }

    public ArenaState getArenaState() { //Получение статуса арены
        return arenaState;
    }

    public void setArenaState(ArenaState arenaState) { //Установка статуса арены
        this.arenaState = arenaState;
    }

    public Location getArenaSpawnPoint() { //Получение локации арены
        return arenaSpawnPoint;
    }

    public void setArenaSpawnPoint(Location arenaSpawnPoint) { //Установка локации арены
        this.arenaSpawnPoint = arenaSpawnPoint;
    }

    public Location getArenaLobby() { //Получение лобби арены
        return arenaLobby;
    }

    public void setArenaLobby(Location arenaLobby) { //Установка лобби арены
        this.arenaLobby = arenaLobby;
    }

    public Location getArenaPos1() { //Получение 1 позиции арены
        return arenaPos1;
    }

    public void setArenaPos1(Location arenaPos1) { //Установка 1 позиции арены
        this.arenaPos1 = arenaPos1;
    }

    public Location getArenaPos2() { //Получение 2 позиции арены
        return arenaPos2;
    }

    public void setArenaPos2(Location arenaPos2) { //Установка 2 позиции арены
        this.arenaPos2 = arenaPos2;
    }

    public boolean isLobbyCountdown() { //Проверка, включен ли таймер в лобби
        return arenaCountdown;
    }

    public void setLobbyCountdown(boolean lobbyCountdown) { //Включение/отключение таймера в лобби
        this.lobbyCountdown = lobbyCountdown;
    }

    public boolean isArenaCountdown() { //Проверка, включен ли таймер на арене
        return arenaCountdown;
    }

    public void setArenaCountdown(boolean arenaCountdown) { //Включение/отключение таймера на арене
        this.arenaCountdown = arenaCountdown;
    }

    public int getLobbyScheduler() { //Получение значения lobbyScheduler
        return lobbyScheduler;
    }

    public void setLobbyScheduler(int lobbyScheduler) { //Установка значения lobbyScheduler
        this.lobbyScheduler = lobbyScheduler;
    }

    public int getArenaScheduler() { //Получение значения arenaScheduler
        return arenaScheduler;
    }

    public void setArenaScheduler(int arenaScheduler) { //Установка значения arenaScheduler
        this.arenaScheduler = arenaScheduler;
    }

На сегодня все, если вам понравилось начало, отпишите в комментариях и я продолжу статью.
lombok
 
runTaskTimer = BukkitTask - BukkitTask[]

Location[]

Зачем хранить игроков в виде их имен? Если игрок выйдет, он уже проигрывает/выйдет из очереди (waiting), как я понимаю, смысл тогда хранить их имена?

Ужас, лучше сделать отдельный класс для работы с бордом, который сам будет искать повторяющиеся линии, записывая при вписывании линии их в лист



АААААААААААААААААААА, стопаем один шедуллер столько раз, сколько игроков на арене 😗

lombok
Спасибо, за твою критику.
 
Добавил бы голосование... хотя нет, не надо, жмякай флажок выше.
 
Ну, а если остальным нравится, я буду продолжать.
Продолжай, не обращай внимание на д*унов выше.
А вам, дорогим д*унам, хочу сказать: Если информация в статье актуальная и развивающая + если она не заброшена, то дополнять статью нужно обязательно, какой бы плохой код не был. К тому же вы все-равно поможете ему исправить костыльность кода.
 
Назад
Верх